Monday, July 13, 2026

Facade Design Pattern

Mastering Design Patterns in C# and ASP.NET Core

Part 3.5 – Facade Design Pattern

Series: Design Patterns in C# and ASP.NET Core
Pattern Category: Structural Design Pattern
Difficulty: ⭐⭐⭐☆☆ (Intermediate)
Prerequisites: OOP Concepts, Classes, Interfaces, Dependency Injection, SOLID Principles


Table of Contents

  1. Introduction

  2. What is the Facade Design Pattern?

  3. Why Do We Need the Facade Pattern?

  4. The Problem with Complex Subsystems

  5. Real-World Analogy

  6. Facade Pattern Structure

  7. UML Class Diagram

  8. Components of the Facade Pattern

  9. Complete C# Console Application

  10. ASP.NET Core Implementation

  11. Real-World Banking Example

  12. E-Commerce Checkout Example

  13. Advantages

  14. Disadvantages

  15. Best Practices

  16. Common Mistakes

  17. Facade vs Adapter vs Mediator

  18. Real-World Uses in .NET

  19. Interview Questions

  20. Summary


Introduction

As enterprise applications grow, they often become collections of many interconnected components and services. A single business operation may require interactions with databases, external APIs, payment gateways, logging services, notification systems, authentication providers, and caching mechanisms.

For example, consider an e-commerce checkout process. Completing a single order may involve:

  • Validating the shopping cart

  • Checking inventory

  • Calculating discounts

  • Processing payment

  • Creating the order

  • Updating stock

  • Sending confirmation emails

  • Logging the transaction

  • Generating an invoice

If the client application has to communicate directly with every subsystem, the code quickly becomes difficult to understand, maintain, and extend.

This is where the Facade Design Pattern becomes invaluable. It provides a single, simplified interface that hides the complexity of the underlying subsystems.


What is the Facade Design Pattern?

Definition

The Facade Design Pattern is a Structural Design Pattern that provides a single unified interface to a set of interfaces in a complex subsystem.

Instead of exposing all subsystem components to the client, the Facade encapsulates their interactions and offers a simple API.

In simple terms:

Facade hides complexity behind a simple interface.


Why Do We Need the Facade Pattern?

Imagine a banking application where a customer wants to transfer money.

Without a Facade, the application might need to interact with:

  • Account Validation Service

  • Balance Service

  • Fraud Detection Service

  • Transaction Service

  • Notification Service

  • Audit Logging Service

The client would have to coordinate all these services manually.

With a Facade, the client simply calls:

bankFacade.TransferMoney(fromAccount, toAccount, amount);

The Facade internally coordinates all subsystem operations, making the client code much simpler and easier to maintain.


The Problem with Complex Subsystems

Consider an online shopping application.

Without the Facade Pattern:

Customer

↓

Inventory Service

↓

Payment Service

↓

Shipping Service

↓

Invoice Service

↓

Notification Service

↓

Logging Service

The client must understand how each subsystem works and in what order to call them.

Problems include:

  • Tight coupling

  • Complex client code

  • Difficult maintenance

  • High risk of errors

  • Repeated orchestration logic

The Facade Pattern centralizes this orchestration.


Real-World Analogy

Imagine visiting a hospital.

Instead of contacting:

  • Reception

  • Billing

  • Doctor

  • Laboratory

  • Pharmacy

individually, you visit the Reception Desk.

The receptionist coordinates everything on your behalf.

The receptionist acts as the Facade.

Similarly, in software, the Facade provides one point of entry into a complex system.


Facade Pattern Structure

The Facade Pattern consists of:

  • Client

  • Facade

  • Multiple Subsystem Classes

The client communicates only with the Facade, while the Facade coordinates the subsystem components.


UML Class Diagram

                    +-------------------+
                    |      Client       |
                    +-------------------+
                              |
                              |
                              V
                    +-------------------+
                    |   BankingFacade   |
                    +-------------------+
                    | + TransferMoney() |
                    +---------+---------+
                              |
        +---------------------+----------------------+
        |                     |                      |
        V                     V                      V
