Hey folks,
Welcome to this week’s blog post. First of all, we are happy to announce that we came up with a new product name for project “Foody”. As you may have already noticed in the URL, we are now Unidash. 👋
This week, the queen shared some insights into coding patterns. I’d like to take this opportunity and explain how we’ve incorporated some well-known patterns in the backend project.
Mediator Pattern
The Mediator Pattern is a behavioral design pattern that restricts calling dependencies to each other directly. Instead, it forces you to call methods through a mediator that coordinates and forwards your requests to the respective handlers that deal with the tasks.
This pattern reduces the overall boilerplate and can help you to organize your code, especially if you are working with various product features.
In the realms of .NET and C#, there’s the MediatR NuGet package that allows you to implement this design pattern. The package provides the base classes, interfaces, and the mediator. All that’s left to do is to write the requests and request handlers.
For Unidash, we’re using the mediator pattern for HTTP route actions and CQRS. Here’s a sample:
As you can see, we are pulling the IMediator
object instance from .NET Core’s built-in IoC Container in the UsersController
class.
In the actions methods, we are using this instance to fire a new request through the mediator object: await _mediator.Send(new GetUserRequest(id));
The Mediator will resolve the corresponding handler based on the same IoC container. The great part is that MediatR integrates well into the IoC container and enables us to use all instances that are part of the container. We’ll let the code speak for itself, it’s really straight forward.
As I said in the beginning of the post: Using this pattern allows us to organize our code very well:
CQRS
CQRS – short for Command Query Responsibility Seperation – is a commonly used pattern for querying/reading data, but also performing write actions.
The Command and Query Responsibility Segregation (CQRS) pattern separates read and update operations for a data store. Implementing CQRS in your application can maximize its performance, scalability, and security. The flexibility created by migrating to CQRS allows a system to better evolve over time and prevents update commands from causing merge conflicts at the domain level.
Microsoft Docs
To understand the problem, we let the experts at Microsoft speak again because they do a way better job at explaining it than us:
In traditional architectures, the same data model is used to query and update a database. That’s simple and works well for basic CRUD operations. In more complex applications, however, this approach can become unwieldy. For example, on the read side, the application may perform many different queries, returning data transfer objects (DTOs) with different shapes. Object mapping can become complicated. On the write side, the model may implement complex validation and business logic. As a result, you can end up with an overly complex model that does too much.
Microsoft Docs
It builds on top of the Mediator Pattern and can be easily achieved with the same approach.
Commands should be task based and always modify data. Queries however should only read data from the data source.
Based on the same principle as above, we can introduce Commands and Queries as MediatR requests and request handlers. Here’s another sample:
Command
Query
The commands/queries are then being used by the request handlers as outlined in the Mediator section above.
var user = await _mediator.Send(new FindUserQuery(request.Id), cancellationToken);
As of now, we resemble CQS. For the ‘Responsibility’ part, we need to introduce a separate read model (also called projection, view, DTO, whatever you prefer) for our queries because right now we are pulling the same domain object class from the database. This is on our to-do list.
A query returns a DTO that does not encapsulate any domain knowledge.
Microsoft Docs
Verdict
Using this pattern will allow us to be more adaptable to future changes and requirements. This is especially useful in our case since we intend to add or even change features as we go, while in SE class but also after our final presentation.
Other than that, it can also help you to maximize performance, scalability, security, and flexibility while development, debugging and in production.
As for flexibility, the MediatR package also allows you to modify the mediator pipeline. We intend to add a validation pipeline to it that validates all requests, commands, and queries before they are executed. Pretty dope.
Here’s a class diagram demonstrating the request flow for the GET /user/{id}
route for your viewing pleasure:
In case you are curious for more, check out Microsoft’s and Martin Fowler’s take on CQRS. Let us know what you think.
Cheers, and stay safe,
Gino
4 replies on “The Mediator Pattern & CQRS”
Heya Unidash!
First of all, a very nice project name you chose now.
Also, you explained very well on why you use certain patterns and what they should do.
However you may consider adding an overall class diagram to show where exactly those patterns appear in your code.
With best regards
Florian from Itemize
Hi Florian, thanks for the overall feedback. I included a code map of a request flow.
Hi,
your blogpost ist quite interesting and it is very well shown what you did. The only thing I miss is a direct comparison of the before and after class diagram.
Yours MAPHYNN
Thank you for your comment. We don’t have a direct comparison because we started embracing Mediator and CQRS from day one.