Exploring the Mediator Pattern in .NET Core


August 23, 2020

When working with complex applications, it’s common to encounter scenarios where multiple components or services need to communicate with each other. This communication can quickly become tangled, resulting in a system that’s difficult to maintain and scale. This is where the Mediator pattern can come in handy.

The Mediator pattern provides a way to reduce the dependencies between objects by introducing a mediator component that handles communication between them. This pattern helps to decouple components, making the system more modular and easier to test. In this post, we will explore how the Mediator pattern can improve the structure of large .NET Core applications and demonstrate how to implement it using the popular MediatR library.


1. What is the Mediator Pattern?

The Mediator pattern is a behavioral design pattern that defines an object (the mediator) that controls the interaction between different objects. Instead of components directly calling each other, they communicate through the mediator. This reduces dependencies between components, making the system easier to maintain and extend.

In traditional systems, components (or services) directly communicate with one another, which leads to a high degree of coupling. With the Mediator pattern, the mediator object acts as the intermediary that handles all communications.


2. Why Use the Mediator Pattern?

Using the Mediator pattern provides several advantages:

  • Reduced coupling: Components no longer need to know about each other, making the system more flexible and maintainable.
  • Centralized control: Communication is handled through a central mediator, allowing for better control over the flow of data.
  • Easier testing: By decoupling components, testing individual parts of the system becomes easier, since you don’t have to worry about the dependencies between them.
  • Scalability: As your system grows, you can add new components without significantly changing existing code.

3. Implementing the Mediator Pattern in .NET Core with MediatR

To implement the Mediator pattern in .NET Core, we’ll use MediatR, a library that simplifies mediator-based communication. MediatR provides a clean API to handle requests and responses, events, and notifications.

a. Installing MediatR

First, let’s install the MediatR package in our .NET Core project:

dotnet add package MediatR

Additionally, we need to install MediatR.Extensions.Microsoft.DependencyInjection to integrate MediatR with the .NET Core dependency injection system:

dotnet add package MediatR.Extensions.Microsoft.DependencyInjection

b. Setting Up MediatR in Startup.cs

Now, we’ll configure MediatR in the Startup.cs file to set up dependency injection.

In the ConfigureServices method, add:

public void ConfigureServices(IServiceCollection services)
{
    // Register MediatR
    services.AddMediatR(typeof(Startup));

    services.AddControllers();
}

This code tells .NET Core to scan the assembly for MediatR-related types like handlers, requests, and responses.


4. Creating a Request and Handler

In this step, we’ll implement a simple request and handler using MediatR.

a. Defining a Request

A request is simply a class that represents the data being passed to the mediator. In our example, we’ll create a request to retrieve a user’s name based on their ID.

public class GetUserNameRequest : IRequest<string>
{
    public int UserId { get; set; }
}

The IRequest<string> interface specifies that this request will return a string (the user’s name).

b. Creating a Handler

A handler processes the request and returns a response. Here’s how to create a handler for our GetUserNameRequest:

public class GetUserNameRequestHandler : IRequestHandler<GetUserNameRequest, string>
{
    public Task<string> Handle(GetUserNameRequest request, CancellationToken cancellationToken)
    {
        // In a real application, you would query a database or other service
        // For simplicity, we return a static name based on UserId
        return Task.FromResult($"User-{request.UserId}");
    }
}

The Handle method is where the logic for processing the request is implemented. In a real-world scenario, you would retrieve data from a database or an external service.


5. Sending a Request with MediatR

Once the request and handler are set up, we can send the request using MediatR. In a controller, you can inject the IMediator interface to send the request:

public class UserController : ControllerBase
{
    private readonly IMediator _mediator;

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

    [HttpGet("user/{id}")]
    public async Task<IActionResult> GetUser(int id)
    {
        var request = new GetUserNameRequest { UserId = id };
        
        // Send the request to MediatR and await the result
        string userName = await _mediator.Send(request);

        return Ok(new { UserId = id, UserName = userName });
    }
}

In this controller, we send a GetUserNameRequest to MediatR via the Send method. The result is returned from the handler, and the user’s name is returned as a response.


6. Benefits of Using the Mediator Pattern in .NET Core

By applying the Mediator pattern with MediatR in your .NET Core application, you can achieve the following:

  • Decoupling: The components no longer directly communicate with each other, reducing dependencies and making the system more modular.
  • Centralized Communication: Communication is routed through a central mediator, making it easier to manage and control the flow of information.
  • Clean Architecture: Using MediatR helps keep your application’s architecture clean by enforcing separation of concerns between the controller, request, and handler.
  • Scalable Systems: The Mediator pattern allows for easy addition of new functionality, such as new commands, queries, or events, without affecting the existing code.

7. Conclusion

The Mediator pattern is a powerful design pattern that helps improve the structure of complex applications by reducing dependencies between components. By implementing the Mediator pattern in .NET Core with MediatR, you can create more modular, maintainable, and scalable applications.

Whether you’re building a small API or a large-scale enterprise application, the Mediator pattern can help manage communication between components in a clean and efficient way. As we’ve seen, MediatR makes it easy to send requests and handle responses, and its integration with .NET Core’s dependency injection system provides a seamless development experience.

In the next post, we’ll explore more advanced techniques for leveraging MediatR in complex scenarios. Stay tuned!


Let me know if you need any changes!

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

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