← Back to Article List         
Facade Design Pattern

Facade Design Pattern

Published on 28 Sep 2026     14 min read Design Principles & Patterns
Facade Pattern

Facade Design Pattern in C#

The Facade Pattern is one of the Structural Design Patterns from the Gang of Four (GoF).

The main idea is simple:

Facade provides one simple interface in front of multiple complex classes or subsystems.

Think of the facade as a single entry point to several operations that normally have to be called separately.


1. What is the Facade Pattern?

Suppose an application needs to perform an Order Placement operation.

To place an order, internally you may need to:

  1. Check inventory.
  2. Process payment.
  3. Create shipment.
  4. Send confirmation email.

Without Facade, the calling code has to know about all four services:

Client
  |
  +-- InventoryService
  |
  +-- PaymentService
  |
  +-- ShippingService
  |
  +-- EmailService

The client needs to understand:

  • which services to call
  • what order to call them
  • what parameters they require

Facade introduces another class:

Client
   |
   v
OrderFacade
   |
   +----> InventoryService
   |
   +----> PaymentService
   |
   +----> ShippingService
   |
   +----> EmailService

Now the client simply says:

orderFacade.PlaceOrder();

The Facade handles the complicated workflow internally.


2. Why Do We Need Facade?

Imagine this is the existing code:

var inventory = new InventoryService();
var payment = new PaymentService();
var shipping = new ShippingService();
var email = new EmailService();

inventory.CheckStock("Laptop");

payment.ProcessPayment(75000);

shipping.CreateShipment("Chennai");

email.SendConfirmation("customer@gmail.com");

This works.

But the client is responsible for coordinating everything.

If you have this code inside a controller:

public IActionResult PlaceOrder()
{
    var inventory = new InventoryService();
    var payment = new PaymentService();
    var shipping = new ShippingService();
    var email = new EmailService();

    inventory.CheckStock("Laptop");
    payment.ProcessPayment(75000);
    shipping.CreateShipment("Chennai");
    email.SendConfirmation("customer@gmail.com");

    return Ok();
}

the controller knows too much about the internal order-processing workflow.

Tomorrow, the business process might become:

Check Inventory
      ↓
Reserve Inventory
      ↓
Validate Customer
      ↓
Process Payment
      ↓
Create Shipment
      ↓
Update Order
      ↓
Send Email

Then every client that implements the workflow may need to change.

Facade solves this

Instead:

orderFacade.PlaceOrder();

The controller only knows:

"I want to place an order."

It doesn't need to know how the order is processed internally.


3. Simple Example

Let's build a very simple Order Processing System.

We have four existing services:

InventoryService
PaymentService
ShippingService
EmailService

We'll create:

OrderFacade

to provide one simplified operation:

PlaceOrder()

4. Full Simple Program

using System;

namespace FacadePattern
{
    // -------------------------------
    // Inventory Service
    // -------------------------------
    public class InventoryService
    {
        public bool CheckStock(string product)
        {
            Console.WriteLine($"Checking stock for {product}...");

            return true;
        }
    }


    // -------------------------------
    // Payment Service
    // -------------------------------
    public class PaymentService
    {
        public bool ProcessPayment(decimal amount)
        {
            Console.WriteLine($"Processing payment of ₹{amount}...");

            return true;
        }
    }


    // -------------------------------
    // Shipping Service
    // -------------------------------
    public class ShippingService
    {
        public void CreateShipment(string address)
        {
            Console.WriteLine($"Creating shipment to {address}...");
        }
    }


    // -------------------------------
    // Email Service
    // -------------------------------
    public class EmailService
    {
        public void SendConfirmation(string email)
        {
            Console.WriteLine($"Sending confirmation email to {email}...");
        }
    }


    // ==================================
    // FACADE
    // ==================================
    public class OrderFacade
    {
        private readonly InventoryService _inventoryService;
        private readonly PaymentService _paymentService;
        private readonly ShippingService _shippingService;
        private readonly EmailService _emailService;

        public OrderFacade()
        {
            _inventoryService = new InventoryService();
            _paymentService = new PaymentService();
            _shippingService = new ShippingService();
            _emailService = new EmailService();
        }

