After understanding the basic concepts and the recommended structure, it's time to move on to implementation. At this stage, we will configure MediatR and create the first commands and queries in a .NET application, using a simple example: Post.
3๏ธโฃ Installing MediatR
To separate responsibilities and avoid direct ties between layers, we use MediatR โ a library that facilitates sending commands and queries to dedicated handlers.
๐ ๏ธ Installation
In the MyApp.Application project, add MediatR:
dotnet add package MediatR
dotnet add package MediatR.Extensions.Microsoft.DependencyInjection
In MyApp.API (or the UI/API project), configure the services in Program.cs:
builder.Services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(typeof(MyApp.Application.AssemblyReference).Assembly));
Add a reference class in MyApp.Application:
namespace MyApp.Application;
public static class AssemblyReference { }
4๏ธโฃ Creating a simple command: CreatePostCommand
๐งพ Command: CreatePostCommand.cs
using MediatR;
public record CreatePostCommand(string Title, string Content) : IRequest<Guid>;
IRequest<Guid> means that this command will return a Guid (the ID of the created post)
๐ง Handler: CreatePostCommandHandler.cs
5๏ธโฃ Complete separation for a query: GetPostByIdQuery
๐ Query: GetPostByIdQuery.cs
PostDto is a display-only object โ without business logic.
๐ Handler: GetPostByIdQueryHandler.cs
๐ฆ PostDto.cs (in Application)
6๏ธโฃ Summary: IRequest and IRequest
|
Request type |
Interface |
Returns |
|---|---|---|
|
Command (write) |
IRequest<Unit> |
Nothing (Unit.Value) |
|
Command with result |
IRequest<Guid> |
For example: the created ID |
|
Query (read) |
IRequest<PostDto> |
A DTO type result |
๐งช Usage example from Controller / UI
๐ง Conclusion
At this stage, we have implemented the first essential pieces of a CQRS system:
-
โ Commands that modify the application state
-
โ Separate queries for reading
-
โ Dedicated and independent handlers
-
โ MediatR as a central intermediary
๐ What's next
In the next article we will add:
-
โ Validations with FluentValidation
-
โ Mapping between DTOs and entities
-
โ Clear separation between WriteModel and ReadModel