Marcio Cunha

Modelagem de Domínio com Event Sourcing e CQRS em Bancos de Dados Relacionais

Descubra como estruturar sistemas resilientes combinando Event Sourcing e CQRS em bancos relacionais tradicionais, garantindo alta disponibilidade e auditoria imutável.

Marcio Cunha4 min
Também disponível em:EnglishEspañol
Resumo
  • O registro de eventos imutáveis substitui atualizações destrutivas de estado e garante auditoria nativa em sistemas corporativos críticos.
  • A separação de modelos de escrita e leitura elimina gargalos de concorrência em bancos relacionais tradicionais.
  • O uso cuidadoso de índices parciais e tabelas otimizadas para anexação resolve problemas de desempenho em volumes massivos de dados.
  • A eventual consistência entre escritas e leituras exige estratégias claras para mitigar o atraso na interface do usuário.
  • A modelagem baseada em fluxo temporal simplifica a reconstrução do estado de negócios sem perda de histórico.

Fundamentos da Modelagem Baseada em Eventos

Na engenharia de software tradicional, costumamos salvar apenas o estado atual de um registro no banco de dados. Quando um cliente altera seu endereço, sobrescrevemos o dado antigo, perdendo o rastro de onde ele morava antes. Na prática, isso significa que perdemos a história de como chegamos até aqui. O Event Sourcing, ou modelagem baseada em eventos, propõe uma mudança radical nessa mentalidade: em vez de salvar o estado final, salvamos cada acontecimento importante como um fato imutável, como se fosse um diário de bordo inalterável.

Para um leitor sem familiaridade técnica, imagine uma conta bancária. Em vez de atualizar o saldo de mil para quinhentos reais após um saque, registramos o evento exato de que um saque de quinhentos reais aconteceu às dez da manhã. O saldo atual não é guardado diretamente, mas calculado sempre que necessário, somando e subtraindo os eventos do passado. Essa abordagem garante uma auditoria perfeita, pois nenhum dado é apagado ou sobrescrito, permitindo voltar no tempo e entender exatamente o que aconteceu em qualquer momento do ciclo de vida do sistema.

A Arquitetura de Separar Escrita e Leitura

Quando adotamos o armazenamento baseado em eventos, consultar dados de forma ágil torna-se um desafio matemático. Ler milhares de eventos toda vez que um usuário abre o perfil exigiria um esforço computacional desnecessário. É aqui que entra o CQRS, uma sigla em inglês para separação de responsabilidades entre consulta e comando. Na prática, criamos duas estradas separadas: uma pista exclusiva e hiper-otimizada para receber novas ações de escrita, e outra pista desenhada sob medida para responder rapidamente às consultas dos usuários.

Em bancos de dados relacionais de alta disponibilidade, essa separação evita que consultas complexas travem as operações críticas de gravação. Enquanto a tabela de eventos armazena fatos brutos de forma sequencial e extremamente rápida, tabelas separadas ou visões materializadas são atualizadas em segundo plano para servir as telas do sistema. Essa divisão de tarefas permite escalar cada parte da aplicação conforme sua necessidade específica, garantindo que o sistema continue respondendo rapidamente mesmo sob forte movimentação de acessos simultâneos.

Implementação Prática em Bancos Relacionais

Muitos desenvolvedores acreditam que o Event Sourcing exige o uso de bancos de dados exóticos ou orientados a documentos. Na prática, bancos relacionais robustos lidam perfeitamente com essa carga quando estruturados com tabelas simples de append-only, ou seja, tabelas onde registros nunca sofrem alterações ou exclusões, apenas inserções. Cada evento é armazenado tipicamente como um documento JSON contendo o identificador do agregado, o tipo do evento, a versão e os dados específicos da mudança ocorrida no sistema.

Para garantir a integridade transacional sem perder performance, utilizamos índices em colunas estratégicas e chaves de idempotência que evitam a duplicação de eventos. Abaixo está um exemplo prático de estrutura relacional em SQL para persistir e consultar eventos de forma segura e eficiente:

CREATE TABLE event_store (
    event_id UUID PRIMARY KEY,
    aggregate_id UUID NOT NULL,
    aggregate_type VARCHAR(255) NOT NULL,
    event_type VARCHAR(255) NOT NULL,
    version INT NOT NULL,
    payload JSONB NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT unique_aggregate_version UNIQUE (aggregate_id, version)
);

CREATE INDEX idx_event_store_aggregate ON event_store (aggregate_id, version);

Com essa estrutura simples, garantimos que nenhum evento seja gravado com uma versão duplicada para o mesmo agregado, mantendo a ordem cronológica estrita necessária para a reconstrução correta do domínio de negócios. O banco relacional assume o papel de guardião da consistência sem abandonar a flexibilidade exigida por arquiteturas modernas.

Gerenciamento de Consistência e Alta Disponibilidade

Em sistemas distribuídos, a ilusão de consistência imediata em todos os lugares cede lugar à realidade da consistência eventual. Quando um evento é gravado na tabela principal, as projeções de leitura levam milissegundos ou frações de segundo para refletir a mudança nas telas dos usuários. Na prática, isso significa que um cliente pode realizar uma compra e, por um curtíssimo intervalo de tempo, não ver o pedido refletido no histórico de compras recente se consultar um nó de leitura desatualizado.

Para mitigar essa sensação de atraso na interface, aplicamos padrões onde o cliente recebe uma confirmação otimista imediata enquanto a aplicação processa o estado em segundo plano. Em bancos relacionais de alta disponibilidade configurados com replicação multi-master ou leitura em réplicas, o segredo reside em direcionar leituras críticas logo após uma escrita para o mesmo nó ou conexão que originou o comando, garantindo uma experiência de uso fluida e sem surpresas desagradáveis para quem está operando o sistema.

Considerações Finais sobre Escalabilidade e Manutenibilidade

Adotar Event Sourcing e CQRS em bancos relacionais de alta disponibilidade não é uma decisão a ser tomada levianamente. A complexidade de desenvolvimento aumenta, pois a lógica de negócios precisa ser traduzida em eventos claros e versionáveis ao longo do tempo. No entanto, os ganhos em termos de rastreabilidade, resiliência operacional e clareza na modelagem superam amplamente o esforço inicial de implementação em ambientes corporativos que exigem histórico impecável.

O segredo para o sucesso reside em começar pequeno, identificando domínios de negócio que realmente se beneficiam de auditoria rigorosa e isolamento de carga. Ao tratar o banco relacional não apenas como um repositório estático de planilhas, mas como um motor confiável de fluxo temporal de eventos, construímos aplicações capazes de absorver crescimento explosivo sem perder a integridade dos dados.