        public void PlaceOrder(
            string product,
            decimal amount,
            string address,
            string email)
        {
            Console.WriteLine("Order processing started...");
            Console.WriteLine();

            bool stockAvailable =
                _inventoryService.CheckStock(product);

            if (!stockAvailable)
            {
                Console.WriteLine("Product is out of stock.");
                return;
            }

            bool paymentSuccess =
                _paymentService.ProcessPayment(amount);

            if (!paymentSuccess)
            {
                Console.WriteLine("Payment failed.");
                return;
            }

            _shippingService.CreateShipment(address);

            _emailService.SendConfirmation(email);

            Console.WriteLine();
            Console.WriteLine("Order placed successfully.");
        }
    }


    // ==================================
    // CLIENT
    // ==================================
    public class Program
    {
        public static void Main()
        {
            OrderFacade orderFacade = new OrderFacade();

            orderFacade.PlaceOrder(
                "Laptop",
                75000,
                "Chennai",
                "customer@gmail.com"
            );
        }
    }
}

5. Output

Order processing started...

Checking stock for Laptop...
Processing payment of ₹75000...
Creating shipment to Chennai...
Sending confirmation email to customer@gmail.com...

Order placed successfully.

6. Understanding the Program

There are three important parts.

Part 1 — Subsystem Classes

These are the actual classes that perform the work.

InventoryService

public class InventoryService
{
    public bool CheckStock(string product)
    {
        Console.WriteLine($"Checking stock for {product}...");

        return true;
    }
}

Its responsibility is only inventory.

InventoryService
       ↓
Check product availability

PaymentService

public class PaymentService
{
    public bool ProcessPayment(decimal amount)
    {
        Console.WriteLine($"Processing payment of ₹{amount}...");

        return true;
    }
}

Its responsibility is payment processing.

PaymentService
      ↓
Process Payment

ShippingService

public class ShippingService
{
    public void CreateShipment(string address)
    {
        Console.WriteLine($"Creating shipment to {address}...");
    }
}

Responsible for shipment creation.


EmailService

public class EmailService
{
    public void SendConfirmation(string email)
    {
        Console.WriteLine($"Sending confirmation email to {email}...");
    }
}

Responsible for sending confirmation.

These classes are called the subsystem classes.


7. The Important Part — OrderFacade

public class OrderFacade
{
}

This class provides a simplified interface to the entire order-processing subsystem.

Internally it uses:

private readonly InventoryService _inventoryService;
private readonly PaymentService _paymentService;
private readonly ShippingService _shippingService;
private readonly EmailService _emailService;

The client doesn't need to interact with these services individually.


8. Facade Constructor

For this basic example:

public OrderFacade()
{
    _inventoryService = new InventoryService();
    _paymentService = new PaymentService();
    _shippingService = new ShippingService();
    _emailService = new EmailService();
}

The Facade creates and manages the subsystem objects.

For a real ASP.NET Core application, I would normally use Dependency Injection instead:

public OrderFacade(
    InventoryService inventoryService,
    PaymentService paymentService,
    ShippingService shippingService,
    EmailService emailService)
{
    _inventoryService = inventoryService;
    _paymentService = paymentService;
    _shippingService = shippingService;
    _emailService = emailService;
}

ASP.NET Core DI would supply those dependencies.


9. PlaceOrder() Is the Simplified Interface

This is the heart of the pattern:

public void PlaceOrder(
    string product,
    decimal amount,
    string address,
    string email)

The client sees only one meaningful business operation:

PlaceOrder()

But internally:

PlaceOrder()
     |
     +--> CheckStock()
     |
     +--> ProcessPayment()
     |
     +--> CreateShipment()
     |
     +--> SendConfirmation()

That orchestration is hidden from the client.


10. Client Code

Without Facade:

var inventory = new InventoryService();
var payment = new PaymentService();
var shipping = new ShippingService();
var email = new EmailService();

inventory.CheckStock("Laptop");
payment.ProcessPayment(75000);
shipping.CreateShipment("Chennai");
email.SendConfirmation("customer@gmail.com");

The client knows the whole workflow.

With Facade:

OrderFacade orderFacade = new OrderFacade();

orderFacade.PlaceOrder(
    "Laptop",
    75000,
    "Chennai",
    "customer@gmail.com"
);

Much simpler.


11. Flow

                 CLIENT
                   |
                   |
                   v
             OrderFacade
                   |
         PlaceOrder(...)
                   |
       +-----------+-----------+
       |           |           |
       v           v           v
   Inventory    Payment     Shipping
    Service      Service      Service
       |                         |
       +------------+------------+
                    |
                    v
                EmailService

Another way to visualize the execution:

Program
   |
   v
OrderFacade.PlaceOrder()
   |
   +--> InventoryService.CheckStock()
   |
   +--> PaymentService.ProcessPayment()
   |
   +--> ShippingService.CreateShipment()
   |
   +--> EmailService.SendConfirmation()
   |
   v
