DEV Community

Cover image for Integrating CQRS and Mediator in .NET with MediatR: An Elegant Convergence for Robust Applications
AlexSandro Cruz
AlexSandro Cruz

Posted on

Integrating CQRS and Mediator in .NET with MediatR: An Elegant Convergence for Robust Applications

When we dive into the world of software architecture, we encounter a myriad of patterns and approaches. Among them, CQRS (Command Query Responsibility Segregation) stands out, especially when combined with the Mediator pattern. In this article, we will explore how these two concepts interrelate and how the MediatR library fits into this scenario, enhancing development in .NET.

Understanding CQRS

CQRS is a design pattern that separates the logic for reading and writing data into distinct parts. This simplifies implementation, allows independent optimizations, and provides a clear separation of concerns where, on one side, we have Queries (queries) that fetch data without changing it, and on the other, Commands (commands) that perform changes in the system (such as insertions, updates, or deletions).

The Role of the Mediator

Now, where does the Mediator pattern fit into this? The Mediator promotes loose coupling between software components by introducing an intermediary layer responsible for communication between different parts of the system. Instead of components communicating directly, they go through this "mediator," which forwards commands and queries to the appropriate handlers.

The Elegant Convergence: MediatR in .NET

The MediatR library, widely used in the .NET ecosystem, is an implementation of the Mediator pattern that harmonizes perfectly with CQRS. With MediatR, commands and queries are objects that encapsulate data and intentions, while handlers process these requests.

Implementing with MediatR

Let's go to a practical example using MediatR in a .NET application:

Defining a Command: First, we create a class to represent our command. This class, simple and straightforward, might look like the following:

public class PerformOperationCommand : IRequest<OperationResult>
{
    // Properties that define the command
}
Enter fullscreen mode Exit fullscreen mode

Creating the Handler: Next, we need a handler for this command — a class that implements the IRequestHandler interface.

public class PerformOperationHandler : IRequestHandler<PerformOperationCommand, OperationResult>
{
    public Task<OperationResult> Handle(PerformOperationCommand request, CancellationToken cancellationToken)
    {
        // Logic to process the command
    }
}
Enter fullscreen mode Exit fullscreen mode

Orchestrating with MediatR: With MediatR set up in our application (usually done at startup, registering it in the dependency injection container), we can send commands as follows:
// Within a controller, for example

public class OperationController : ControllerBase
{
    private readonly IMediator _mediator;

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

    public async Task<IActionResult> PerformOperation(OperationData data)
    {
        var command = new PerformOperationCommand(/* ... */);
        var result = await _mediator.Send(command);
        return Ok(result);
    }
}
Enter fullscreen mode Exit fullscreen mode

Why is this combination powerful?

By using CQRS with MediatR, we gain several benefits:

Decoupling of Logic: Our commands/queries and their business logic are in separate classes, making maintenance easier.
Testability: We can test commands, queries, and their handlers separately, which is excellent for unit and integration tests.
Readability and Organization: The code becomes cleaner, and operations within the system are easily traceable since each action is represented by a specific command or query.
Conclusion
The integration of CQRS with the Mediator pattern through MediatR in .NET applications provides a cohesive, decoupled, and highly efficient framework for managing business logic. This approach not only facilitates the maintenance and expansion of software but also promotes cleaner and more testable coding practices. In a world where software complexity is always increasing, such strategies are invaluable.

Top comments (0)