CQRS with MediatR

Matias Paulo
5 min readAug 26, 2021

--

In this article I’ll try to describe to you how to implement the CQRS pattern in .NET 5, making use of the MediatR library.

But what is CQRS? Command and Query Responsibility Segregation is a pattern that allows us to have different structures for reading and writing data.

According to Microsoft definition:

CQRS stands for Command and Query Responsibility Segregation, a pattern that 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.

When to use CQRS?

  1. Scenarios where we need to tune the load of the operations, allowing us to optimize the reads or writes as need it independently.
  2. If we can have a team working with reading operations and other with writes.
  3. Cases where we need to combinate with event sourcing.

Some definitions

Command: a command is any method that mutates state. Commands are imperatives; they are requests for the system to perform a task or action and are usually processed just once, by a single recipient.

Query: is any method that returns a value. Queries never modify the database.

References:

Let’s start coding

In this example, we are going to create a simple app that allows us to manage students. A quick and easy example.

The main idea of this project is to have an API with an endpoint for creating students and other one to retrive them. For the creation we’ll have a “command” and for the GET endpoint we’ll implement a “query”.

So, lets create a solution with the following projects:

The main project will be “CQRS.Api”, which will contain the API. Then we have “CQRS.Application” where we’ll have our commands and queries and “CQRS.Core” will have the entities and the data access.

Lets begin with the data access, add the following packages to “CQRS.Core”:

<PackageReference Include=”Microsoft.EntityFrameworkCore” Version=”5.0.9" />
<PackageReference Include=”Microsoft.EntityFrameworkCore.InMemory” Version=”5.0.9" />

Then add the “Student” entity and the DbContext.

DbContext

With the basis for creating and retrieving data, now we can focus on the CQRS pattern.

In order to use MediatR add the following package to the “CQRS.Application” project.

<PackageReference Include=”MediatR” Version=”9.0.0" />

MediatR is a library that helps us to implement a “mediator” pattern that will help us to handle the communication between objects.

A good practice could be to standardize the responses, with that in mind will create the following wrappers.

We have defined 2 interfaces:

  • IRequestWrapper<TResponse> : IRequest<Response<TResponse>>: This means that the handler which will implement this interface will have to return a “Response” instance of some generic “TResponse” class. In our case, this interface will be used by the “queries”.
  • IHandlerWrapper<in TRequest, TResponse>: IRequestHandler<TRequest, Response<TResponse>>: In this case, we are going to use this interface for the “commands”. We defined “TRequest” as contravariance and needs to be of “IRequestWrapper<TResponse>>” type. The handler will have to return some instance of “Response<TResponse>”.
Wrapper

The “Response” model will have the option to return if the method fails or not, also in case of failure, we can attach more info to the “Error” property.

Response model

Now, we can create the commands and queries. Let’s create a “command” to create a new student and a “query” to retrieve all the students that have X years old.

This query model exposes an “Age” property as a parameter and will return a collection of “StudentDto”.

Get students query
Create student command

The command exposes 2 properties for the student registration and returns a new “StudentDto” instance.

Then we can add the handlers for the commands and queries. The handler is that piece of code that will have to deal with the business rules and orchestrate data flow.

The query handler is just getting the data from the DB and transforming the results to a DTO.

Query handler

In the command, we are just adding a new student to the context, using the values that we receive in the command as parameters. In this case, we are not performing any checks but we can easily add the business rules that are necessary, like check that the “Age” is greater than X or that the “Name” is not empty or whatever you need.

Command handler

As we can see, any of the handlers is returning the entity, instead of that is returning a DTO, which allows us to not expose the data model to the outside.

The last step need it, is the endpoint and services registration.

Students controller

We can register the “SchoolContext” making use of the “AddDbContext” extension method. In this case, we are going to use the in-memory provider.

In order to register our handlers, we can add the following package: “MediatR.Extensions.Microsoft.DependencyInjection” which will allow us to make use of the “AddMediatR” extension method.

EF and MediatR registration

The controller is returning a generic standarized response for each endpoint.

In next articles we will review how to implement a “clean architecture” making use of the CQRS pattern. Also, we will make use of the pippelines that the “MediatR” library provide us to handle the request logging and db transactions.

As always you can check the source code from here.

Enjoy and happy coding! : )

--

--