Order Completed

12. What Exactly Is the Facade Hiding?

An important interview point:

Facade does not remove the subsystem classes.

They still exist:

InventoryService
PaymentService
ShippingService
EmailService

Facade simply provides an easier way to use them together.

So:

Complex subsystem
        ↓
     Facade
        ↓
Simple interface
        ↓
     Client

13. Real-World Analogy

Think about ordering food at a restaurant.

You tell the waiter:

"I want a chicken biryani."

You don't personally talk to:

Chef
Kitchen assistant
Cashier
Store room
Delivery staff

The waiter acts as a simple interface between you and the complicated restaurant operations.

Customer
    |
    v
  Waiter
    |
    +--> Kitchen
    +--> Billing
    +--> Inventory
    +--> Serving

The waiter is conceptually similar to a Facade.


14. Advantages

1. Simplifies Complex Systems

Instead of:

service1.Method();
service2.Method();
service3.Method();
service4.Method();

the client uses:

facade.DoSomething();

2. Reduces Coupling

Without Facade:

Controller
   |
   +--> Inventory
   +--> Payment
   +--> Shipping
   +--> Email

With Facade:

Controller
     |
     v
OrderFacade
     |
     +--> Inventory
     +--> Payment
     +--> Shipping
     +--> Email

The controller has fewer direct dependencies.


3. Hides Implementation Complexity

Client knows:

PlaceOrder()

Client doesn't need to know:

Check inventory
Reserve stock
Validate payment
Process payment
Generate shipment
Send notification

4. Easier Maintenance

Suppose tomorrow you add:

TaxService

You can change:

OrderFacade

rather than changing every client that places orders.


5. Cleaner Controllers

This is especially useful in ASP.NET Core when a controller would otherwise become orchestration-heavy.

Instead of:

public async Task<IActionResult> PlaceOrder()
{
    await _inventory.Check();
    await _payment.Process();
    await _shipping.Create();
    await _email.Send();

    return Ok();
}

you can expose a higher-level operation:

public async Task<IActionResult> PlaceOrder()
{
    await _orderFacade.PlaceOrder();

    return Ok();
}

15. Disadvantages

Facade is useful, but don't create one unnecessarily.

Facade Can Become Too Large

A badly designed facade can eventually become:

OrderFacade
   |
   +-- PlaceOrder
   +-- CancelOrder
   +-- RefundOrder
   +-- TrackOrder
   +-- UpdateCustomer
   +-- GenerateInvoice
   +-- GenerateReport
   +-- UpdateInventory
   +-- SendNotifications
   ...

Then the Facade itself becomes a God Class.

Keep facade methods focused on meaningful high-level operations.


16. Where We Use Facade in Real Applications

Facade is useful when several components need to work together for one business operation.

For example:

E-commerce

OrderFacade
   |
   +--> InventoryService
   +--> PaymentService
   +--> ShippingService
   +--> NotificationService

Banking

FundTransferFacade
   |
   +--> AccountService
   +--> ValidationService
   +--> TransactionService
   +--> AuditService
   +--> NotificationService

File Processing

DocumentFacade
   |
   +--> ValidationService
   +--> ConversionService
   +--> StorageService
   +--> LoggingService

API Integration

CustomerFacade
   |
   +--> CustomerService
   +--> AddressService
   +--> CreditService
   +--> NotificationService

17. Facade vs Service

This distinction is useful in interviews.

A normal service usually performs a particular responsibility:

PaymentService
    ↓
Payment operations

A facade usually provides a higher-level interface over multiple components:

OrderFacade
    |
    +--> PaymentService
    +--> InventoryService
    +--> ShippingService

However, in modern application architecture, the class performing this role may not literally be named Facade.

You might see names such as:

OrderService
OrderProcessor
CheckoutService
PaymentCoordinator
OrderApplicationService

The design responsibility matters more than putting Facade in the class name.


18. Facade Pattern Structure

The generic pattern looks like:

              Client
                 |
                 v
              Facade
                 |
        +--------+--------+
        |        |        |
        v        v        v
    Service A Service B Service C

Or in C#:

public class Facade
{
    private readonly ServiceA _serviceA;
    private readonly ServiceB _serviceB;
    private readonly ServiceC _serviceC;

    public void Execute()
    {
        _serviceA.DoSomething();
        _serviceB.DoSomething();
        _serviceC.DoSomething();
    }
}

Client:

var facade = new Facade();