+----------------+   +----------------+   +----------------+
| AccountService |   | PaymentService |   | Notification   |
+----------------+   +----------------+   +----------------+
| Validate()     |   | Transfer()     |   | SendSMS()      |
+----------------+   +----------------+   +----------------+

Components of the Facade Pattern

Client

The client interacts only with the Facade.


Facade

Coordinates the subsystem classes.

public class BankingFacade
{
    private readonly AccountService accountService;
    private readonly PaymentService paymentService;
    private readonly NotificationService notificationService;

    public BankingFacade()
    {
        accountService = new AccountService();
        paymentService = new PaymentService();
        notificationService = new NotificationService();
    }

    public void TransferMoney(string from, string to, decimal amount)
    {
        accountService.Validate(from);

        paymentService.Transfer(from, to, amount);

        notificationService.SendNotification("Transfer Successful");
    }
}

Subsystem Classes

public class AccountService
{
    public void Validate(string account)
    {
        Console.WriteLine("Account Validated");
    }
}

public class PaymentService
{
    public void Transfer(string from, string to, decimal amount)
    {
        Console.WriteLine($"Transferred ₹{amount}");
    }
}

public class NotificationService
{
    public void SendNotification(string message)
    {
        Console.WriteLine(message);
    }
}

Complete C# Console Application

class Program
{
    static void Main()
    {
        BankingFacade banking = new BankingFacade();

        banking.TransferMoney(
            "ACC1001",
            "ACC2002",
            5000);
    }
}

Output

Account Validated
Transferred ₹5000
Transfer Successful

The client interacts with only one class while the Facade manages all subsystem operations.


ASP.NET Core Implementation

Suppose an e-commerce application requires several services during checkout.

Services

public interface IInventoryService
{
    void ReserveStock();
}

public interface IPaymentService
{
    void ProcessPayment();
}

public interface IShippingService
{
    void CreateShipment();
}

public interface INotificationService
{
    void SendConfirmation();
}

Facade

public class CheckoutFacade
{
    private readonly IInventoryService inventory;
    private readonly IPaymentService payment;
    private readonly IShippingService shipping;
    private readonly INotificationService notification;

    public CheckoutFacade(
        IInventoryService inventory,
        IPaymentService payment,
        IShippingService shipping,
        INotificationService notification)
    {
        this.inventory = inventory;
        this.payment = payment;
        this.shipping = shipping;
        this.notification = notification;
    }

    public void Checkout()
    {
        inventory.ReserveStock();

        payment.ProcessPayment();

        shipping.CreateShipment();

        notification.SendConfirmation();
    }
}

Dependency Injection

builder.Services.AddScoped<IInventoryService, InventoryService>();
builder.Services.AddScoped<IPaymentService, PaymentService>();
builder.Services.AddScoped<IShippingService, ShippingService>();
builder.Services.AddScoped<INotificationService, NotificationService>();

builder.Services.AddScoped<CheckoutFacade>();

Controller

[ApiController]
[Route("api/orders")]
public class OrderController : ControllerBase
{
    private readonly CheckoutFacade checkout;

    public OrderController(CheckoutFacade checkout)
    {
        this.checkout = checkout;
    }

    [HttpPost]
    public IActionResult PlaceOrder()
    {
        checkout.Checkout();

        return Ok("Order Placed Successfully");
    }
}

The controller remains clean and focused, while the Facade coordinates all business operations.


Real-World Banking Example

A money transfer typically involves:

  • Validate sender account

  • Validate receiver account

  • Check balance

  • Perform fraud detection

  • Debit sender account

  • Credit receiver account

  • Log transaction

  • Send SMS/Email notification

Without a Facade, the client would call each service individually.

With a BankingFacade, all of these operations are executed through a single method:

bankingFacade.TransferMoney();

E-Commerce Checkout Example

A checkout process may involve:

Customer

↓

Shopping Cart Validation

↓

Inventory Check

↓

Discount Calculation

↓

Payment Processing

↓

Invoice Generation

↓

Shipment Creation

↓

Email Notification

↓

Order Completed

The CheckoutFacade hides all this complexity behind one simple API.


