Use Case: When applications change frequently, you can updated the Queries & Commands without affecting each other. When the Application is read heavy, you can then split the database into a Read (performance tuned for read) and Write (Fully normalized)
Definition
“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.”
CQRS has 3 building blocks
Building block | Description |
---|---|
Command / Query | All the data we need to execute the Query or Command. This is represented as an object instance. |
Handler | Business logic to execute the Command or Query. This returns a response. |
Response | Return data that we want to return encapsulated as an object. |
.Net 5 C# 9 & MediatR
All of the examples below are from Jonathan Williams: CQRS using C# and MediatR
Dependancy libraries
Jonathan keeps the code together in a static class for findability and describes this as a container for the building blocks. Type of record
is recommended for Query
, Command
and Responses
as they are immutable, these are DTO’s (data transfer objects) so we dont want them to change.
Implementation
All the code below has been simplified for brevity, the full source is on github. The Repository
is just a data repository that returns a List<Todo> Todos
where type Todo
is a domain entity with properties Id
, Name
and Complete
.
- In a
Command
folder create the static classAddTodo
- Add the record
Command
, classHandler
and recordResponse
1 | namespace CQRSTest.Commands |
- In a
Queries
folder create the static classGetTodoById
. - Add the record
Query
,Handler
and recordResponse
1 | namespace CQRSTest.Queries |
- In the controller use constructor injection to inject
IMediator mediator
, then usemediator.Send(requestObject)
to infer the relativeCommand
orQuery
.
1 | namespace CQRSTest.Controllers |
- In the application startup pipeline add Mediator to the dependancy injection container using the extension method
AddMediatR
. Passing the assemblyStartup
tells mediator to use refection to include our classes above in the dependancy injection container (as they are in the same assembly as Startup). It will be looking for theIRequestHandler
interface.
The order is important, any Repository
being used needs to be added to the container first as this will be resolved/injected into the handlers constructor.
1 | services.AddSingleton<Repository>(); |
Jonathan did a sweet video covering the code above.
.Net 3.1
TODO