CQRS (Command Query Responsibility Segregation)

Introdução

CQRS singifica Command Query Responsibility Segragation. O objetivo principal é separar as operações de leitura (Queries) das operações de escritas (Commands) em modelos distintos. (Em APIs, essa separação costuma se refletir em e…


This content originally appeared on DEV Community and was authored by Yuri Peixinho

Introdução

CQRS singifica Command Query Responsibility Segragation. O objetivo principal é separar as operações de leitura (Queries) das operações de escritas (Commands) em modelos distintos. (Em APIs, essa separação costuma se refletir em endpoints distintos, mas não é obrigatório)

  • Leitura (Query): consultar dados, mas não altera o estado
  • Escrita (Command): criar, atualizar e deletar

Em sistemas tradicionais (CRUD padrão), usamos a mesma classe, entidade e até estrutura de banco, tanto pra salvar quanto pra consultar dados. É simples no começo, mas com o tempo gera acoplamento e limitações.

Conflitos de objetivos

Antes de entendermos a resolução de problema proposto pelo CQRS é fundamental compreeendermos a diferença entre leitura e escrita aplicados a contextos de sistemas:

  • O modelo de escrita precisa representar regras de negócios e manter consistência. Exemplo: não permitir criar um pedido sem cliente, sem itens, etc.
  • O modelo de leitura, por sua vez, precisa ser flexível e rápido para exibir as informações. Exemplo: mostrar o nome do cliente, total do pedido e status em uma única tela

Se você utilizar os mesmos modelos, como proposto pelos sistemas tradicionais (CRUD), ele tentará servir dois propósitos ao mesmo tempo, o que deixa pesado e confuso.

Motivação e origem do CQRS

O CQRS surge da evolução do CQS (Command Query Separation). Que diz que: “Um método deve ou não alterar o estado do sistema (Command) ou retornar um valor (Query), mas nunca fazer as duas coisas”, portanto, o CQRS leva a ideia além, não apenas a nível de método, mas a nível de arquitetura.

Os principais benefícios do CQRS são:

  • Separação clara das responsabilidades Cada parte do código tem um propósito único. Isso torna o código mais legível, testável e modular
  • Otimização independente Você pode otimizar escrita para consistência e leitura para perfomance — até em bancos diferentes
  • Escalabilidade As leituras e escritas podem escalar separadamente
  • Combinação com Event Sourcing O CQRS é a base ideal para implementar Event Sourcing, onde o estado do sistema é reconstruído a partir de eventos e a leitura é derivada desses eventos

Estrutura conceitual do CQRS

Como já vimos anteriormente, em uma aplicação CQRS temos dois lados bem definidos. Lado da escrita (Command) e Lado da Leitura (Query).

Lado da Escrita (Command Side)

Responsável por alterar o estado do sistema. Aqui moram as regras de negócios, as validações e o modelo de domínio.

  • Command: uma intenção de ação (ex: CriarPedidoCommand)
  • Command Handle: executa o comando (valida, aplica a lógica e grava no banco)
  • Domain Model: representa entidades, agradados e invariantes
public class CriarPedidoCommand 
{
    public Guid ClienteId { get; set; }
    public List<Guid> Itens { get; set; }
}
public class CriarPedidoHandler {
    public void Handle(CriarPedidoCommand command)
    {
        var pedido = new Pedido(command.ClienteId, command.Itens);
        pedido.Validar();
        _repo.Salvar(pedido);
    }
}

Lado da Leitura (Query Side)

Responsável por consultar os dado sem alterar o estado. Aqui o objetivo é simplicidade, rapidez e perfomance

  • Query: representa uma requisição de dados (ex: GetPedidosPorClienteQuery)
  • Query Handler: executa a busca (com Dapper, SQL, cache, etc).
  • Read Model: DTOs otimizados para exibição (sem regas de negócio)
public class GetPedidosPorClienteQuery
{
    public Guid ClienteId { get; set; }
}
public class GetPedidosPorClienteHandler
{
    public IEnumerable<PedidoResumoDto> Handle(GetPedidosPorClienteQuery query)
    {
        return _context.Pedidos
            .Where(p => p.ClienteId == query.ClienteId)
            .Select(p => new PedidoResumoDto(p.Id, p.Total, p.Status));
    }
}

Quando aplicar CQRS

✅ Indicado para:

  • Sistemas de alta complexidade de domínio.
  • Cenários com muito mais leitura que escrita.
  • Regras de negócio densas.
  • Escalabilidade ou Event Sourcing.
  • Integração com bancos diferentes (SQL + NoSQL).

❌ Evite em:

  • CRUDs simples ou pequenos.
  • MVPs, protótipos, microprojetos.
  • Times ainda sem maturidade arquitetural.

CQRS + Mediator Pattern

Na prática, em .NET o CQRS é quase sempre utilizado com o padrão Mediator. No .NET moderno, esse padrão é muito utilizado através da biblioteca MediatR.

O papel do Mediator no CQRS

Como já sabemos, no CQRS separamos comandos (escritas) e consultas (leituras)… Mas quem orquestra essas operações? É nesse momento que entra o Mediator. Ele age como ponto central por onde os commands e queries passam. Desse modo, os controladores (controllers) não precisam conhecer diretamente os handlers.

Como funciona o fluxo?

  1. O Controller recebe uma requisição HTTP
  2. Ele cria um Command ou Query
  3. Ele envia isso para o Mediator (IMediator).
  4. O Mediator localiza o Handler correspondente e o executa
[Cliente / Front-end]
        |
        | HTTP POST /api/pedidos
        v
[PedidoController] 
        | Recebe CriarPedidoCommand
        v
[Mediator (IMediator)]
        | Localiza o handler correto (CriarPedidoHandler)
        v
[CriarPedidoHandler] 
        | Executa lógica de criação do pedido
        | (gera Guid, salva no banco, etc.)
        v
[Mediator] 
        | Retorna resultado (Guid do pedido)
        v
[PedidoController] 
        | Retorna Ok(Guid) para o cliente
        v
[Cliente / Front-end]

Estrutura típica com MediatR

Application/
 ├── Commands/
     ├── CriarPedido/
          ├── CriarPedidoCommand.cs
          └── CriarPedidoHandler.cs
 ├── Queries/
     ├── GetPedidosPorCliente/
          ├── GetPedidosPorClienteQuery.cs
          └── GetPedidosPorClienteHandler.cs
 └── Behaviors/ (opcional: logs, validação etc.)

Exemplo real com MediatR

A seguir, irei apresentar um bloco de código contendo exemplo real do mediator e logo após sua aplicação em detalhes.

// Command
public record CriarPedidoCommand(Guid ClienteId, decimal ValorTotal) : IRequest<Guid>;

// Handler
public class CriarPedidoHandler : IRequestHandler<CriarPedidoCommand, Guid>
{
    public Task<Guid> Handle(CriarPedidoCommand request, CancellationToken cancellationToken)
    {
        var pedidoId = Guid.NewGuid();
        Console.WriteLine($"Pedido criado para cliente {request.ClienteId} com valor {request.ValorTotal}");
        return Task.FromResult(pedidoId);
    }
}

// Controller
[ApiController]
[Route("api/pedidos")]
public class PedidoController : ControllerBase
{
    private readonly IMediator _mediator;

    public PedidoController(IMediator mediator) => _mediator = mediator;

    [HttpPost]
    public async Task<IActionResult> CriarPedido([FromBody] CriarPedidoCommand command)
    {
        var id = await _mediator.Send(command);
        return Ok(id);
    }
}

1. Command

public record CriarPedidoCommand(Guid ClienteId, decimal ValorTotal) : IRequest<Guid>;
  • record é uma forma imutável de criar um tipo simples no C#.
  • IRequest<Guid> vem do MediatR e indica que esse comando espera como resposta um Guid (o ID do novo pedido).
  • Nenhuma lógica aqui — é só dados.

2. Handler

public class CriarPedidoHandler : IRequestHandler<CriarPedidoCommand, Guid>
{
    public Task<Guid> Handle(CriarPedidoCommand request, CancellationToken cancellationToken)
    {
        var pedidoId = Guid.NewGuid();
        Console.WriteLine($"Pedido criado para cliente {request.ClienteId} com valor {request.ValorTotal}");
        return Task.FromResult(pedidoId);
    }
}

Esse é o módulo que realmente executa o comando.

  • Implementa IRequestHandler<TRequest, TResponse>. No caso, lida com CriarPedidoCommand e retorna um Guid.
  • O método Handle é o ponto onde a ação acontece:
    • Recebe o comando (request), que traz ClienteId e ValorTotal.
    • Cria um novo pedidoId.
    • Faz alguma ação (aqui só imprime, mas poderia salvar no banco).
    • Retorna o ID do pedido.

Ou seja: o handler é o executador da intenção.

3. Controller

[ApiController]
[Route("api/pedidos")]
public class PedidoController : ControllerBase
{
    private readonly IMediator _mediator;

    public PedidoController(IMediator mediator) => _mediator = mediator;

    [HttpPost]
    public async Task<IActionResult> CriarPedido([FromBody] CriarPedidoCommand command)
    {
        var id = await _mediator.Send(command);
        return Ok(id);
    }
}

O controller recebe a requisição HTTP (ex: via Postman ou front-end).

  1. Recebe um CriarPedidoCommand no corpo da requisição.
  2. Chama _mediator.Send(command), o Mediator encontra o handler certo e executa.
  3. Recebe o resultado (id) e devolve no Ok(id).

O controller não sabe qual handler executa o comando, só fala com o Mediator. Isso mantém o baixo acoplamento e deixa a aplicação mais organizada.


This content originally appeared on DEV Community and was authored by Yuri Peixinho


Print Share Comment Cite Upload Translate Updates
APA

Yuri Peixinho | Sciencx (2025-10-07T22:54:04+00:00) CQRS (Command Query Responsibility Segregation). Retrieved from https://www.scien.cx/2025/10/07/cqrs-command-query-responsibility-segregation-2/

MLA
" » CQRS (Command Query Responsibility Segregation)." Yuri Peixinho | Sciencx - Tuesday October 7, 2025, https://www.scien.cx/2025/10/07/cqrs-command-query-responsibility-segregation-2/
HARVARD
Yuri Peixinho | Sciencx Tuesday October 7, 2025 » CQRS (Command Query Responsibility Segregation)., viewed ,<https://www.scien.cx/2025/10/07/cqrs-command-query-responsibility-segregation-2/>
VANCOUVER
Yuri Peixinho | Sciencx - » CQRS (Command Query Responsibility Segregation). [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/10/07/cqrs-command-query-responsibility-segregation-2/
CHICAGO
" » CQRS (Command Query Responsibility Segregation)." Yuri Peixinho | Sciencx - Accessed . https://www.scien.cx/2025/10/07/cqrs-command-query-responsibility-segregation-2/
IEEE
" » CQRS (Command Query Responsibility Segregation)." Yuri Peixinho | Sciencx [Online]. Available: https://www.scien.cx/2025/10/07/cqrs-command-query-responsibility-segregation-2/. [Accessed: ]
rf:citation
» CQRS (Command Query Responsibility Segregation) | Yuri Peixinho | Sciencx | https://www.scien.cx/2025/10/07/cqrs-command-query-responsibility-segregation-2/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.