Advantages

  • Simplifies interaction with complex systems.

  • Reduces coupling between clients and subsystems.

  • Improves code readability.

  • Promotes separation of concerns.

  • Centralizes orchestration logic.

  • Makes subsystem changes transparent to clients.

  • Easier maintenance and testing.


Disadvantages

  • The Facade can become a "God Object" if too many responsibilities are added.

  • May hide useful subsystem functionality that advanced clients need.

  • Adds an additional abstraction layer.

  • Poorly designed facades can become difficult to maintain.


Best Practices

  • Keep the Facade focused on a single business workflow.

  • Do not implement business rules unrelated to orchestration.

  • Inject subsystem dependencies using Dependency Injection.

  • Avoid exposing subsystem objects directly to clients.

  • Split large facades into smaller, feature-specific facades when necessary.


Common Mistakes

Creating a Huge Facade

Avoid placing unrelated operations in one large facade class. Create separate facades for different business domains.


Putting Business Logic Inside the Facade

The Facade should coordinate services, not replace them.


Ignoring Dependency Injection

Always inject subsystem services rather than creating them manually inside the facade.


Overusing the Pattern

Use a Facade only when subsystem complexity justifies it. For simple systems, it may add unnecessary abstraction.


Facade vs Adapter vs Mediator

FeatureFacadeAdapterMediator
PurposeSimplifies subsystemConverts interfacesCoordinates object interactions
Changes InterfaceNoYesNo
Simplifies UsageYesSometimesNo
Primary GoalHide complexityCompatibilityReduce direct communication

Real-World Uses in .NET

The Facade Pattern is widely used in enterprise .NET applications, including:

  • ASP.NET Core service layers

  • Repository and Unit of Work abstractions

  • E-commerce checkout workflows

  • Banking transaction processing

  • Healthcare appointment systems

  • Travel booking systems

  • Azure service orchestration

  • Microservices API gateways (conceptually similar in providing a unified entry point)


Interview Questions

1. What is the Facade Design Pattern?

It is a structural design pattern that provides a simplified interface to a complex subsystem.


2. When should you use the Facade Pattern?

When clients need to interact with multiple subsystem classes and you want to simplify those interactions.


3. Does the Facade Pattern hide subsystem classes?

Yes. Clients typically interact only with the Facade, while the Facade manages communication with subsystem classes.


4. Which SOLID principles does the Facade Pattern support?

  • Single Responsibility Principle (SRP): The client focuses on business tasks while the Facade manages orchestration.

  • Dependency Inversion Principle (DIP): The Facade can depend on abstractions (interfaces) instead of concrete implementations.


5. What is the difference between Facade and Adapter?

  • Facade simplifies access to a complex subsystem.

  • Adapter converts one interface into another compatible interface.


6. What are common real-world examples of the Facade Pattern?

  • Banking transactions

  • E-commerce checkout

  • Hospital management systems

  • Home theater systems

  • Cloud service orchestration

  • Travel booking platforms


Summary

The Facade Design Pattern is one of the most practical structural patterns for enterprise software development. By providing a unified, easy-to-use interface over complex subsystems, it reduces coupling, simplifies client code, and improves maintainability.

In C# and ASP.NET Core applications, the Facade Pattern is especially useful for orchestrating workflows such as banking transactions, order processing, document generation, and service integrations. When applied correctly, it creates cleaner architectures, improves readability, and makes systems easier to evolve as business requirements grow.


Coming Up Next: Part 3.6 – Flyweight Design Pattern

In the next article, we'll explore the Flyweight Design Pattern, including:

  • What is the Flyweight Pattern?

  • Why memory optimization matters

  • Intrinsic vs. Extrinsic State

  • Object Sharing and Caching

  • UML Class Diagram

  • Complete C# Console Application

  • ASP.NET Core implementation

  • Real-world examples (Text Editors, Game Development, Icon Libraries)

  • Advantages and disadvantages

  • Best practices

  • Common mistakes

  • Interview questions

You'll learn how the Flyweight Pattern minimizes memory usage by sharing common object state, making it ideal for high-performance applications that create and manage a large number of similar objects.

