C# Architecture Mastery — CQRS in ASP.NET Core (When It Helps, When It Hurts) (Part 9)

Published: (December 23, 2025 at 04:03 PM EST)
2 min read
Source: Dev.to

Source: Dev.to

Introduction

CQRS is one of the most misunderstood patterns in modern .NET.
Some teams adopt it too early, while others avoid it entirely out of fear. Both mistakes stem from treating CQRS 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.

What CQRS Stands For

Command Query Responsibility Segregation

  • Commands change state
  • Queries read state

These responsibilities are separated.

What CQRS Does Not Require

  • Event sourcing
  • Microservices
  • Message brokers
  • Separate databases

These are optional—not core to CQRS.

Traditional CRUD Services

CRUD services typically 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 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.

Commands

Commands represent intent to change state.

public record CreateOrderCommand(decimal Total);

class CreateOrderHandler
{
    public Task Handle(CreateOrderCommand command) { }
}

Characteristics

  • Return minimal data (or nothing)
  • Contain validation
  • Enforce business rules

Queries

Queries represent questions.

public record GetOrderQuery(int Id);

class GetOrderHandler
{
    public Task Handle(GetOrderQuery query) { }
}

Characteristics

  • Never change state
  • Can use optimized read models
  • Often bypass domain objects (intentional)

When CQRS Is Beneficial

  • Read and write models differ significantly
  • Query performance is critical
  • Business rules are complex
  • Teams want clear intent separation
  • Vertical Slice Architecture (VSA) is in use

CQRS shines in medium‑to‑large systems.

When CQRS Hurts

  • The system is a simple CRUD app
  • Teams lack architectural discipline
  • Everything becomes a “handler” → boilerplate overwhelms value

Example of proliferating handlers:

CreateUserCommandHandler
UpdateUserCommandHandler
DeleteUserCommandHandler
GetUserByIdQueryHandler
GetUsersQueryHandler

This can become noise without benefit.

CQRS vs. MediatR

Many teams equate CQRS = MediatR, which is incorrect.

  • MediatR is a messaging library and convenience tool.
  • CQRS is a design decision and a separation of responsibilities.

You can implement CQRS without MediatR.

CQRS Paired with Vertical Slice Architecture

Each slice contains:

  • Command or query
  • Handler
  • Validator
  • Endpoint
Features/
 └─ Orders/
     ├─ Create/
     └─ GetById/

This reduces coupling and improves clarity.

Adoption Checklist

Before adopting CQRS, ask:

  1. Are reads and writes evolving differently?
  2. Is performance a real problem?
  3. Is domain complexity growing?
  4. Will this reduce cognitive load?
  5. Is the team ready for the required discipline?

If the answer is “no” to any of these, postpone CQRS.

Summary

  • CQRS is not a silver bullet.
  • Used correctly: clarifies intent, simplifies reasoning, scales gracefully.
  • Used incorrectly: adds ceremony, slows teams down, obscures simple logic.

Senior engineers know when not to use it.

Back to Blog

Related posts

Read more »