Blog

Event sourcing: quando vale e quando é tiro no pé.

Event sourcing é uma das ideias mais elegantes de arquitetura de software. Em vez de guardar o estado atual, você guarda os eventos que produziram esse estado, e o estado passa a ser uma função dos eventos.

Também é uma das que mais destroem produtividade de time quando adotada pelo motivo errado.

Já vi as duas coisas. A diferença raramente está na execução técnica.

O que ele é de verdade

Não é ter uma tabela de eventos. Isso é log append-only, que é bem mais barato e resolve a maior parte das necessidades.

Event sourcing é os eventos serem a única fonte de verdade. Não existe tabela orders com o estado do pedido. Existe uma sequência de eventos, e o pedido é o resultado de aplicá-los em ordem.

class Order {
  static from(events: OrderEvent[]): Order {
    return events.reduce((state, e) => state.apply(e), new Order());
  }

  apply(event: OrderEvent): Order {
    switch (event.type) {
      case "created":  return this.with({ status: "pending", items: event.items });
      case "paid":     return this.with({ status: "paid", paidAt: event.at });
      case "cancelled":return this.with({ status: "cancelled" });
    }
  }
}

Essa distinção é a coisa mais importante do texto. A maioria dos times que diz fazer event sourcing está fazendo log de eventos ao lado do estado, o que é uma escolha sensata e não tem nenhum dos custos que vêm a seguir.

Os custos que aparecem no mês três

Consulta. Você não consegue mais fazer select * from orders where status = 'pending'. Precisa de projeções, que são tabelas derivadas dos eventos, mantidas por processos que consomem o fluxo. Cada consulta nova pode exigir uma projeção nova, e cada projeção precisa ser reconstruível e monitorada.

Consistência eventual. A projeção fica atrás do fluxo. O usuário salva e não vê a mudança na listagem, porque a projeção ainda não processou. Isso é resolvível, com leitura do próprio fluxo depois da escrita ou com atualização otimista na interface, e é trabalho que não existiria.

Versionamento de evento. Evento é imutável e eterno. O evento pedido_criado de 2024 tem uma forma, o de 2026 tem outra, e o código precisa saber ler as duas pra sempre. Isso vira um problema de compatibilidade permanente que se acumula.

Curva de aprendizado. Todo desenvolvedor novo precisa aprender o padrão antes de fazer qualquer coisa. Em time com rotatividade, esse custo é recorrente.

Dado pessoal. Evento imutável e direito à exclusão se contradizem. A saída usual é criptografar o dado pessoal com chave por titular e jogar a chave fora, o que funciona e é mais uma peça a construir.

Os quatro casos onde vale

O histórico é o produto. Auditoria regulatória, prontuário, rastreabilidade de cadeia. Se o cliente compra a capacidade de saber o que aconteceu, event sourcing é o desenho natural em vez de um extra.

Reconstruir o passado tem valor de negócio. Poder responder “qual era o estado em 3 de março?” ou reprocessar meses de dados com uma regra corrigida. Sistema de precificação, de risco e de comissão se beneficiam muito disso.

Concorrência alta no mesmo agregado. A versão dentro do fluxo dá controle otimista natural, sem lock. Escrevi sobre a mecânica em log append-only.

Vários consumidores com visões diferentes do mesmo fato. Quando cinco áreas precisam reagir ao mesmo evento de formas distintas, o fluxo é uma boa espinha dorsal.

Os motivos ruins, que são os mais comuns

“Vamos precisar de auditoria.” Log append-only ao lado da tabela de estado resolve, com um décimo do custo.

“Escala melhor.” Não necessariamente. Escala diferente, com mais peças pra operar.

“É mais moderno.” Esse é o motivo real em boa parte dos casos, e ninguém diz em voz alta.

“Já usamos Kafka.” Ter um broker não é fazer event sourcing. São coisas independentes.

O caminho do meio, que é o que eu recomendo

Guarde os eventos. Guarde também o estado.

-- estado, do jeito que sempre foi. consulta normal, join normal.
create table orders (...);

-- eventos, gravados na mesma transação
create table event (
  id bigserial primary key,
  stream_type text not null,
  stream_id uuid not null,
  version int not null,
  type text not null,
  payload jsonb not null,
  occurred_at timestamptz not null,
  unique (stream_type, stream_id, version)
);

Você fica com histórico completo, capacidade de auditoria, base pra integração via outbox e a possibilidade de migrar pra event sourcing de verdade depois, porque os eventos já existem.

E mantém consulta simples, consistência forte e um modelo que qualquer pessoa entende no primeiro dia.

É noventa por cento do benefício por dez por cento do custo. Em quase todo produto, essa é a conta certa.

O sinal de que você precisa mesmo

Se o seu time está perguntando “será que a gente devia usar event sourcing?”, a resposta provavelmente é não. Quando é sim, isso costuma aparecer como uma necessidade concreta e recorrente: alguém pedindo reprocessamento do passado toda semana, ou uma exigência regulatória explícita.

Padrão adotado por necessidade tem chance. Padrão adotado por elegância cobra depois, e cobra do time inteiro.

← Voltar pro blog