C# Architecture Mastery — CQRS in ASP.NET Core (When It Helps, When It Hurts) (Part 9)
CQRS is one of the most misunderstood patterns in modern .NET.
Some teams adopt it too early.
Others avoid it entirely out of fear.
Both mistakes come from the same problem:
CQRS is treated as a framework choice instead of an architectural strategy.
In this Part 9, we’ll explain what CQRS really is, when it brings clarity, and when it actively hurts ASP.NET Core systems.
1. What CQRS Actually Means
CQRS stands for:
Command Query Responsibility Segregation
It means:
- Commands change state
- Queries read state
- These responsibilities are separated
CQRS does not require:
- Event sourcing
- Microservices
- Message brokers
- Separate databases
Those are optional — not core.
2. The Problem CQRS Tries to Solve
Traditional CRUD services mix:
- Reads
- Writes
- Validation
- Business rules
- Mapping logic
Into a single abstraction.
// ❌ CRUD service
class OrderService
{
Order Get(int id) { }
void Create(Order order) { }
void Update(Order order) { }
}
As systems grow, this becomes:
- Hard to reason about
- Hard to optimize
- Hard to scale independently
CQRS separates intent.
3. CQRS in Clean Architecture
In Clean Architecture:
- Commands live in the Application layer
- Queries live in the Application layer
- Infrastructure implements persistence
- ASP.NET Core adapts HTTP to commands/queries
CQRS fits naturally when boundaries already exist.
4. What Commands Look Like
Commands represent intent to change state.
public record CreateOrderCommand(decimal Total);
class CreateOrderHandler
{
public Task Handle(CreateOrderCommand command) { }
}
Commands:
- Return minimal data (or nothing)
- Contain validation
- Enforce business rules
5. What Queries Look Like
Queries represent questions.
public record GetOrderQuery(int Id);
class GetOrderHandler
{
public Task<OrderDto> Handle(GetOrderQuery query) { }
}
Queries:
- Never change state
- Can use optimized read models
- Often bypass domain objects
This is intentional.
6. When CQRS HELPS
CQRS is beneficial when:
- Read and write models differ significantly
- Query performance is critical
- Business rules are complex
- Teams want clear intent separation
- Vertical Slice Architecture is in use
CQRS shines in medium-to-large systems.
7. When CQRS HURTS
CQRS hurts when:
- The system is simple CRUD
- Teams lack architectural discipline
- Everything becomes a "handler"
- Boilerplate overwhelms value
CreateUserCommandHandler
UpdateUserCommandHandler
DeleteUserCommandHandler
GetUserByIdQueryHandler
GetUsersQueryHandler
This can become noise without benefit.
8. CQRS Is Not About MediatR
Many teams equate:
CQRS = MediatR
This is incorrect.
MediatR is:
- A messaging library
- A convenience tool
CQRS is:
- A design decision
- A separation of responsibilities
You can do CQRS without MediatR.
9. CQRS + Vertical Slice Architecture
CQRS pairs extremely well with VSA.
Each slice contains:
- Command or query
- Handler
- Validator
- Endpoint
Features/
└─ Orders/
├─ Create/
├─ GetById/
This reduces coupling and improves clarity.
10. A Senior Decision Checklist
Before adopting CQRS, ask:
- Are reads and writes evolving differently?
- Is performance a real problem?
- Is domain complexity growing?
- Will this reduce cognitive load?
- Is the team ready for discipline?
If not, don’t use CQRS yet.
Final Thoughts
CQRS is not a silver bullet.
Used correctly:
- It clarifies intent
- It simplifies reasoning
- It scales gracefully
Used incorrectly:
- It adds ceremony
- It slows teams down
- It obscures simple logic
CQRS is a tool.
Senior engineers know when not to use it.
✍️ Written by Cristian Sifuentes — helping teams apply CQRS pragmatically, without dogma or unnecessary complexity.

Top comments (0)