Project – The façades – Structural Patterns

In this example, we play with the following C# projects:

  • The OpaqueFacadeSubSystem class library showcases an opaque façade.
  • The TransparentFacadeSubSystem class library showcases a transparent façade.
  • The Facade project is a REST API that consumes the façades. It exposes two endpoints to access the OpaqueFacadeSubSystem project, and two others that target the TransparentFacadeSubSystem project.

Let’s start with the class libraries.

To follow the SOLID principles, adding some interfaces representing the elements of the subsystem seemed appropriate. In subsequent chapters, we explore how to organize our abstractions to be more reusable, but for now, both abstractions and implementations are in the same assembly.

Opaque façade

In this assembly, only the façade is public; all the other classes are internal, which means they are hidden from the external world. In most cases, this is not ideal; hiding everything makes the subsystem less flexible and harder to extend.However, you may want to control access to your internal APIs in some scenarios. This may be because they are not mature enough and you don’t want any third party to depend on them, or for any other reasons you deem appropriate for your specific use case.Let’s start by taking a look at the following subsystem code:

// An added interface for flexibility
public interface IOpaqueFacade
{
    string ExecuteOperationA();
    string ExecuteOperationB();
}
// A hidden component
internal class ComponentA
{
    public string OperationA() => “Component A, Operation A”;
    public string OperationB() => “Component A, Operation B”;
}
// A hidden component
internal class ComponentB
{
    public string OperationC() => “Component B, Operation C”;
    public string OperationD() => “Component B, Operation D”;
}
// A hidden component
internal class ComponentC
{
    public string OperationE() => “Component C, Operation E”;
    public string OperationF() => “Component C, Operation F”;
}
// The opaque façade using the other hidden components
public class OpaqueFacade : IOpaqueFacade
{
    private readonly ComponentA _componentA;
    private readonly ComponentB _componentB;
    private readonly ComponentC _componentC;
    // Using constructor injection
    internal OpaqueFacade(ComponentA componentA, ComponentB componentB, ComponentC componentC)
    {
        _componentA = componentA ??
throw new ArgumentNullException(nameof(componentA));
        _componentB = componentB ??
throw new ArgumentNullException(nameof(componentB));
        _componentC = componentC ??
throw new ArgumentNullException(nameof(componentC));
    }
    public string ExecuteOperationA()
    {
        return new StringBuilder()
            .AppendLine(_componentA.OperationA())
            .AppendLine(_componentA.OperationB())
            .AppendLine(_componentB.OperationD())
            .AppendLine(_componentC.OperationE())
            .ToString();
    }
    public string ExecuteOperationB()
    {
        return new StringBuilder()
            .AppendLine(_componentB.OperationC())
            .AppendLine(_componentB.OperationD())
            .AppendLine(_componentC.OperationF())
            .ToString();
    }
}

The OpaqueFacade class is coupled with ComponentA, ComponentB, and ComponentC directly. There was no point in extracting any internal interfaces since the subsystem is not extensible anyway. We could have done this to offer some kind of internal flexibility, but in this case, there was no advantage.Besides this coupling, ComponentA, ComponentB, and ComponentC define two methods each, which return a string describing their source. With that code in place, we can observe what is happening and how the final result was composed.OpaqueFacade also exposes two methods, each composing a different message using the underlying subsystem’s components. This is a classic use of a façade; the façade queries other objects more or less complicatedly and then does something with the results, taking away the caller’s burden of knowing the subsystem.Since the members use the internal visibility modifier, we can’t directly register the dependencies with the IoC container from the program. To solve this problem, the subsystem can register its dependencies by adding an extension method. The following extension method is accessible by the consuming application:

public static class StartupExtensions
{
    public static IServiceCollection AddOpaqueFacadeSubSystem(this IServiceCollection services)
    {
        services.AddSingleton<IOpaqueFacade>(serviceProvider
            => new OpaqueFacade(new ComponentA(), new ComponentB(), new ComponentC()));
        return services;
    }
}

The preceding code manually creates the dependencies and adds a binding to the IOpaqueFacade interface so the system can use it. This hides everything but the interface from the consumer.Before exploring the REST API, we look at the transparent façade implementation.

Leave a Reply

Your email address will not be published. Required fields are marked *



          Copyright © 2015-2024 | About | Terms of Service | Privacy Policy