Decorator Design Pattern

 Mastering Design Patterns in C# and ASP.NET Core

Part 3.4 – Decorator Design Pattern

Series: Design Patterns in C# and ASP.NET Core
Pattern Category: Structural Design Pattern
Difficulty: ⭐⭐⭐⭐☆ (Intermediate)
Prerequisites: OOP Concepts, Interfaces, Composition, Dependency Injection, SOLID Principles


Table of Contents

  1. Introduction

  2. What is the Decorator Design Pattern?

  3. Why Do We Need the Decorator Pattern?

  4. The Problem with Inheritance

  5. Real-World Analogy

  6. Decorator Pattern Structure

  7. UML Class Diagram

  8. Components of the Decorator Pattern

  9. Complete C# Console Application

  10. Fluent Decorators

  11. ASP.NET Core Implementation

  12. Real-World Examples

  13. Advantages

  14. Disadvantages

  15. Best Practices

  16. Common Mistakes

  17. Decorator vs Adapter vs Proxy

  18. Middleware in ASP.NET Core (Decorator in Action)

  19. Interview Questions

  20. Summary


Introduction

One of the biggest challenges in software development is adding new functionality to existing classes without modifying their source code.

Consider an application that sends notifications.

Initially, it only supports:

  • Email

Later, business requirements change:

  • Email + Logging

  • Email + Encryption

  • Email + Validation

  • Email + Retry

  • Email + Audit

  • Email + Compression

Using inheritance, you might create classes like:

EmailNotification

LoggedEmailNotification

EncryptedEmailNotification

EncryptedLoggedEmailNotification

ValidatedEncryptedLoggedEmailNotification

RetryEncryptedLoggedEmailNotification

As features grow, the number of subclasses increases dramatically.

This is known as class explosion.

The Decorator Pattern solves this problem elegantly by allowing behaviors to be added dynamically at runtime.


What is the Decorator Design Pattern?

Definition

The Decorator Pattern is a Structural Design Pattern that allows behavior to be added to an object dynamically without modifying its existing code.

Instead of changing the original object, the object is wrapped inside one or more decorator objects, each adding additional behavior.

Think of it like wrapping a gift:

  • The gift remains the same.

  • Each wrapper adds something extra.


Why Do We Need the Decorator Pattern?

Suppose your application sends notifications.

Basic implementation:

Send Email

New requirements:

Validate

↓

Encrypt

↓

Log

↓

Retry

↓

Send Email

Without decorators, every combination would require a new subclass.

With decorators:

RetryDecorator
      ↓
LoggingDecorator
      ↓
EncryptionDecorator
      ↓
ValidationDecorator
      ↓
EmailNotification

Each decorator performs one responsibility.


The Problem with Inheritance

Imagine a coffee ordering system.

Base Coffee

Need:

  • Milk

  • Sugar

  • Chocolate

  • Cream

  • Caramel

Inheritance approach:

Coffee

MilkCoffee

SugarCoffee

MilkSugarCoffee

MilkChocolateCoffee

MilkChocolateSugarCoffee

MilkChocolateSugarCreamCoffee

The number of classes grows exponentially.

Decorators solve this by allowing ingredients to be added dynamically.


Real-World Analogy

Imagine buying a pizza.

Base Pizza

Optional Toppings

  • Cheese

  • Mushroom

  • Olive

  • Paneer

  • Corn

Each topping wraps the existing pizza.

The pizza itself never changes.

Exactly how decorators work.


Decorator Pattern Structure

The Decorator Pattern consists of:

  • Component

  • Concrete Component

  • Base Decorator

  • Concrete Decorators

  • Client

Every decorator implements the same interface as the object it decorates.


UML Class Diagram

                 +----------------------+
                 |     INotifier        |
                 +----------------------+
                 | + Send()             |
                 +----------^-----------+
                            |
          +-----------------+-----------------+
          |                                   |
+----------------------+        +---------------------------+
| EmailNotifier        |        | NotificationDecorator     |
+----------------------+        +---------------------------+
| Send()               |        | - notifier                |
+----------------------+        | + Send()                  |
                                +------------^--------------+
                                             |
              +------------------------------+-----------------------------+
              |                              |                             |
