Marcio Cunha

Event Sourcing com Snapshots: Como Restaurar o Estado de Agregações sem Reprocessar Eventos

Descubra como o uso de snapshots resolve o problema de desempenho na leitura de sistemas baseados em Event Sourcing, evitando a necessidade de recalcular o passado a cada requisição.

Marcio Cunha10 min
Também disponível em:EnglishEspañol
Resumo
  • Sistemas baseados em Event Sourcing registram cada mudança de estado como um evento isolado e imutável ao longo do tempo.
  • O reprocessamento linear de milhares de eventos degrada a performance de leitura de agregações antigas e muito ativas.
  • Snapshots funcionam como pontos de salvamento instantâneo que congelam o estado atual de um objeto de domínio.
  • A combinação de eventos incrementais com capturas periódicas de estado equilibra a escrita rápida e a leitura otimizada.
  • Estratégias consistentes de invalidação e versionamento evitam corrupção de dados durante alterações no modelo de domínio.

O Desafio do Crescimento Contínuo em Sistemas de Event Sourcing

Event Sourcing é um padrão de arquitetura de software em que, em vez de guardar apenas a foto atual de um dado, salvamos cada passo que levou até ali, como um extrato bancário cheio de transações. Na prática, isso significa que para saber o saldo de uma conta ou o status de um pedido, o sistema precisa ler todos os eventos gerados desde o nascimento daquele registro e somá-los um a um. No começo da vida da aplicação, quando existem apenas algumas dezenas de eventos, essa matemática acontece em milissegundos e ninguém percebe qualquer lentidão.

Porém, à medida que o tempo passa e o negócio cresce, uma única entidade de domínio pode acumular dezenas de milhares ou até milhões de eventos históricos. Quando um usuário tenta abrir o histórico ou realizar uma nova transação nessa entidade, o servidor sofre um pico de esforço computacional apenas para reconstruir o passado antes de tomar qualquer decisão. Esse gargalo de leitura compromete a experiência do usuário e desperdiça poder de processamento em tarefas repetitivas, exigindo uma solução inteligente para aliviar a carga sobre a base de dados.

O Conceito de Snapshot e a Redução de Custo Computacional

Um snapshot, que na tradução literal significa uma fotografia instantânea, resolve esse problema salvando o estado consolidado de um objeto de domínio em um determinado momento da sua linha do tempo. Em termos simples, em vez de ler o extrato bancário inteiro desde o ano de 2015, o sistema guarda o saldo exato calculado no fechamento do ano passado. Quando uma nova consulta é feita, a aplicação carrega essa fotografia recente e processa apenas os poucos eventos que aconteceram após aquela data, poupando tempo de CPU e memória RAM.

Essa abordagem transforma radicalmente a curva de complexidade de leitura, alterando o custo de processamento de proporcional ao número total de eventos para proporcional apenas aos eventos gerados após o último ponto de salvamento. Na prática, um sistema que demoraria segundos para recalcular uma entidade pesada passa a responder quase instantaneamente. O ganho de performance é tão expressivo que viabiliza o uso de arquiteturas orientadas a eventos em cenários de altíssima escala onde a latência é inaceitável.

Estratégias de Criação e Frequência de Captura de Estado

Criar um snapshot para absolutamente todo evento gerado elimina a própria vantagem de usar eventos, pois transformaria o modelo em um registro tradicional de banco de dados relacional. Por isso, os engenheiros precisam definir uma política clara para determinar quando uma fotografia deve ser tirada, sendo a métrica mais comum baseada em contagem de eventos. Por exemplo, a regra de negócio pode estipular que um novo snapshot será gravado a cada cem eventos novos adicionados àquela agregação específica.

Outras estratégias incluem a captura baseada em tempo, como gerar um snapshot ao final de cada dia útil, ou gatilhos acionados por eventos críticos que alteram drasticamente a estrutura dos dados. A escolha do intervalo ideal exige monitoramento constante e análise de trade-offs entre o espaço em disco consumido para armazenar essas fotos e a velocidade desejada na recuperação do estado. Ajustar esse ponteiro garante que o sistema mantenha o equilíbrio perfeito entre o consumo de armazenamento e a agilidade nas consultas.