facade.Execute();

That is essentially the Facade Pattern.


Key Points for Interview

  • Facade is a Structural Design Pattern.
  • It is one of the GoF patterns.
  • It provides a simple, unified interface to a complex subsystem.
  • It hides the complexity of coordinating multiple classes.
  • The client communicates primarily with the Facade instead of several subsystem classes.
  • It reduces direct coupling between the client and subsystem.
  • Existing subsystem classes do not need to be changed.
  • Facade can coordinate several services as one high-level operation.
  • Facade does not mean the underlying classes become inaccessible.
  • In ASP.NET Core, dependencies should normally be provided through Dependency Injection rather than created with new.
  • A facade should remain focused; otherwise it can grow into a God Class.
  • In modern projects, a facade-like class may be named OrderService, Processor, Coordinator, or ApplicationService rather than Facade.

One-line interview answer

Facade Pattern provides a simplified, unified interface over multiple complex subsystem classes so that the client can perform a high-level operation without knowing the subsystem's internal workflow.

 

Facade Pattern is still used in modern ASP.NET Core applications even though ASP.NET Core has built-in Dependency Injection. They solve different problems.

DI vs Facade

DI Facade
Manages dependencies Simplifies interactions
Creates/provides objects Coordinates multiple services
Reduces object-creation coupling Reduces workflow/subsystem complexity
Answers "How do I get this service?" Answers "How do I perform this business operation?"

Without Facade

DI can inject all required services directly into a controller:

public class OrderController : ControllerBase
{
    private readonly InventoryService _inventory;
    private readonly PaymentService _payment;
    private readonly ShippingService _shipping;
    private readonly EmailService _email;

    public OrderController(
        InventoryService inventory,
        PaymentService payment,
        ShippingService shipping,
        EmailService email)
    {
        _inventory = inventory;
        _payment = payment;
        _shipping = shipping;
        _email = email;
    }

    [HttpPost]
    public async Task<IActionResult> PlaceOrder()
    {
        await _inventory.CheckStock();
        await _payment.ProcessPayment();
        await _shipping.CreateShipment();
        await _email.SendConfirmation();

        return Ok();
    }
}

DI solved the object creation problem.

But the controller still knows the complete workflow:

Controller
   ├── InventoryService
   ├── PaymentService
   ├── ShippingService
   └── EmailService

It knows what must happen and in what order.


With Facade + DI

We can move that orchestration into a Facade:

public class OrderFacade
{
    private readonly InventoryService _inventory;
    private readonly PaymentService _payment;
    private readonly ShippingService _shipping;
    private readonly EmailService _email;

    public OrderFacade(
        InventoryService inventory,
        PaymentService payment,
        ShippingService shipping,
        EmailService email)
    {
        _inventory = inventory;
        _payment = payment;
        _shipping = shipping;
        _email = email;
    }

    public async Task PlaceOrder()
    {
        await _inventory.CheckStock();
        await _payment.ProcessPayment();
        await _shipping.CreateShipment();
        await _email.SendConfirmation();
    }
}

Register everything with DI:

builder.Services.AddScoped<InventoryService>();
builder.Services.AddScoped<PaymentService>();
builder.Services.AddScoped<ShippingService>();
builder.Services.AddScoped<EmailService>();

builder.Services.AddScoped<OrderFacade>();

Now the controller becomes:

public class OrderController : ControllerBase
{
    private readonly OrderFacade _orderFacade;

    public OrderController(OrderFacade orderFacade)
    {
        _orderFacade = orderFacade;
    }

    [HttpPost]
    public async Task<IActionResult> PlaceOrder()
    {
        await _orderFacade.PlaceOrder();

        return Ok();
    }
}

Architecture:

               ASP.NET Core DI
                     │
                     │ creates/injects
                     ▼
Controller ────> OrderFacade
                     │
             ┌───────┼─────────┐
             ▼       ▼         ▼
         Inventory Payment  Shipping
                              │
                              ▼
                            Email

Important industry point

Modern applications do use the Facade concept, but the class frequently isn't called SomethingFacade.

You are more likely to encounter names such as:

OrderService
CheckoutService
OrderProcessor
PaymentProcessor
OrderApplicationService
OrderCoordinator

For example, in a Clean Architecture application:

Controller
     ↓
Application Layer
     ↓
PlaceOrderHandler / OrderService
     ↓
 ┌────────────┬────────────┬────────────┐
Inventory   Payment     Repository   Notification

Architecturally, such an application service or coordinator may be performing a facade-like role, even though the team doesn't explicitly label it the GoF Facade Pattern.