+--------------------------+    +------------------------+    +-----------------------+
| LoggingDecorator         |    | EncryptionDecorator    |    | RetryDecorator        |
+--------------------------+    +------------------------+    +-----------------------+
| Send()                   |    | Send()                 |    | Send()                |
+--------------------------+    +------------------------+    +-----------------------+

Components of the Decorator Pattern

Component

public interface INotifier
{
    void Send(string message);
}

Concrete Component

public class EmailNotifier : INotifier
{
    public void Send(string message)
    {
        Console.WriteLine($"Sending Email: {message}");
    }
}

Base Decorator

public abstract class NotificationDecorator : INotifier
{
    protected readonly INotifier notifier;

    protected NotificationDecorator(INotifier notifier)
    {
        this.notifier = notifier;
    }

    public virtual void Send(string message)
    {
        notifier.Send(message);
    }
}

Logging Decorator

public class LoggingDecorator : NotificationDecorator
{
    public LoggingDecorator(INotifier notifier)
        : base(notifier)
    {
    }

    public override void Send(string message)
    {
        Console.WriteLine("Logging Notification");

        base.Send(message);
    }
}

Encryption Decorator

public class EncryptionDecorator : NotificationDecorator
{
    public EncryptionDecorator(INotifier notifier)
        : base(notifier)
    {
    }

    public override void Send(string message)
    {
        Console.WriteLine("Encrypting Message");

        base.Send(message);
    }
}

Complete C# Console Application

class Program
{
    static void Main()
    {
        INotifier notifier =
            new LoggingDecorator(
                new EncryptionDecorator(
                    new EmailNotifier()));

        notifier.Send("Welcome");
    }
}

Output

Logging Notification

Encrypting Message

Sending Email: Welcome

Each decorator adds functionality before passing control to the next object.


Fluent Decorators

Decorators can be chained dynamically.

INotifier notifier =
    new RetryDecorator(
        new LoggingDecorator(
            new EncryptionDecorator(
                new EmailNotifier())));

You can easily add or remove decorators without changing the base class.


ASP.NET Core Implementation

One of the best examples of the Decorator Pattern is ASP.NET Core Middleware.

Each middleware:

  • Receives the request

  • Performs additional work

  • Calls the next middleware

Pipeline:

Authentication

↓

Authorization

↓

Logging

↓

Exception Handling

↓

Controller

Each middleware decorates the next one.


Example Service

public interface IReportService
{
    void Generate();
}

Base Service

public class ReportService : IReportService
{
    public void Generate()
    {
        Console.WriteLine("Generating Report");
    }
}

Logging Decorator

public class LoggingReportService : IReportService
{
    private readonly IReportService report;

    public LoggingReportService(IReportService report)
    {
        this.report = report;
    }

    public void Generate()
    {
        Console.WriteLine("Log Started");

        report.Generate();

        Console.WriteLine("Log Finished");
    }
}

Dependency Injection

builder.Services.AddScoped<ReportService>();

builder.Services.AddScoped<IReportService>(provider =>
{
    return new LoggingReportService(
        provider.GetRequiredService<ReportService>());
});

Real-World Examples

The Decorator Pattern is commonly used when behavior needs to be added dynamically.

Coffee Shop

Coffee

Milk

Sugar

Chocolate


ASP.NET Core Middleware

Request

↓

Authentication

↓

Authorization

↓

Logging

↓

Controller

Stream Classes in .NET

Examples:

  • FileStream

  • BufferedStream

  • CryptoStream

  • GZipStream

Each stream decorates another stream.


Logging

Business Service

Logging Decorator

Audit Decorator

Caching Decorator


Payment Processing

Payment

Validation

Fraud Detection

Logging

Execution


Advantages

  • Follows the Open/Closed Principle.

  • Adds functionality dynamically.

  • Eliminates subclass explosion.

  • Promotes composition over inheritance.

  • Behaviors can be combined in different ways.

  • Each decorator has a single responsibility.

  • Highly reusable.


Disadvantages

  • Many small classes may be created.

  • Debugging long decorator chains can be difficult.

  • Order of decorators can affect behavior.

  • Configuration may become complex in very large systems.


