The Mediator Design Pattern is a behavioral design pattern that helps manage complexity in applications with tightly coupled components by centralizing communication through a mediator object, reducing dependencies and making the application easier to maintain, implemented using the MediatR library in .NET.
Introduction
Any application of substantial complexity usually consists of many different components communicating with each other. Components consist of business logic.
This creates tight coupling as we cannot replace or change one component without impacting the rest of the application.
Mediator Design Pattern
Mediator is a behavioral design pattern which is used to manage complexity in applications which have tight coupling between many different components. It helps remove such tight coupling between the objects. This makes the application easy to maintain.
Instead of directly communicating with each other, objects communicate using the mediator
object. Let's say if we have components A and B. A needs to communicate with B. Now instead of directly communicating with B, A notifies the mediator
object of its intent to communicate with B. And it's the mediator
object's job to notify B. This centralizes all dependencies to the mediator
object.
Mediator
class defines a single notify
method which components uses to trigger the appropriate handler component using the mediator
object. On receiving the notification from the sending component, mediator
identifies and calls the handler component.
MediatR Library in .NET
In .NET, MediatR
NuGet package is available for implementing the Mediator
pattern. MediatR
library is also useful for implementing CQRS in your. NET application.
To use it in your application, you need to add the MediatR
NuGet package in your application. You can add it using the Package Manager Console as:
Install-Package MediatR
or using the Nuget package manager:
Add the Dependency
Next step is to add the MediatR
dependency in your application. In .NET 7 application, you can inject the dependency as:
builder.Services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
Inject mediator
dependency in your class using constructor injection:
private readonly IMediator _mediator;
public MediatorTestController(IMediator mediator)
{
_mediator = mediator;
}
Define a Class Deriving from IRequest Interface
This is our Message which clients will pass to the mediatR
.
public class Product : IRequest<string>
{
public int Productid { get; set; }
public Product(int id)
{
Productid = id;
}
}
Define Message Handler
This is the actual class which will handle the request for the above message object:
public class ProductHandler : IRequestHandler<Product, string>
{
public Task<string> Handle(Product request, CancellationToken cancellationToken)
{
return Task.FromResult("Sample Order");
}
}
Send the Message
Now, the final step is to invoke the send
method of the MediatR
object. To this method, pass the message object we defined in the first step as a parameter.
As you can see, we are not directly calling the ProductHandler
class, instead we are calling the send
method defined in the mediator
object.
Now when you execute your application, you will get the following response:
This may not make much difference in this scenario since we have just one class here, But imagine if our application consists of hundreds of classes. This would significantly reduce the coupling between our classes. This is because our classes will communicate with the mediator
object rather than communicating with each other directly. This implies that our classes will now depend on just one mediator
class instead of depending on a lot of other classes.
History
- 21st September, 2023: Initial version