CQRS & Mediator in .NET Core — “A piece of cake”

Thanh Le
8 min readMar 18, 2020

--

Photo by Cédric Dhaenens on Unsplash

As a Technical Architect, I always read and try to practice design patterns as well as architectural principles. And martinfowler.com is one of the websites that I regularly visit. To be honest, I’m a big fan of Martin Fowler :). When I first read about CQRS pattern on martinfowler page, 7–8 years ago. My first impressions were very negative — as I had a feeling that CQRS makes applications become over-complex than they need. But I’ve changed my mind since Microservices Architecture and DDD (Domain Driven Design) become increasingly popular. I’ve done 2 projects with CQRS so far.

CQRS Overview

Photo by Will Francis on Unsplash

CQRS stands for Command Query Responsibility Segregation. This is a design pattern or development practice that allows you to separate your create/update operations (we call it — Commands) from your reads (we call it — Queries) each returning their response models for clear segregation of each action.

But, why CQRS?

Having 1 model for both command and query purpose is better than 1 model for query and 1 model for command, isn’t it?

Having a central place for all business logics (both command and query) would help us to reduce the line of code as well as make our solution is simple — Simple is the best! Even if you’re a Junior Developer, it would help you to get familiar with source code quickly.

And furthermore… (feel free to share something that you have experienced with)

Yes, that was mine 7–8 years ago. I have had positive comments about using CQRS since I applied this pattern to my project for the first time.

Photo by Ross Findon on Unsplash

Please see the picture below

I have an Order class that has 2 methods (MakeOrder and GetOrderById). I also have OrderModel class that is shared between 2 methods (MakeOrder and GetOrderById) — ProductName and OrderPersonName are only used for GetOrderById. There is no problem with my code if our business is not growth. But, let’s imagine when your business logic is growth and Order class will have to add more and more functions to adapt the changes, go along with this your OrderModel will become very complicated — I don’t want to say that out of our control as we have to add more properties that fit for new functions.

So, what CQRS can help?

Firstly, separating the commands and queries allows the input and output model to be more focused on the specific task they are performing. This also makes testing the models simpler because they are less generalized and are therefore not bloated with additional code.

Back to the sample above, a command input model should contain only table fields that need to store Order and foreign key Id from related tables (Product, Identity) and the response will be very small (boolean or integer). Besides, command input models may perform some business logic on the properties in order to validate the object. Queries, on the other hand, will usually require slimmer input models (Id, or search criteria) but have a large response model as they might need to get data from many sources/tables. By contrast, the models used for a query will generally contain less business logic.

Let’s try to refactor the sample above to apply CQRS!

Command Query Responsibility Segregation

Here is the Solution Structure

Firstly, we should create 2 interfaces IMakeOrderCommandHandler and IGetOrderByIdQueryHandler

And, implement these interfaces in MakeOrderCommandHandler and GetOrderByIdQueryHandler

Create corresponding request and response models

Create OrderController

Wait, please remember to register your interfaces in Startup.cs

Note: if you’re not familiar with Dependency Injection in ASP.NET Core. This is the article, you should read: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1

Well, a lot of files/classes need to be created…

As with any pattern, there are pros and cons to consider. Some may feel that the complexity added by having to manage different models may outweigh the benefits of separating them. Also, as with all patterns, the concept can be taken too far and start to become a burden on productivity and readability of the code. Therefore the degree to which one uses the CQRS pattern should be governed by each use case. If it’s not providing value, then don’t use it!

Photo by Erik Brolin on Unsplash

But, you still want to use CQRS and don’t have to worry about managing different models…. is there any design pattern or library that could help you?

Yeah, ladies and gentlemen! I would like to introduce Mediatorrrrr!

Mediator

Photo by Bruno Kelzer on Unsplash

Let’s imagine what happens if we don’t have air traffic control (ATC) at the airport. The airplane A will have to send and wait for the signal from other airplane before landing or taking off. If there are only 2 or 3 airplane then it’s no problem. But it will become chaos if have more airplanes.

Instead of sending and getting signal from other airplanes, the airplane A just has to send the signal to ATC and ATC will help to send the signal to other airplanes and vice versa.

Yes, that is the Mediator design pattern.

The ATC in the sample above provides a central point of communication for airplanes. It’s the same with Mediator that helps us to solve the following problems:

  • Reducing the number of connections between classes.
  • Encapsulation of objects using the mediator interface.
  • Providing a unified interface to managing dependencies between classes.

Note: Instead of injecting IMakeOrderCommandHandler and IGetOrderByIdQueryHandler in the sample above, we just need to register IMediator and it will help us the rest :)

Applying Mediator to ASP.NET Core project become more easily with MediatR library.

Let’s apply MediatR to the sample above.

Note: You can download the source code here: https://github.com/thanhle0212/CQRSAndMediatorSample

Install MediatR library via Nuget

You can install MediatR package via Nuget Package Manager with the following command:

Install-Package MediatR

Or via the .NET Core command-line interface:

dotnet add package MediatR

I choose the following way

Find MediatR package and install

Refactor source code like the following

Update Handler classes

Register MediatR in Startup.cs

Make sure that you install the following package from NuGet “
MediatR.Extensions.Microsoft.DependencyInjection

Update Order controller

Instead of injecting all handler interfaces, we just need to inject IMediator interface.

Let’s do some testing with Postman to verify refactoring

Great, it’s working well :)

Unit Test

Let’s write some unit test script to test our OrderController

I will use “XUnit” library to write UnitTest and Moq for mocking

Write test cases

Explicit Dependencies Principle

Although the code sample above already helped us to resolve some issue with applying CQRS, however, it introduced a new problem with hiding Dependency Injection implementation (we are done this via MediatR).

“Explicit Dependencies Principle” is one of Architectural Principles that we should follow. You can find principles in this article from Microsoft: https://docs.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/architectural-principles#explicit-dependencies

Hiding dependencies with Mediator will create some problems like:

  • Run-time errors on the execution of a method.
  • Harder to trace dependencies: Devs maintaining the code have to understand the naming conventions, how the call stack for request handlers looks like, etc. in order to resolve dependencies. It may increase maintenance costs.

SUMMING UP

Together, we finish a sample project that applies CQRS and Mediator pattern. They are two separated design patterns — please remember this.

Hopefully, through this article, you can have more view on considering apply CQRS and Mediator to your projects.

Don’t try to apply CQRS and Mediator design pattern in your real projects if you don’t really understand what they are and how they work. CQRS and Mediator might good for some projects, not for all.

REFERENCES:

SOURCE CODE:

--

--

Thanh Le
Thanh Le

Written by Thanh Le

A Software Technical Architect — Who code for food and write for fun :)

Responses (7)