CQRS e Event Sourcing em C#: Arquitetura para sistemas complexos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Quando o CRUD comum deixa de funcionar: Introdução ao CQRS e Event Sourcing

\n\n

Imagine que você está escrevendo um sistema para um banco. A cada segundo, centenas de usuários verificam saldos, transferem dinheiro, abrem investimentos. Sua aplicação em C# funciona com o Entity Framework padrão e operações CRUD típicas. E de repente — o banco cai. Por quê? Porque um relatório pesado bloqueou a tabela enquanto uma transação estava sendo gravada. Familiar?

\n\n

Desenvolvedores de sistemas complexos em C# mais cedo ou mais tarde encontram o limite da arquitetura clássica. Dois padrões poderosos vêm em socorro: CQRS (Command Query Responsibility Segregation) e Event Sourcing. Vamos analisá-los na prática — com exemplos reais, e não teoria seca.

\n\n

O que é CQRS e por que não é "apenas separação"?

\n\n

CQRS não é sobre colocar métodos em pastas diferentes. É uma filosofia: comandos (alteram o estado) e consultas (leem o estado) devem viver separadamente. No CRUD clássico, você tem um único modelo de dados tanto para leitura quanto para escrita. No CQRS — pelo menos dois.

\n\n

O problema da abordagem clássica

\n

Suponha que você tenha um UserRepository com o método GetUserById. Quando você lê um usuário para exibir na tela, precisa de 3 campos. Quando você atualiza — 10 campos e validações. Como resultado, um único modelo se transforma em um monstro com campos null e mapeadores confusos.

\n\n

No CQRS, você separa as responsabilidades:

\n
    \n
  • Command — executa uma ação (ex.: CreateOrderCommand)
  • \n
  • Query — retorna dados (ex.: GetOrderQuery)
  • \n
  • Read Model — DTO simples, otimizado para uma tela específica
  • \n
  • Write Model — modelo de domínio rico com lógica de negócios
  • \n
\n\n

Escrevendo CQRS em C# com MediatR

\n

A maneira mais popular de implementar CQRS em C# é usar a biblioteca MediatR. Ela implementa o padrão Mediator e se encaixa perfeitamente no conceito de comandos e consultas.

\n\n

Passo 1: Definindo o comando

\n
public record CreateOrderCommand(\n    Guid CustomerId,\n    List<OrderItemDto> Items,\n    string ShippingAddress\n) : IRequest<Guid>;  // Retorna o ID do pedido criado
\n\n

Passo 2: Handler do comando

\n
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, Guid>\n{\n    private readonly IOrderRepository _orderRepository;\n    private readonly IUnitOfWork _unitOfWork;\n\n    public CreateOrderCommandHandler(IOrderRepository orderRepository, IUnitOfWork unitOfWork)\n    {\n        _orderRepository = orderRepository;\n        _unitOfWork = unitOfWork;\n    }\n\n    public async Task<Guid> Handle(CreateOrderCommand request, CancellationToken cancellationToken)\n    {\n        // Lógica de negócios: verificação, validação, criação\n        var order = new Order(request.CustomerId, request.ShippingAddress);\n        \n        foreach (var item in request.Items)\n        {\n            order.AddItem(item.ProductId, item.Quantity, item.Price);\n        }\n\n        await _orderRepository.AddAsync(order);\n        await _unitOfWork.SaveChangesAsync(cancellationToken);\n        \n        return order.Id;\n    }\n}
\n\n

Passo 3: Definindo a consulta

\n
public record GetOrderSummaryQuery(Guid OrderId) : IRequest<OrderSummaryDto>;
\n\n

Passo 4: Handler da consulta (lê de um banco separado!)

\n
public class GetOrderSummaryQueryHandler : IRequestHandler<GetOrderSummaryQuery, OrderSummaryDto>\n{\n    private readonly IReadDbContext _readDbContext; // Contexto separado para leitura\n\n    public GetOrderSummaryQueryHandler(IReadDbContext read                

Blogs

Book Recommendations