Type Structural
Purpose Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

Description

The Facade pattern is a structural design pattern that provides a simplified interface to a complex subsystem. Rather than exposing the client directly to the full detail of that subsystem, it introduces a single entry point that presents only the operations the client actually needs.

The facade itself does not perform the underlying work. Instead, it delegates requests to the appropriate subsystem objects, which continue to carry out the real behaviour. The subsystem classes can still be used independently where necessary; the facade simply offers a more convenient and controlled way of accessing them.

Facade is useful because it simplifies interaction with complex systems, making them easier to understand and use. It also reduces coupling between the client and the subsystem, so changes within the subsystem are less likely to affect client code, provided the facade's interface remains stable. This can improve readability, reduce cognitive load, and make a system easier to maintain over time.


Overview

Facade
Subsystem The classes or components that make up the subsystem, which could be a library or a complex system within the application. Each subsystem class can be complex and hard to use directly.
Facade A front-facing interface which is easy to use. It knows which subsystem classes are responsible for a request and delegates client requests to appropriate subsystem objects. Thus, it hides the complexities of the subsystem and provides a simpler interface to the client.
Client The component that interacts with the Facade instead of accessing the subsystem classes directly. It calls methods on the Facade to perform operations on the subsystem.

Implementation

// Subsystem abstractions

public interface ISuspension
{
    string Balancing();
}

public interface IWheels
{
    string TurnLeft();
    string TurnRight();
}

public interface ISteering
{
    string SteerLeft();
    string SteerRight();
}

// Subsystem classes

public class Suspension : ISuspension
{
    public string Balancing() => "Balancing the car...\n";
}

public class Wheels : IWheels
{
    public string TurnLeft() => "Wheels turn to the Left...\n";
    public string TurnRight() => "Wheels turn to the Right...\n";
}

public class Steering : ISteering
{
    public string SteerLeft() => "Steering wheel turns to the Left...\n";
    public string SteerRight() => "Steering wheel turns to the Right...\n";
}

// Facade

public class SteeringFacade(ISuspension suspension, IWheels wheels, ISteering steering)
{
    public string SteerCar(string direction)
    {
        var result = suspension.Balancing();

        switch (direction.ToLowerInvariant())
        {
            case "left":
                result += steering.SteerLeft();
                result += wheels.TurnLeft();
                break;

            case "right":
                result += steering.SteerRight();
                result += wheels.TurnRight();
                break;
        }

        return result;
    }
}

// Client

public static class FacadeDemo
{
    public static void Execute()
    {
        var facade = new SteeringFacade(
            new Suspension(),
            new Wheels(),
            new Steering());

        Console.WriteLine(facade.SteerCar("left"));
        Console.WriteLine(facade.SteerCar("right"));
    }
}

Notes

Supplying subsystem dependencies to a facade through dependency injection gives the design a great deal of flexibility. It allows multiple facades to be created for different use cases or client needs, even when they rely on the same underlying subsystems.

This also supports the Single Responsibility Principle, since each facade can remain focused on a specific role rather than forcing the subsystem itself to absorb behaviour required by many different clients.

This approach also improves testability. When facades depend on abstractions, their subsystem dependencies can be mocked or stubbed, making it easier to test each facade in isolation.

The essential purpose of the Facade pattern is to simplify the client's interaction with complex subsystems. That benefit can be especially valuable in systems that have drifted into a Big Ball of Mud architecture.

A Big Ball of Mud is a system with little or no clear architectural structure. It is typically characterised by tangled dependencies, sprawling code, and weak separation of concerns, all of which make the codebase harder to understand, maintain, and extend.

In such cases, Facade can introduce a measure of structure and organisation. By placing facades in front of loosely organised or overly entangled subsystems, you create an abstraction layer that presents a cleaner and more stable API to clients while hiding some of the complexity underneath.

Once those facades are in place, the system often becomes easier to test and reason about. A clearer API can support more isolated unit tests and can provide a starting point for gradual refactoring. That said, Facade is not a magic bullet: a Big Ball of Mud cannot be cured by a few wrapper classes alone. Real improvement still requires careful planning and deliberate restructuring over time.

Facade is therefore best seen as one useful tool in a broader refactoring effort.

[TestFixture]
public class SteeringFacadeTests
{
    private SteeringFacade _facade;

    private Mock<ISuspension> _suspensionMock;
    private Mock<IWheels> _wheelsMock;
    private Mock<ISteering> _steeringMock;

    [SetUp]
    public void Setup()
    {
        // Create mocks        
        _suspensionMock = new Mock<ISuspension>();
        _wheelsMock = new Mock<IWheels>();
        _steeringMock = new Mock<ISteering>();
        
        // Create the facade with the mocks
        _facade = new SteeringFacade(
            _suspensionMock.Object, 
            _wheelsMock.Object, 
            _steeringMock.Object);
    }

    [Test]
    public void Test_Car_Steers_Left()
    {
        // Arrange
        _suspensionMock.Setup(
            s => s.Balancing()).Returns("Balancing the car...\n");

        _wheelsMock.Setup(
            w => w.TurnLeft()).Returns("Wheels turn to the Left...\n");

        _steeringMock.Setup(
            st => st.SteerLeft()).Returns("Steering wheel turns to the Left...\n");

        // Act
        var result = _facade.SteerCar("left");

        // Assert        
        Assert.That(result, Is.EqualTo(
            "Balancing the car...\n" + 
            "Steering wheel turns to the Left...\n" +
            "Wheels turn to the Left...\n"));
    }
}

Summary

For more information, please see here.