Writing this for my own benefit... Need to know this for work, so learning it and wanted to capture my learning.
How to do MediatR in 5 mins. The very basic stuff and how to get started.
Why use it?
To reduce complexity in a complex system. Rather than classes depending on lots of other classes, a mediator class (using the mediator pattern) handles query requests and commands by farming them out to the appropriate class to service the request or command. The calling class does not know which class handles it, they just know they have requested something and it gets returned/done.
Also allows your code to follow the Command Query Responsibility Segregation (CQRS) architecture so you can seperate and scale up the reads or the writes if necessary. For example a read query for a list might access a reporting server that is updated every 2 minutes, whereas updates might go to the live database.
MediatR also has a great pipeline behaviors feature. This allows you to seperate concerns, so things like validation and logging can be done separate to (pre or post) the request handler.
How do you use it?
- In your API project, manage Nuget Packages and add MediatR.Extension.Microsoft.DependencyInjection. This will add the MediatR NuGet package too as it is a dependency.
- Add a new project for your infrastructure layer
- Manage Nuget packages and add MediatR for you infrastructure layer project
- Add folders for commands, queries and handlers in your infrastructure layer project
- Each request for data (a query) or create/update (a command) should be a record (if using C# 9.0 and above) that implements the
public record GetBankAccountsQuery() : IRequest<List<BankAccount>>
- Each request should have a handler. Handlers are classes that implement the
IRequestHandler<request type, return type>interface.
public class GetBankAccountsHandler() : IRequestHandler<GetBankAccountsQuery, List<BankAccount>>
- Right click on the interface and choose "Implement Interface". This will create a Handle method. Enter code within the Handle method to handle the request e.g. access data in the database or a file.
- In the ConfigureServices method in your API, add the MediatR to the services collection:
services.AddMediatR(typeof(any class where you have mediatr code e.g. a class that implements IRequest).Assembly). The .Assembly part allows MediatR to look through the asembly and find all the classes that implement IRequest or IRequestHandler and set up the mapping between them.
- In the code where you need to recieve the value returned from the query e.g. list of BankAccounts, add IMediator as a dependency to the constructor and then do code like this:
var accounts = mediator.Send(GetBankAccountQuery);
Why would you not use it?
Once your application gets to a size where multiple handlers need to process an event, you might be better going to a message handling system such as NServiceBus or MassTransit.
MediatR can have multiple handlers per event via notifications, but they are executed in the same process, so if one fails and the others don't you are left in a funny state - has it worked and you report success or not worked because one of the handlers failed?
Message based systems like NServiceBus allow you to process each event individually and respond to errors as you find them. For example say the event for making the payment from the BankAccount succeeded but the event for send confirmation email failed. Well, just re-queue the send confirmation email event again and try it again at another time when the email server is back up. No need to fail the whole event because one part failed.
We should really abstract the implementation of the mediator pattern, so that we can use other mediator packages other than MediatR if necessary, without breaking our code.
However... this requires extra code and complexity. If your project is small, this may not be worth it. e.g. a small microservice that just sends confirmation emails. Balance the benefit of being able to swap this out later easily vs speed of delivery.
Top comments (0)