The key distinction

DI
"Give OrderFacade the objects it needs."

Facade
"Hide the complicated order workflow from the Controller."

So DI does not replace Facade. In ASP.NET Core, they can work together.

For your Clean Architecture/Web API work, I wouldn't add a class named Facade merely to say the pattern is being used. Use it when you genuinely need to expose a simpler interface over several services or a complex subsystem.

 

The important point is: DI passes the service objects, not the model/data values.

In my previous example, I simplified PlaceOrder() too much. In a real application, the request/model values are passed from Controller → Facade → individual services.

Example

Suppose the API receives this model:

public class OrderRequest
{
    public int ProductId { get; set; }
    public int Quantity { get; set; }
    public decimal Amount { get; set; }
    public string Address { get; set; }
    public string Email { get; set; }
}

Controller

The values come from the HTTP request:

[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
    private readonly OrderFacade _orderFacade;

    public OrderController(OrderFacade orderFacade)
    {
        _orderFacade = orderFacade;
    }

    [HttpPost]
    public async Task<IActionResult> PlaceOrder(OrderRequest request)
    {
        await _orderFacade.PlaceOrder(request);

        return Ok("Order placed successfully");
    }
}

For example, client sends:

{
  "productId": 101,
  "quantity": 2,
  "amount": 75000,
  "address": "Chennai",
  "email": "customer@gmail.com"
}

ASP.NET Core Model Binding creates:

request.ProductId = 101
request.Quantity  = 2
request.Amount    = 75000
request.Address   = "Chennai"
request.Email     = "customer@gmail.com"

Facade Receives the Model

public class OrderFacade
{
    private readonly InventoryService _inventory;
    private readonly PaymentService _payment;
    private readonly ShippingService _shipping;
    private readonly EmailService _email;

    public OrderFacade(
        InventoryService inventory,
        PaymentService payment,
        ShippingService shipping,
        EmailService email)
    {
        _inventory = inventory;
        _payment = payment;
        _shipping = shipping;
        _email = email;
    }

    public async Task PlaceOrder(OrderRequest request)
    {
        await _inventory.CheckStock(
            request.ProductId,
            request.Quantity);

        await _payment.ProcessPayment(
            request.Amount);

        await _shipping.CreateShipment(
            request.Address);

        await _email.SendConfirmation(
            request.Email);
    }
}

Here you can clearly see where the values come from.

OrderRequest
    │
    ▼
OrderFacade
    │
    ├── ProductId, Quantity ──> InventoryService
    │
    ├── Amount ───────────────> PaymentService
    │
    ├── Address ──────────────> ShippingService
    │
    └── Email ────────────────> EmailService

What is _inventory then?

This:

private readonly InventoryService _inventory;

is not the inventory model/data.

It is an object/reference to the InventoryService.

ASP.NET Core DI creates that service and passes it into:

public OrderFacade(
    InventoryService inventory,
    PaymentService payment,
    ShippingService shipping,
    EmailService email)

Then:

_inventory = inventory;

means:

Store the injected InventoryService object so that OrderFacade can use it later.

The actual business values are supplied when calling its method:

await _inventory.CheckStock(
    request.ProductId,
    request.Quantity);

For example:

public class InventoryService
{
    public async Task<bool> CheckStock(
        int productId,
        int quantity)
    {
        Console.WriteLine($"Product: {productId}");
        Console.WriteLine($"Quantity: {quantity}");

        return true;
    }
}

Complete Flow

POST /api/order
       │
       │ JSON
       ▼
ASP.NET Core Model Binding
       │
       ▼
OrderRequest
ProductId = 101
Quantity  = 2
Amount    = 75000
Address   = Chennai
Email     = customer@gmail.com
       │
       ▼
OrderController
       │
       │ request
       ▼
OrderFacade.PlaceOrder(request)
       │
       ├── CheckStock(101, 2)
       │
       ├── ProcessPayment(75000)
       │
       ├── CreateShipment("Chennai")
       │
       └── SendConfirmation("customer@gmail.com")

So remember these two separate flows

Objects/services come through DI:

DI Container
    ↓
InventoryService
PaymentService
ShippingService
EmailService
    ↓
OrderFacade Constructor

Business data comes through method parameters/models:

HTTP JSON
    ↓
OrderRequest
    ↓
Controller
    ↓
OrderFacade.PlaceOrder(request)
    ↓
Individual Services

That's the key distinction: DI injects dependencies; it doesn't normally inject the current request's model values.