Best Practices

  • Keep decorators focused on a single responsibility.

  • Use Dependency Injection for decorator registration.

  • Avoid adding unrelated business logic.

  • Document the order of decorators when it matters.

  • Prefer composition over inheritance.


Common Mistakes

Confusing Decorator with Inheritance

Decorators wrap objects at runtime.

Inheritance extends classes at compile time.


Large Decorators

Each decorator should perform one task only.

Examples:

  • Logging

  • Validation

  • Encryption

  • Retry


Ignoring Order

The following are not equivalent:

Logging

↓

Encryption

↓

Email

vs.

Encryption

↓

Logging

↓

Email

Execution order changes the result.


Using Decorators for Object Creation

Decorators enhance behavior.

Factories create objects.

Builders construct complex objects.


Decorator vs Adapter vs Proxy

FeatureDecoratorAdapterProxy
PurposeAdd behaviorConvert interfacesControl access
Changes Interface❌ No✅ Yes❌ No
Adds Features✅ Yes❌ NoSometimes
Wraps Object✅ Yes✅ Yes✅ Yes
Primary GoalExtend functionalityCompatibilityAccess control

Middleware in ASP.NET Core (Decorator in Action)

The ASP.NET Core request pipeline is a classic implementation of the Decorator Pattern.

HTTP Request

↓

Exception Middleware

↓

Authentication Middleware

↓

Authorization Middleware

↓

Logging Middleware

↓

MVC Controller

↓

HTTP Response

Each middleware:

  • Receives the request

  • Adds behavior

  • Calls the next middleware

  • Receives the response

  • Performs additional work

This layered architecture is one of the reasons ASP.NET Core is highly modular and extensible.


Interview Questions

1. What is the Decorator Design Pattern?

A structural design pattern that dynamically adds responsibilities to an object by wrapping it inside decorator objects.


2. What problem does the Decorator Pattern solve?

It avoids class explosion caused by inheritance when adding multiple optional behaviors.


3. What is the difference between Decorator and Inheritance?

  • Inheritance extends behavior at compile time.

  • Decorator extends behavior dynamically at runtime through composition.


4. Which SOLID principles does the Decorator Pattern support?

  • Open/Closed Principle (OCP) – New behaviors can be added without modifying existing classes.

  • Single Responsibility Principle (SRP) – Each decorator has one specific responsibility.


5. Where is the Decorator Pattern used in .NET?

Common examples include:

  • ASP.NET Core Middleware

  • Stream classes (BufferedStream, CryptoStream, GZipStream)

  • Logging pipelines

  • Caching layers

  • Validation and auditing decorators


6. What is the difference between Decorator and Proxy?

  • Decorator enhances an object's behavior.

  • Proxy controls access to an object, such as lazy loading, security, or remote communication.


Summary

The Decorator Design Pattern is one of the most flexible and widely used structural patterns in modern software development. By wrapping objects with additional functionality at runtime, it allows applications to evolve without modifying existing classes or creating an excessive number of subclasses.

In C# and ASP.NET Core, the Decorator Pattern is used extensively in middleware pipelines, stream classes, logging frameworks, validation layers, caching mechanisms, and auditing solutions. Mastering this pattern helps you build modular, maintainable, and extensible applications while adhering to key SOLID principles and the philosophy of composition over inheritance.


Coming Up Next: Part 3.5 – Facade Design Pattern

In the next article, we'll explore the Facade Design Pattern, including:

  • What is the Facade Pattern?

  • Why complex subsystems need simplification

  • Facade Pattern Structure

  • UML Class Diagram

  • Complete C# Console Application

  • ASP.NET Core implementation

  • Real-world examples (Banking System, Home Theater, E-commerce Checkout)

  • Advantages and disadvantages

  • Best practices

  • Common mistakes

  • Facade vs Adapter vs Mediator

  • Interview questions

You'll learn how the Facade Pattern provides a simple, unified interface to complex subsystems, making enterprise applications easier to use, maintain, and extend.

Don't Copy

Protected by Copyscape Online Plagiarism Checker