Is MediatR library overused in CQRS examples on the web?
Asked Answered
I

3

17

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:

DDD with CQRS schema

Let's imagine a scenario: Creating a domain object needs to increment a counter stored in another domain object (a different aggregate)

  1. A request is made to API endpoint to add some new domain object to database (layer 6: Presentation)
  2. The controller method uses a command injected in it's constructor to create a domain object (layer 4: Commands)
  3. 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
  4. Then the Command uses Repository from Infrastructure layer to add this newly created object to database.
  5. Then just before database save is performed: the "domain object created" event is sent through MediatR (layer 2: Infrastructure)
  6. The event is then caught in layer 3: Application in one of the Domain Events Handler.
  7. 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.
  8. 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:

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?

Innermost answered 11/3, 2021 at 21:50 Comment(0)
N
16

MediatR is just one library to solve a specific need. As the repository puts it:

In-process messaging with no dependencies.

Command Query Responsibility Segregation (or CQRS) is a design pattern that can be implemented in many ways. Fundamentally as long as your reads and writes are independent you are "following" this pattern.

So no, you can build an entire app that is CQRS "compliant" without using MediatR or any other messaging library. That said you can also build it as one massive file. CQRS is simply one of many tools for managing code you can deploy depending on your needs.

You can also use MediatR for in process messeging only and not apply the CQRS pattern.


That said a common reason you likely see tutorials, blogs and other .NET CQRS resources use MediatR (or a similar messaging library) is that generally any app using CQRS is also going to want the following:

  1. A way to validate at some level that queries remain queries and commands remain commands
  2. A greater degree of "separation of concerns" applied to the code as a whole.

MediatR generally solves both these problems very nicely by separating out the execution of the command to it's implementation (which can live in a separate project)

In your case for example, Presentation has to have a knowledge of the database implementation as well as it's schema in order to execute queries as well as mapping them to database resources rather then leaving that as a concern of infrastructure and infrastructure alone. In my experience this can lead to either lots of duplicate code or lots of unintended coupling between projects. Better to have the presentation layer souly focus on presentation and send a message out to whichever service (it does not care which or deal with registering them in MediatR) can give it Query information upon request.

Essentially in your diagram MediatR (and/or NServiceBus, Brighter, MassTransit, Rebus... if you need to scale beyond a single process) would act as a way to control the flow of data and detatch the client of the Query/Command from it's handler.


So finally to answer:

Are people just using MediatR for Commands and Queries just for sake of using it?

Yes and no, they are mostly using it as a separate good practice that plays very nicely together with the CQRS pattern to control their dependency flow. Although you are right that for many beginners to these ideas they can tie the two together in ways that are not required or recommended.


I recommend you take a look at his other work on Clean Code to understand how all these pieces play together to create (what he calls) a "pit of success" for future developers working on the project. He has a template repo here and several talks online about it: https://github.com/jasontaylordev/CleanArchitecture

Nikkinikkie answered 11/3, 2021 at 22:15 Comment(0)
S
5

You are right in that MediatR library and DDD/CQRS for some reason have become synonymous. But they do not have to be used together. You can use MediatR without Domain-Driven Design and visa-versa. The approach has become popular due to its simplicity and availability of a variety of sample projects.

One of the reasons MediatR is popular is because it takes the business and application logic outside of Controller, which, historically, are difficult to write tests for.

MediatR pattern gives you an easy way to separate these concerns. There are other alternates available as well such as Service Stack. The same concept could be also be used with Vertical Slice Architecture that aims to separate the code by features.

Similarly, while implementing Domain-Driven Design, you are free to choose what works for you. However, MediatR does give you the flexibility to keep your code extensible (Open-Close Principle). In future, if you need to replace this architecture with a real Message Bus using NServiceBus or other mechanisms you do not need to change your entire code-base.

Staffman answered 15/3, 2021 at 19:58 Comment(3)
Yes, but in 99% of the controller you call a single method. Mediator, I think, is useful in decoupling but using a service behind the controller is enough in most cases and easier to implement and debug. I agree that Mediator pattern is overused in web example.Makeup
"But these are mutually exclusive" - that's implying they can't be used together - I don't think that's what you mean is it? In your post you are saying you can use both of them or one or the other, so I think you mean "they don't have to be used together, but often are".Quaternary
Hi @tomRedox, you are right. I have fixed my answer to make it clear.Staffman
P
2

The greatest disconnect I see between MediatR and CQRS is that both commands and requests go through the IMediator.Send() method. If, at the very least, there were separate abstractions for commands and queries, I'd see the connection to CQRS.

MediatR is at best neutral with regard to CQRS. The implied connection between MediatR and CQRS is nonexistent, and articles that offer to explain it don't.

For example, one of the posts relates MediatR to CQRS this way:

In CQRS, the responsibilities for Querying and Commands are taken up by their respective Handlers and it becomes quite difficult for us to manually wire up a Command with its Handler and maintain the mappings.

The implication is that by learning how to map commands and queries to handlers we are learning how to implement CQRS. But CQRS is about separating queries and commands. It has nothing to do with whether or how an application maps commands to handlers. So the articles tend to describe CQRS and then provide an implementation detail which has nothing to do with CQRS. The popularity of such articles nonetheless reinforces the notion that MediatR and CQRS are intrinsically connected.

This isn't a problem with MediatR. Some articles leave out MediatR and just refer to the mediator pattern, still without drawing the connection between CQRS and the mediator pattern. That appears to be the core misunderstanding. We might use any number of design patterns in an application that applies CQRS, but describing both the design pattern and CQRS in the same article doesn't mean that using the pattern leads to CQRS.

At worst the IMediator interface hinders CQRS because it fails to distinguish between commands and requests. Neither MediatR nor any other abstraction can prevent a query handler from executing command behavior, but if we're separating commands and queries - the heart of CQRS - the abstraction should support that separation.

Pectize answered 14/10, 2021 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.