O que é event sourcing?
Um banco que não sabe se explicar
Sua conta mostra R$2.847,63. Você esperava R$3.347,63. Você liga para o banco e pergunta para onde foram os R$500. O banco responde: "Não sabemos. Só guardamos o seu saldo atual."
Seria assustador, e mesmo assim é assim que a maior parte dos softwares funciona.
Uma aplicação Rails tradicional, de CRUD, guarda o estado atual de cada registro. Quando alguém atualiza o perfil, você sobrescreve os dados antigos. Quando um pedido passa de pending para shipped, você roda order.update!(status: :shipped), e o estado anterior, pending, some. Se alguém perguntar "quando esse pedido foi enviado?" ou "quem mudou isso?", você fica sem resposta, a não ser que tenha montado um log de auditoria à parte.
O event sourcing inverte essa lógica. Em vez de guardar o estado atual, você guarda cada mudança que já aconteceu, como uma sequência de eventos imutáveis. O estado atual nem é guardado: ele é derivado reproduzindo esses eventos.
Baseado em estado vs baseado em eventos: dois modelos mentais
Pense num carrinho de compras simples numa app Rails tradicional:
# Abordagem CRUD — armazena estado atual
cart = Cart.find(42)
cart.items << Item.new(product_id: 7, quantity: 2)
cart.save!
# Depois...
cart.items.find_by(product_id: 7).update!(quantity: 3)
# Mais tarde...
cart.items.find_by(product_id: 7).destroy!
Depois dessas operações, o banco mostra um carrinho vazio. O que aconteceu no caminho (um item adicionado, a quantidade alterada, o item removido) se perdeu.
Agora o mesmo cenário com event sourcing:
# Abordagem event-sourced — armazena toda mudança
event_store.append(ItemAddedToCart.new(
cart_id: 42, product_id: 7, quantity: 2
))
event_store.append(CartItemQuantityChanged.new(
cart_id: 42, product_id: 7, new_quantity: 3
))
event_store.append(ItemRemovedFromCart.new(
cart_id: 42, product_id: 7
))
O carrinho continua vazio, mas agora você sabe por quê. Dá para responder "O que o cliente quase comprou?", "Quando ele mudou de ideia?" e "Com que frequência os clientes removem itens depois de mudar a quantidade?". O CRUD não responde a essas perguntas. O event sourcing responde com facilidade.
flowchart LR
subgraph CRUD["CRUD (Baseado em Estado)"]
direction TB
C1["tabela cart_items"]
C2["Estado Atual: (vazio)"]
C3["Auditoria: O que aconteceu? Nenhuma ideia."]
C1 --> C2 --> C3
end
subgraph ES["Event Sourcing"]
direction TB
E1["stream de eventos"]
E2["1. ItemAdded"]
E3["2. QtyChanged"]
E4["3. ItemRemoved"]
E5["Auditoria: Histórico completo preservado."]
E1 --> E2 --> E3 --> E4 --> E5
end
A analogia do livro-razão
O event sourcing não é novidade. Os contadores trabalham assim há mais de 700 anos.
Na contabilidade de partidas dobradas, você nunca apaga um lançamento. Se errou, faz um lançamento de correção. O saldo atual é sempre a soma de todos os lançamentos, então dá para auditar cada transação e reconstruir o saldo de qualquer momento.
O event sourcing faz o mesmo com o software:
# Livro-razão contábil (event-sourced por natureza)
class LedgerEntry
# date, description, debit, credit
end
balance = entries.sum { |e| e.credit - e.debit }
# Event store de software (mesmo princípio)
class AccountEvent
# timestamp, event_type, data
end
current_state = events.reduce(initial_state) do |state, event|
apply(state, event)
end
Uma frase resume a ideia: "o log é a verdade". Os eventos são o registro oficial. Todo o resto (o saldo atual, o conteúdo do carrinho, o status do pedido) é uma visão derivada que pode ser reconstruída a qualquer hora.
Onde o Rails entra nisso
O Rails é construído em torno do ActiveRecord, que é baseado em estado por natureza: você chama save! e a linha é sobrescrita. Event sourcing não significa abandonar o Rails, porém. Significa modelar certas partes do domínio de outro jeito.
Este é um padrão mínimo de event sourcing numa app Rails:
# app/models/event.rb
class Event < ApplicationRecord
# tabela: events
# colunas: id, stream_name, event_type,
# data (jsonb), metadata (jsonb),
# created_at
validates :stream_name, :event_type, presence: true
scope :for_stream, ->(name) { where(stream_name: name) }
scope :ordered, -> { order(:id) }
end
# Publicando um evento
Event.create!(
stream_name: "Order-#{order_id}",
event_type: "OrderPlaced",
data: { customer_id: 42, total: 99.99 },
metadata: { actor_id: current_user.id }
)
# Reconstruindo o estado atual
events = Event.for_stream("Order-#{order_id}").ordered
order_state = events.reduce({}) do |state, event|
case event.event_type
when "OrderPlaced"
state.merge(status: :placed, total: event.data["total"])
when "OrderShipped"
state.merge(status: :shipped, shipped_at: event.created_at)
when "OrderCancelled"
state.merge(status: :cancelled)
else
state
end
end
Isso não é event sourcing pronto para produção; gems como a rails_event_store fornecem a infraestrutura de verdade. Mas mostra a ideia central: acrescentar eventos e derivar o estado.
Quando o event sourcing é essencial
O event sourcing compensa a complexidade quando o seu domínio tem estas características.
A auditoria é inegociável
Sistemas financeiros, prontuários médicos, documentos jurídicos. O regulador pergunta "mostre cada mudança que levou a este estado". CRUD com log de auditoria é frágil, porque o log pode se afastar da realidade. Com event sourcing, os eventos são a realidade.
Perguntas sobre o passado importam
"Em que plano esse cliente estava no dia 15 de março?" No CRUD, você torce para alguém ter criado uma tabela versions. Com event sourcing, você reproduz os eventos até aquela data.
O negócio depende do comportamento ao longo do tempo
Análise de carrinhos abandonados, mapa da jornada do usuário, detecção de fraude. Tudo isso precisa saber o que aconteceu, e não só o que é.
Várias pessoas editam a mesma coisa
Google Docs, Figma, jogos multiplayer: várias pessoas mudam a mesma entidade ao mesmo tempo. Eventos (operações) podem ser combinados e reproduzidos; estado sobrescrito, não.
Quando o event sourcing é exagero
Tem uma coisa que os defensores do event sourcing quase nunca dizem em voz alta: a maioria das aplicações não deveria usá-lo.
| Cenário | Event sourcing? | Por quê |
|---|---|---|
| Blog com posts e comentários | Não | CRUD é mais simples e basta. Não há necessidade de auditoria. |
| Gestão de perfis de usuário | Não | Sobrescrever um nome não tem problema. Ninguém precisa do histórico. |
| Painel interno de administração | Não | Pouca complexidade e pouca exigência de auditoria. |
| Fluxo de pedidos de e-commerce | Sim | Trilha de auditoria, perguntas sobre o passado, mudanças de estado complexas. |
| Transações financeiras | Sim | A lei exige o histórico completo. |
| Editor colaborativo de documentos | Sim | Edições simultâneas precisam do histórico de operações. |
| Matrícula simples num LMS de CRUD | Não | Uma flag booleana completed basta. |
| Livro-razão de XP num LMS gamificado | Sim | Registros imutáveis, só de acréscimo. Encaixe natural. |
O model Enrollment::ExperiencePoint deste LMS segue o espírito do event sourcing. Cada XP concedido é um registro imutável, e o total sai da soma deles. Você nunca atualiza um registro de XP, só acrescenta novos. Isso é event sourcing sem a infraestrutura completa. Um detalhe: nada no model impede uma atualização, então "imutável" aqui é uma convenção. Sobrescrever readonly? para devolver persisted? transformaria a convenção em regra.
A matriz de trade-offs
Event sourcing não sai de graça. Veja o que você ganha e o que paga:
| Dimensão | CRUD | Event sourcing |
|---|---|---|
| Armazenamento | Compacto: uma linha por entidade | Cresce sem limite: toda mudança fica guardada |
| Velocidade de leitura | Rápida: consulta o estado atual direto | Mais lenta sem projeções: é preciso reproduzir |
| Velocidade de escrita | Moderada: lê, altera, grava | Rápida: só acrescenta, mas escritas simultâneas no mesmo stream precisam de lock otimista |
| Trilha de auditoria | Precisa de uma implementação à parte | Já vem junto: os eventos são a trilha de auditoria |
| Depuração | "Qual é o estado?" é fácil; "Como chegou aqui?" é difícil | O log de eventos responde às duas |
| Complexidade | Baixa: conhecida e nativa do Rails | Alta: projeções, consistência eventual, versionamento de eventos |
| Habilidades do time | Conhecimento padrão de Rails | Um jeito diferente de pensar para o time inteiro |
| Mudanças de schema | Migrations alteram os dados existentes | Eventos são imutáveis, então evoluir o schema é mais difícil |
O custo organizacional é o que os times mais subestimam. O event sourcing muda o jeito como os desenvolvedores pensam os dados. Se o seu time de dez desenvolvedores Rails nunca trabalhou com isso, conte com uma queda real de produtividade enquanto ele se adapta, muitas vezes de meses.
Erros comuns
Aplicar event sourcing na aplicação inteira
Noventa por cento da sua app é CRUD simples: páginas de configuração, painéis de admin, perfis de usuário. Embrulhar tudo isso em eventos só acrescenta cerimônia.
Em vez disso, use event sourcing só nos domínios em que histórico, auditoria ou perguntas sobre o passado importam. O resto continua como CRUD padrão do Rails.
Tratar event sourcing como "só um log de auditoria"
Um log de auditoria é um registro secundário do que aconteceu. No event sourcing, os eventos são o registro principal, e a tabela de estado atual é que é secundária. Confunda esses papéis e os eventos e o estado vão se afastar.
Em vez disso, faça dos eventos a fonte da verdade. Derive todo o estado a partir dos eventos, nunca o contrário.
Guardar eventos que ninguém lê
Evento que ninguém lê é peso morto: armazenamento e complexidade por nada.
Em vez disso, antes de criar um tipo de evento, aponte pelo menos um consumidor real que precise dele: uma projeção, um relatório, uma notificação.
Resumo
| Conceito | O que guardar |
|---|---|
| CRUD (baseado em estado) | Guarda só o estado atual; o histórico se perde a cada atualização |
| Event sourcing | Guarda cada mudança como um evento imutável; o estado atual é derivado |
| O log é a verdade | Os eventos são o registro oficial; as tabelas de estado são visões derivadas |
| Livro-razão | A contabilidade funciona assim há mais de 700 anos |
| Quando usar | Trilhas de auditoria, sistemas financeiros, perguntas sobre o passado, edição colaborativa |
| Quando evitar | CRUD simples, painéis de admin, perfis de usuário, blogs |
| Principal trade-off | Você ganha histórico completo e auditoria; paga com complexidade e armazenamento |
| Custo organizacional | Muda o jeito de pensar do time; conte com meses de produtividade menor |
Continue com a aula 2
Eventos como cidadãos de primeira classe
Trata de nomes no passado, schemas com valores do momento, metadados, imutabilidade e três estratégias de versionamento de eventos.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.