CQRS (Command Query Responsibility Segregation) is an architectural pattern that divides the application's responsibilities into two clearly defined areas:
-
Commands — modify the application's state (e.g., add an article)
-
Queries — only read data, without side effects
This separation brings clarity to the code, allows for more precise validations, and prepares the application for scalability and extensibility.
1️⃣ What is CQRS?
At its core, CQRS proposes not to use the same objects and services for reading and writing data. Instead, we completely separate:
-
✏️ Commands – requests that modify the state (e.g., CreatePost, UpdateUser, DeleteInvoice)
-
🔍 Queries – requests that only read the information (e.g., GetPostById, SearchUsers, ListInvoices)
📌 Key Benefits
|
Benefit |
Explanation |
|---|---|
|
🔄 Clear separation |
Write code is isolated from read code – easier to understand, test, and maintain |
|
🧪 Dedicated validations |
You can apply different rules for what is written vs. what is read |
|
🚀 Easy scalability |
You can optimize or even separate infrastructure for read and write (e.g., replicated DB for queries) |
|
⚡ Performance |
Queries can use lightweight DTOs, projections, or cache without affecting business rules |
|
🔐 Security & audit |
Commands can trigger events, logs, or additional checks |
2️⃣ Recommended project structure
CQRS works excellently in an application organized according to the principles of DDD (Domain-Driven Design). Such a structure promotes separation of responsibilities and testability.
📁 Layered structure:
🔍 Layer details
✅ MyApp.Application
-
📦 Here live the Commands and Queries
-
✔️ Contains Handlers, Validators, DTOs
-
🤝 Communication is done through MediatR: Send(new CreatePostCommand(...))
🧠 MyApp.Domain
-
Definition of the business model
-
Entities, ValueObjects, aggregates, interfaces (IPostRepository)
-
Does not contain references to infrastructure or MediatR
🧱 MyApp.Infrastructure
-
Implementations for interfaces from Domain or Application
-
ApplicationDbContext (EF Core), Repositories, files, email services, etc.
🌐 MyApp.API / MyApp.UI.Blazor
-
Controllers or UI pages
-
Here requests are received and forwarded to Application (via MediatR)
-
Does not contain business logic
🧩 Example flow
🏁 What’s next
In the next article, we will build the first complete Command: CreatePostCommand, with handler, validation, mapping, and saving to the database. We will introduce MediatR and FluentValidation and see how this approach brings clarity and extensibility.