Implementação Prática e Estrutura de Dados

Para colocar essa arquitetura de pé no código, a aplicação precisa consultar se existe um snapshot válido antes de disparar a leitura de todo o fluxo de eventos. O trecho de código abaixo ilustra de forma simplificada como uma agregação reconstrói seu estado combinando a fotografia armazenada com os eventos subsequentes:

public class OrderAggregate {
    public string Id { get; private set; }
    public string Status { get; private set; }
    public decimal TotalAmount { get; private set; }
    private int _version;

    public static OrderAggregate Load(string orderId, ISnapshotStore snapshotStore, IEventStore eventStore) {
        var order = new OrderAggregate();
        var snapshot = snapshotStore.GetLatestSnapshot(orderId);
        
        int fromVersion = 0;
        if (snapshot != null) {
            order.RestoreFromSnapshot(snapshot);
            fromVersion = snapshot.Version;
        }

        var events = eventStore.GetEventsAfterVersion(orderId, fromVersion);
        order.ApplyEvents(events);
        
        return order;
    }

    private void RestoreFromSnapshot(OrderSnapshot snapshot) {
        Id = snapshot.Id;
        Status = snapshot.Status;
        TotalAmount = snapshot.TotalAmount;
        _version = snapshot.Version;
    }

    private void ApplyEvents(IEnumerable<IDomainEvent> events) {
        foreach (var ev in events) {
            // Aplica a mudança e incrementa a versão
            _version++;
        }
    }
}

Neste exemplo, a classe verifica se há um registro prévio na tabela de snapshots; caso exista, o objeto recupera suas propriedades base sem esforço. Em seguida, busca apenas os eventos que ocorreram depois daquela versão específica e aplica as atualizações incrementais necessárias. Essa lógica garante que o banco de dados principal não seja sobrecarregado com leituras massivas e desnecessárias a cada interação do usuário.

Armadilhas Comuns, Versionamento e Limpeza de Dados Antigos

O uso de snapshots introduz novos desafios operacionais que precisam ser gerenciados com cuidado para evitar corrupção silenciosa de dados. O problema mais frequente ocorre quando o modelo de domínio muda, adicionando novos campos ou alterando regras de negócio, tornando as fotografias antigas incompatíveis com o código atual. Para resolver isso, cada snapshot deve carregar um número de versão explícito, permitindo que a aplicação saiba exatamente como lidar com estruturas legadas ou execute migrações em tempo de execução.

Outro ponto crítico diz respeito à retenção e ao expurgo de snapshots e eventos obsoletos que já cumpriram seu papel na auditoria do sistema. Manter todas as fotografias antigas para sempre gera um consumo desnecessário de espaço em disco, enquanto apagar eventos precipitadamente pode violar exigências legais de compliance e auditoria financeira. Estabelecer políticas automatizadas de retenção e compactação garante que o histórico permaneça saudável, seguro e enxuto ao longo dos anos de operação.

Conclusão e Considerações Finais sobre Escalabilidade

A adoção de Event Sourcing combinada com snapshots representa um divisor de águas para aplicações que exigem rastreabilidade absoluta e alta capacidade de escala. Embora adicione complexidade operacional no gerenciamento de versões e na arquitetura de persistência, o benefício de eliminar o reprocessamento linear de dados compensa largamente o esforço inicial. Engenheiros e arquitetos devem avaliar o volume de eventos esperados e a criticidade da latência antes de implementar o padrão, garantindo que o investimento técnico traga retornos sólidos para o negócio.

Em última análise, dominar essa técnica permite que sistemas corporativos processem milhões de transações sem perder a agilidade nas consultas ou comprometer a consistência do domínio. O segredo do sucesso reside em planejar o ciclo de vida dos dados desde o primeiro dia, unindo a precisão histórica dos eventos à eficiência imediata dos snapshots.