In this article let us learn the basics of the CQRS pattern using the MediatR NuGet package in .Net 5.
We know how a traditional web API works. It mainly consists of the CRUD operations. All four operations are tied together inside an API controller.
C — Create
R — Read
U — Update
D — Delete.
To decouple the application we make interfaces for our information access layer, inject it in the constructor of the API controller and perform the actions. This turns out great and all looks great. Yet, as your application grows in size the number of dependencies also increases. Now we need to inject multiple interfaces in the API controller and hence the application complexity increases.
In order to solve this problem, let’s use CQRS & Mediator Patterns.
CQRS stands for Command Query Responsibility Segregation. That is separating Command (write) and Query (read) models of an application to scale read and write operations of an application independently. Instead of having all of the four CRUD operations together, let us segment them out into two different pieces.
The mediator is used to reducing communication complexity between multiple objects or classes. This pattern provides a mediator class that normally handles all the communications between different classes and supports easy maintenance of the code by loose coupling. Mediator pattern falls under behavioral pattern category.
Basically, a Mediator performs 2 operations.
Accept the incoming request
Handles the incoming request and provides the response.
CQRS + Mediator Patterns are preferred over large projects.
Let’s get into the code and see how these CQRS & Mediator patterns work together.
Read data using CQRS & MediatR
Open Visual Studio 2019. Create an ASP.Net Core Web API project named EmployeeManagement.API. I have given the solution name as EmployeeManagementDemo. (You can give any name of your choice).
Make sure that you select the .Net 5.0 as the target framework and enable the Open API support for Swagger and click create.
Add a new class library project named EmployeeManagementLibrary. Select the target framework as .Net 5.0
Add folders named Data & Models. In the Models folder, add a class named EmployeeModel with three properties named Id, FirstName & LastName.
Create a class named DataAccess and an interface named IDataAccess in the Data folder. In this example, I am using an in-memory collection for storing data. If you want to try it out with any DB, you may add the logic to perform DB operations.
Add two methods named GetEmployees & AddEmployees to the IDataAccess interface as shown in the figure below.
Let DataAccess class inherit from IDataAccess interface and implement the interface as shown in the figure below.
Now let’s add the MediatR NuGet package to EmployeeManagementLibrary project. Browse for MediatR NuGet package in install it.
At the time of writing this article, the latest stable version of MediatR is 9.0.0.
Add two new folders named Queries & Handlers in EmployeeManagementLibrary project.
Add a class named GetEmployeeListQuery in the Queries folder. I am using records (C# 9 feature) instead of a class. If you are not familiar with records, I highly recommend you go through them. Let us implement the IRequest interface of the MediatR package as shown in the figure below.
IRequest is a generic interface. We need to specify the return type of the request. In our case, I have specified List<EmployeeModel> as I need to return all employees.
We need to handle this request. For that let us add a handler class in Handlers folder. In our case, I have added a class named GetEmployeeListHandler.
GetEmployeeListHandler implements the IRequestHandler interface of the MediatR package as shown in the figure below.
IRequestHandler is a generic interface. So what do you mean by IRequestHandler<GetEmployeeListQuery, List<EmployeeModel>? It means that if the request is of type GetEmployeeListQuery, then return List<EmployeeModel>.
Inject the IDataAccess in the GetEmployeeListHandler class and apply the logic to return all employees in the Handle method.
Now let us go to the API project and configure the Startup services. Add the reference of EmployeeManagementLibrary.
We need to configure the IDataAccess and the MediatR in the API project. Install the NuGet package named MediatR.Extensions.Microsoft.DependencyInjection.
Once the installation is complete, open the Startup.cs class file and configure the IDataAccess and MediatR in the dependency container.
I have added the MediatR to the services collection which can access all classes in EmployeeManagementLibrary project.
Now let us create an API controller named EmployeesController. Inject the IMediator interface in the constructor and add logic to fetch all employees as shown in the figure below.
Invoke the Send method of the IMediator interface. The send method accepts a type of IRequest. In our case, GetEmployeeListQuery is a type of IRequest.
So, what happens behind the scenes is that when we invoke the Send method with the request as GetEmployeeListQuery, the mediator will invoke the handler which accepts this request. In our case, it will go and call the GetEmployeeListHandler class and returns the response as List<EmployeeModel>.
Now run the API project. Since we have added the open API support, the swagger page will be displayed for testing our endpoints. Click on the api/Employees get method.
Now let us see how to retrieve an employee by Id.
As we did before, add a new query named GetEmployeeByIdQuery under the Queries folder in EmployeeManagementLibrary project. This time the query accepts a parameter named Id of type int and the response is of type EmployeeModel.
Now let’s add a handler for this request as shown in the figure below.
In this handler, instead of injecting the IDataAccess, I have injected the IMediator interface in the constructor. In the handle method, using the injected interface, I am trying to get all the employees bypassing the GetEmployeeListQuery object and then filtering the employee with the Id passed as input.
Here you can see that I am able to access the id passed as input using the request parameter.
In a real scenario, we will be injecting the IDataAccess to retrieve the employee by Id from a database.
Go to the API project -> Open EmployeesController and add a HTTP get method which accepts an integer parameter as shown below.
Note that I am passing the GetEmployeeByIdQuery(id)as the request. Run the API project and click on the Get button of api/Employees/{id}. Pass an input id=1 and verify the output.
I hope you all understood how to use the Mediator & CQRS pattern in reading data.
Write Data using CQRS & MediatR
Till now all Read requests are kept inside a Queries folder. All reads are treated as queries.
All other CRUD operations except Read are kept under a Commands folder. Create a Commands folder in EmployeeManagementLibrary project.
Create, Update & Delete operations are treated as Commands.
Add a new record named AddEmployeeCommand which takes two parameters named FirstName and LastName. Make sure that this record inherits the IRequest interface of the MediatR package as shown below. So, whenever the user request for AddEmployeeCommand, it returns an EmployeeModel.
Create a handler for AddEmployeeCommand requests. Add a class named AddEmployeeHandler in the Handlers folder.
The IDataAccess interface is injected in the constructor. Implement the Handle method of IRequestHandler<AddEmployeeCommand,EmployeeModel>. The FirstName and LastName inputs can be accessed from the request parameter.
Open the EmployeesController of API project and include a Post method to add a new employee as shown below.
Now run the API project and execute the post method in swagger UI by passing the following inputs and verify the output.
The response is a new employee with id=3.
Source: Medium : Alpesh Patel
The Tech Platform
コメント