Event Sourcing e CQRS: eventos como trilha auditável
Quando o histórico importa mais que o estado atual, Event Sourcing e CQRS oferecem rastreabilidade total com modelos separados de leitura e escrita.
Por Alexandre Martins · · 3 min de leitura
Sistemas complexos frequentemente carregam uma exigência que os modelos tradicionais de persistência falham em atender: entender não apenas o que é o estado atual de uma entidade, mas como ela chegou até ali.
Quando o histórico se torna mais valioso do que o estado final, é hora de considerar arquiteturas que tratem o dado como narrativa — e não apenas como fotografia.
Por que Event Sourcing?
Event Sourcing propõe uma mudança radical: em vez de gravar o estado atual de uma entidade, gravamos todos os eventos que a levaram até ele.
Imagine um pedido entregue. Em uma arquitetura tradicional, você veria algo como:
status: ENTREGUE
No Event Sourcing, o que temos é:
- PedidoCriadoEvent
- PagamentoConfirmadoEvent
- PedidoEnviadoEvent
- EntregaConfirmadaEvent
Essa sequência de eventos constitui uma linha do tempo imutável. O estado atual é derivado da reprodução (ou “replay”) desses eventos. Isso traz não só auditabilidade total, mas também a possibilidade de simular, depurar e evoluir o sistema com mais segurança.
CQRS: leitura e escrita desacopladas
O padrão Command Query Responsibility Segregation (CQRS) complementa bem o Event Sourcing ao propor a separação entre:
- Comandos (Commands): ações que alteram o estado do sistema.
- Consultas (Queries): operações que apenas leem os dados.
Essa divisão permite otimizar cada lado com foco distinto. Comandos podem aplicar regras de negócio rigorosas. Leitura pode ser estruturada para atender interfaces, relatórios ou APIs com desempenho e formato adequados.
Ver o fluxograma em texto (Mermaid)
flowchart TD
cmd["Comando CriarPedido"] --> agg{"O Aggregate aceita\nas regras de negócio?"}
agg -- "não" --> rej["Comando rejeitado"]
agg -- "sim" --> evt["Evento PedidoCriado"]
evt --> store[("Event Store\nlog imutável")]
store --> proj["Projeção consome o evento"]
proj --> view[("Modelo de leitura\nPedidoView")]
consulta["Consulta"] --> view
store -. "replay" .-> rebuild["Reconstrói o estado\ne as projeções"]
Exemplo prático com Java e Axon Framework
Vamos a um exemplo de modelagem de pedidos. Começamos com um comando:
public class CriarPedidoCommand {
@TargetAggregateIdentifier
private String pedidoId;
private List<Item> itens;
private BigDecimal valorTotal;
}
E o evento correspondente:
public class PedidoCriadoEvent {
private String pedidoId;
private List<Item> itens;
private BigDecimal valorTotal;
}
A Aggregate é a responsável por aplicar comandos e emitir eventos:
@Aggregate
public class PedidoAggregate {
@AggregateIdentifier
private String pedidoId;
public PedidoAggregate() {}
@CommandHandler
public PedidoAggregate(CriarPedidoCommand cmd) {
if (cmd.getItens().isEmpty()) {
throw new IllegalArgumentException("Pedido sem itens");
}
AggregateLifecycle.apply(new PedidoCriadoEvent(cmd.getPedidoId(), cmd.getItens(), cmd.getValorTotal()));
}
@EventSourcingHandler
public void on(PedidoCriadoEvent event) {
this.pedidoId = event.getPedidoId();
}
}
Para leitura, criamos uma projeção desacoplada:
@Component
public class PedidoProjection {
@Autowired
private PedidoRepository repository;
@EventHandler
public void on(PedidoCriadoEvent event) {
PedidoView view = new PedidoView(event.getPedidoId(), "CRIADO", event.getItens());
repository.save(view);
}
}
Desafios reais que surgem com essa abordagem
Apesar das vantagens claras, Event Sourcing e CQRS introduzem complexidades operacionais e conceituais que exigem maturidade técnica da equipe. Os principais desafios incluem:
Versionamento de eventos
Com o tempo, eventos evoluem. Campos mudam, regras mudam. A melhor prática é tratar eventos como imutáveis, criando novas versões (ex: PedidoCriadoV2Event) e mantendo o suporte a eventos antigos.
Rebuild de projeções
Se o modelo de leitura mudar, será necessário reprocessar todos os eventos históricos. Isso demanda infraestrutura que permita replays ordenados e confiáveis — como Kafka, EventStoreDB ou Axon Server.
Consistência eventual
A leitura não é sincronizada com a escrita em tempo real. Isso significa que usuários podem ver dados “defasados” por alguns instantes. É necessário explicitar isso nas interfaces e ajustar expectativas no domínio.
Diagnóstico e debug
Com Event Sourcing, você não investiga o banco — você reproduz eventos. Para isso, ferramentas como Axon Dashboard, Kibana, observabilidade com tracing e correlação de eventos são essenciais.
Ferramentas que ajudam a implementar
- Axon Framework (Java): suporte completo a Event Sourcing, CQRS e DDD.
- EventStoreDB: banco de dados de eventos com log append-only nativo.
- Kafka + Spring Boot: estrutura escalável baseada em consumidores assíncronos.
- Temporal.io: engine de orquestração de workflows com histórico imutável (não é Event Sourcing puro, mas compartilha muitas ideias).
Boas práticas
- Trate eventos como contratos públicos: imutáveis, versionáveis e bem documentados.
- Não acople o modelo de leitura ao domínio: evite usar entidades de negócio diretamente na projeção.
- Use snapshots com moderação: úteis para desempenho, mas não substituem o log completo.
- Respeite os Bounded Contexts: nem todo o sistema precisa seguir CQRS e Event Sourcing.
- Automatize replays de eventos: tenha pipelines versionadas e seguras para reconstrução de projeções.
Conclusão
Event Sourcing e CQRS não são soluções universais — e não devem ser aplicadas por modismo. Mas quando o domínio exige rastreabilidade, múltiplas visões sobre os dados e controle total sobre o histórico de mudanças, essas abordagens brilham.
A adoção inicial é exigente. Envolve novas mentalidades, novas ferramentas e novas formas de pensar persistência. Mas os ganhos em termos de resiliência, auditabilidade, flexibilidade e desacoplamento são evidentes quando bem aplicados.
Comece pelo domínio. Se ele for complexo o suficiente, Event Sourcing e CQRS surgirão naturalmente como aliados valiosos.
Assuntos: Event Sourcing, CQRS, Arquitetura de Software, Java, Axon Framework
Continue lendo
- Por que microsserviços “odeiam” transações? — Os limites naturais de microsserviços entram em choque com transações distribuídas e pedem compensações via SAGA.
- Governança e Automação de APIs em Larga Escala com Apigee — Apigee vai além de um gateway: centraliza governança, segurança e automação para APIs estratégicas em escala corporativa.
- Sua loja virtual é lenta? O que realmente pesa no carregamento — Core Web Vitals sem mistério e as causas mais comuns de lentidão em e-commerce.
Quer aplicar isto no seu sistema? Fale com a MFactor: contato ou WhatsApp +55 (19) 99778-9831.