Implementando padrões de CQRS e Event Sourcing com .NET e RabbitMQ
Aprenda a implementar CQRS e Event Sourcing em microsserviços com .NET e RabbitMQ, separando leitura e escrita e garantindo rastreabilidade robusta das operações.

Em projetos de microsserviços, especialmente aqueles que demandam alta escalabilidade, rastreabilidade e separação clara entre comandos e consultas, implementar padrões como CQRS (Command Query Responsibility Segregation) e Event Sourcing pode melhorar significativamente a qualidade e a manutenibilidade do sistema.
O problema que quero resolver
Imagine um sistema financeiro que precisa registrar todas as operações de débito e crédito com total rastreabilidade e, ao mesmo tempo, responder rapidamente a consultas do histórico de movimentos. Uma arquitetura tradicional pode ficar confusa, misturando lógica de leitura e escrita, dificultando auditorias e escalabilidade.
Conceito funcionando antes de aprofundar
Vou começar com um exemplo simples, mostrando as duas partes do CQRS separadas:
- Write model (comandos): para alterar o estado do sistema.
- Read model (consultas): para obter dados otimizados para leitura.
Suponha que temos um comando para registrar uma transação e uma consulta para listar as transações:
// Comando (write)
public class RegisterTransactionCommand
{
public Guid AccountId { get; set; }
public decimal Amount { get; set; }
}
// Consulta (read)
public class TransactionDto
{
public Guid Id { get; set; }
public Guid AccountId { get; set; }
public decimal Amount { get; set; }
public DateTime CreatedAt { get; set; }
}
Separamos a lógica para receber o comando, validar, persistir e publicar eventos, e a lógica para manejar consultas rapidamente via bancos otimizados para leitura.
Integrando CQRS com Event Sourcing e RabbitMQ
Event Sourcing significa que o estado do sistema é derivado de uma série de eventos imutáveis que guardam todas as alterações. RabbitMQ entra para garantir que os eventos sejam difundidos com segurança e que todos os microsserviços possam reagir.
Arquitetura explicada
flowchart TD
subgraph Command_side
cmd["Recebe comando (RegisterTransactionCommand)"] --> val["Validação e regras de negócio"]
val --> ev["Persistência do evento (TransactionRegisteredEvent)"]
ev --> rmq["Publica evento no RabbitMQ"]
end
subgraph Read_side
rmq --> handle["Handler consume evento TransactionRegisteredEvent"]
handle --> update["Atualiza modelos de leitura (Read model)"]
end
consulta["Consulta (Queries via REST ou GraphQL)"] --> readmodel["Modelos de leitura otimizados"]
Código prático para publicar e consumir eventos
// Evento que representa a transação
public class TransactionRegisteredEvent
{
public Guid TransactionId { get; set; }
public Guid AccountId { get; set; }
public decimal Amount { get; set; }
public DateTime OccurredAt { get; set; }
}
// Publicador de evento usando RabbitMQ simplificado
public class EventPublisher
{
private readonly IModel _channel;
public EventPublisher(IModel channel) {
_channel = channel;
}
public void PublishTransactionRegistered(TransactionRegisteredEvent evt) {
var body = JsonSerializer.SerializeToUtf8Bytes(evt);
_channel.BasicPublish(exchange: "transactions-exchange", routingKey: "transaction.registered", body: body);
}
}
// Consumidor do evento no Read Side
public class TransactionEventHandler
{
private readonly IReadModelRepository _repository;
public TransactionEventHandler(IReadModelRepository repository) {
_repository = repository;
}
public void Handle(TransactionRegisteredEvent evt) {
var dto = new TransactionDto {
Id = evt.TransactionId,
AccountId = evt.AccountId,
Amount = evt.Amount,
CreatedAt = evt.OccurredAt
};
_repository.Save(dto);
}
}
Armadilhas comuns
- Ignorar a eventual consistência: os modelos de leitura podem estar defasados em relação aos dados de escrita por alguns instantes; isso deve ser esperado e projetado.
- Acoplamento forte entre comandos e consultas: mantenha os modelos independentes para facilitar manutenção e evolução.
- Persistir estado e eventos em armazenamentos diferentes sem cuidado: garanta a atomicidade ou mecanismos de compensação para evitar inconsistências.
Quando usar CQRS e Event Sourcing
Prefiro aplicar essas técnicas em sistemas com requisitos claros de rastreabilidade, auditoria, ou onde a escalabilidade das consultas difere muito da escalabilidade das escritas. Em sistemas simples ou com baixa complexidade, a sobrecarga pode não valer a pena.
Próximos passos
- Estude o CQRS pattern no Microsoft Docs
- Explore o Event Sourcing como padrão
- Experimente integrar com RabbitMQ usando a biblioteca oficial
- Avalie frameworks como MediatR para organização dos comandos e eventos
Adotar CQRS e Event Sourcing com RabbitMQ no .NET pode parecer complexo inicialmente, mas traz ganhos estruturais importantes para sistemas distribuídos e escaláveis, facilitando manutenção e evolução contínua do seu backend.