How to Implement Mediator Pattern in .NET

By FoxLearn 2/21/2025 7:18:34 AM   26
In complex applications, objects often need to communicate with one another. While this may be simple in smaller applications with only a few components, the complexity increases as more components are added.

This article demonstrates how to reduce this complexity using the Mediator pattern and how to implement it in a .NET Web API.

The Real-Time Problem

Let’s take a real-time example of a product ordering system. Suppose we have the following components:

  • Order Component
  • Payment Component
  • Inventory Component
  • Notification Component

These components need to interact. For example, when an order is placed, the Order Component might need to communicate with the Payment Component to process the payment, the Inventory Component to check stock, and the Notification Component to send confirmation emails. If each component directly communicates with others, the system quickly becomes tightly coupled and difficult to manage, especially when the number of components increases.

Reducing Coupling with the Mediator Pattern

The Mediator Pattern is a behavioral design pattern that centralizes communication between components through a mediator object, reducing the direct dependencies between components. Instead of components communicating directly with each other, they communicate via the mediator, which delegates the message to the appropriate recipient.

Let’s consider our product ordering system again and redesign it using the Mediator pattern.

Real-World Example: Order Processing System

Imagine an OrderProcessing system in which we have various components:

  • Order Component
  • Payment Component
  • Inventory Component
  • Notification Component

Each component should communicate through a mediator rather than directly with one another.

Components Involved in the Mediator Pattern

  1. Component: These are the individual classes with business logic, each referring to the mediator.
  2. Mediator: The mediator interface defines communication methods for all components.
  3. Concrete Mediator: This manages interactions between the components.

Example Code Implementation:

Step 1: Define the Mediator Interface

public interface IOrderMediator
{
    void ProcessOrder(string orderDetails);
    void RegisterComponent(IOrderComponent component);
}

Step 2: Define the Order Component Interface

public interface IOrderComponent
{
    void Execute(string orderDetails);
}

Step 3: Concrete Order Components

public class PaymentComponent : IOrderComponent
{
    private readonly IOrderMediator _mediator;

    public PaymentComponent(IOrderMediator mediator)
    {
        _mediator = mediator;
    }

    public void Execute(string orderDetails)
    {
        Console.WriteLine($"Processing payment for order: {orderDetails}");
    }
}

public class InventoryComponent : IOrderComponent
{
    private readonly IOrderMediator _mediator;

    public InventoryComponent(IOrderMediator mediator)
    {
        _mediator = mediator;
    }

    public void Execute(string orderDetails)
    {
        Console.WriteLine($"Checking inventory for order: {orderDetails}");
    }
}

public class NotificationComponent : IOrderComponent
{
    private readonly IOrderMediator _mediator;

    public NotificationComponent(IOrderMediator mediator)
    {
        _mediator = mediator;
    }

    public void Execute(string orderDetails)
    {
        Console.WriteLine($"Sending order confirmation for order: {orderDetails}");
    }
}

Step 4: Implement the Concrete Mediator

public class OrderMediator : IOrderMediator
{
    private readonly List<IOrderComponent> _components;

    public OrderMediator()
    {
        _components = new List<IOrderComponent>();
    }

    public void RegisterComponent(IOrderComponent component)
    {
        _components.Add(component);
    }

    public void ProcessOrder(string orderDetails)
    {
        foreach (var component in _components)
        {
            component.Execute(orderDetails);
        }
    }
}

Step 5: Main Program for Mediator Interaction

class Program
{
    static void Main(string[] args)
    {
        var mediator = new OrderMediator();

        var payment = new PaymentComponent(mediator);
        var inventory = new InventoryComponent(mediator);
        var notification = new NotificationComponent(mediator);

        mediator.RegisterComponent(payment);
        mediator.RegisterComponent(inventory);
        mediator.RegisterComponent(notification);

        // Process an order
        mediator.ProcessOrder("Order #12345");

        Console.ReadKey();
    }
}

How the Mediator Pattern Solves the Problem

In this setup, each component (Payment, Inventory, and Notification) communicates through the OrderMediator. Instead of calling each other directly, the components rely on the mediator to handle communication and actions, making the system easier to maintain and extend.

Implementing Mediator Pattern in .NET Web API

The MediatR library provides a convenient way to implement the Mediator pattern in .NET applications, allowing communication between objects without tight coupling.

Step 1: Install MediatR Package

To get started, install the MediatR package using NuGet:

Install-Package MediatR
Install-Package MediatR.Extensions.Microsoft.DependencyInjection

Step 2: Define the Request and Response Models

For example, to process an order:

public class ProcessOrderCommand : IRequest<string>
{
    public string OrderDetails { get; set; }
}

Step 3: Create the Command Handler

public class ProcessOrderHandler : IRequestHandler<ProcessOrderCommand, string>
{
    public Task<string> Handle(ProcessOrderCommand request, CancellationToken cancellationToken)
    {
        // Logic to process the order
        string response = $"Order processed: {request.OrderDetails}";
        return Task.FromResult(response);
    }
}

Step 4: Configure MediatR in the Startup

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMediatR(typeof(Startup).Assembly);
    }
}

Step 5: Create the API Controller

public class OrderController : ControllerBase
{
    private readonly IMediator _mediator;

    public OrderController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpPost("process-order")]
    public async Task<IActionResult> ProcessOrder([FromBody] ProcessOrderCommand command)
    {
        string result = await _mediator.Send(command);
        return Ok(result);
    }
}

Flow of the Process

  • A POST request is made to the process-order endpoint with the order details.
  • The ProcessOrderCommand is sent via MediatR.
  • MediatR locates the appropriate handler (ProcessOrderHandler) to process the command.
  • The handler returns a response (e.g., "Order processed: Order #12345").
  • The controller sends the response back to the client.

In this article, we’ve explored the Mediator pattern, how it helps reduce tight coupling between components, and how to implement it in a .NET Web API. By using MediatR, we can streamline communication between objects and create more modular, maintainable code.