I'm struggling to understand why so many examples on the web are using MediatR when explaining CQRS patterns, when dealing with commands and queries.
Almost everywhere I see examples where Commands and Queries are handled by MediatR but I do not see any benefit from it other than not needing to register every Command or Query in Dependency Injection Container. But then you need to implement Query objects (inheriting IRequest), Query Handlers, and Query Responses objects so that in your API controller method you can then call _mediatr.Send(queryObject)
.
Why not just use dependency injection to inject query object into API controller which you can call "get" methods directly on? Like:
[HttpGet]
[Route("getall")]
public async Task<IncidentQueryResult> GetAll(int page = 0, int pageSize = 25)
{
var result = await _incidentQueries.GetIncidents(page, pageSize);
return result;
}
instead of:
[HttpGet]
[Route("getall")]
public async Task<IncidentQueryResult> GetAll(int page = 0, int pageSize = 25)
{
var query = new IncidentQuery(page, pageSize);
var result = await _mediatr.Send(query);
return result;
}
Then, inside the GetIncidents
method, there's a direct sql call to database and mapping results to C# objects. Plain and simple.
For me, the perfect and only reasonable use of MediatR library is to handle Domain Events. When implementing DDD, I'm trying to set up a project in the way presented below. Each rectangle is a different project in the solution. The arrows represents references:
Let's imagine a scenario: Creating a domain object needs to increment a counter stored in another domain object (a different aggregate)
- A request is made to API endpoint to add some new domain object to database (layer 6: Presentation)
- The controller method uses a command injected in it's constructor to create a domain object (layer 4: Commands)
- Inside the command, a new domain object is created along with "domain object created" event stored in this object, ready to be broadcasted just before saving to database
- Then the Command uses Repository from Infrastructure layer to add this newly created object to database.
- Then just before database save is performed: the "domain object created" event is sent through MediatR (layer 2: Infrastructure)
- The event is then caught in layer 3: Application in one of the Domain Events Handler.
- The domain event handler (layer 3: Application) uses Repository from Infrastructure layer to get another domain Aggregate holding a counter to be incremented and then increments the counter.
- All domain events were handled, save to database is performed.
So the MediatR for me works only in Infrastructure and Application layer.
Are people just using MediatR for Commands and Queries just for sake of using it? For me it looks like adding commands and queries handlers, query and command request and response types only adds more code that has no real value and only makes it less understandable.
Here are some links I visited:
- https://referbruv.com/blog/posts/implementing-cqrs-using-mediator-in-aspnet-core-explained
- https://www.edument.se/en/blog/post/net-5-source-generators-mediatr-cqrs
- https://itnext.io/why-and-how-i-implemented-cqrs-and-mediator-patterns-in-a-microservice-b07034592b6d
- https://www.rubicon-world.com/blog/2020/06/a-developers-guide-to-cqrs-using-net-core-and-mediatr/
- https://dotnetdetail.net/cqrs-and-mediator-patterns-in-asp-net-core-3-1/
A lot of sites in my native language has this as well.
Having too many handlers used throughout your application makes difficult to read what your application does and what triggers what. What I saw was that people were handling domain events in Commands layer but the domain probably shouldn't send commands directly?
Do you need to use MediatR at all in CQRS?