Event Surcing: base
O que é Event Surcing
O Event Surcing (ES) é uma forma de armazenar o status dos objetos de domínio, não como uma «linha atual», mas como um registro imutável de eventos que descrevem tudo o que aconteceu. O estado atual de uma unidade é um ajuntamento (replay) de seus eventos, e qualquer visão de leitura é construída como projeções acima deste registro.
Investigações-chave:- A história é a «fonte primária da verdade», o estado é a projeção da história.
- Qualquer estado pode ser reproduzido novamente, verificado e explicado (auditoria).
- Adicionar novas visualizações e analistas não requer migrações de «instantâneos» antigos - basta perder eventos.
Termos básicos
A unidade é uma unidade de domínio de coerência com invariantes nítidos (Order, Payment, UserBalance).
O evento é um fato imutável que aconteceu no passado ('payment. authorized`, `order. shipped`).
Event Store é uma revista de apend-only que garante a ordem dos eventos dentro da unidade.
A versão do aparelho é o número do último evento aplicado (para optimístico concurrency).
O Snapshot é um molde de estado periódico para acelerar o encolhimento.
Projeção (modelo read) - Uma vista materializada para leitura/pesquisa/relatório (frequentemente asincrona).
Como funciona (fluxo de comandos → eventos → projeções)
1. O cliente envia um comando ('CapturePayment', 'PlaceOrder').
2. A unidade valida os invariantes e, se estiver tudo bem, gera eventos.
3. Eventos atômicos são adicionados à Event Store com uma versão otimística.
4. Os processadores de projeção estão assinados para o fluxo de eventos e atualizam os modelos read.
5. Ao carregar o dispositivo para o seguinte comando, o estado será restaurado (se houver) → evento após o resultado.
Design de eventos
Atributos obrigatórios (núcleo)
json
{
"event_id": "uuid",
"event_type": "payment. authorized. v1",
"aggregate_type": "Payment",
"aggregate_id": "pay_123",
"aggregate_version": 5,
"occurred_at": "2025-10-31T10:42:03Z",
"payload": { "amount": 1000, "currency": "EUR", "method": "card" },
"meta": { "trace_id": "t-abc", "actor": "user_42" }
}
Recomendações:
- Nome: 'domain. action. v{major}`.
- Adutora: novos campos - opcionais, sem alterar o sentido dos antigos.
- Minimalismo: apenas factos, sem duplicação de dados facilmente recuperáveis.
Contratos e esquemas
Verifique os circuitos (Avro/JSON Schema/Protobuf) e verifique a compatibilidade em CI.
As alterações «quebrantes» incluem uma nova versão maior do evento e a publicação paralela de «v1 »/« v2» durante o período de migração.
Acesso competitivo: optimística concurrency
Regra: só é possível gravar novos eventos se 'expected _ version = = current _ version'.
Pseudocode:pseudo load: snapshot(state, version), then apply events > version new_events = aggregate. handle(command)
append_to_store(aggregate_id, expected_version=current_version, events=new_events)
//if someone has already written an event between load and append, the operation is rejected -> retray with reload
Assim garantimos a integridade dos invariantes sem transações distribuídas.
Snapshots (aceleração do comprimido)
Faça um deslize a cada N de eventos ou temporizador.
Храните `snapshot_state`, `aggregate_id`, `version`, `created_at`.
Verifique sempre e atinja os acontecimentos após o processo (não confia apenas no molde).
Retire-os de modo que possam ser reencaminhados (não guarde os campos mágicos).
Projeções e CQRS
ES é naturalmente combinado com o CQRS:- Modelo Write = unidades + Event Store.
- Modelos read = projeções atualizadas por eventos (cartões Redis, OpenSearch para pesquisa, ClickHouse/OLAP para relatórios).
- As projeções são idimpotentes: reexaminar o mesmo 'event _ id' não altera o resultado.
Evolução dos circuitos e compatibilidade
Aditivo-first: adicione os campos; não altere os tipos/semântica.
Para alterações complexas: produza novos tipos de eventos e escreva migradores de projeção.
Mantenha a gravação dupla ('v1' + 'v2') por um período de transição e retire 'v1' quando todas as projeções estiverem prontas.
Segurança, PII e «direito ao esquecimento»
O histórico muitas vezes contém dados sensíveis. Abordagens:- Minimize o PII em eventos (identificadores em vez de dados, peças em lados protegidos).
- Cripto-Apagar: Criptografe os campos e destrua a chave ao pedir a remoção (o evento permanece, mas os dados não estão disponíveis).
- Eventos de redação: 'user. piiredacted. v1 'com substituição de campos sensíveis em projeções (o histórico mantém o fato de edição).
- Políticas de retenção: Para alguns domínios, alguns eventos podem ser arquivados no armazenamento WORM.
Desempenho e zoom
Particionamento: a ordem é importante dentro do dispositivo - particione por 'aggregate _ id'.
Início frio: snapshots + compactação periódica.
Batch-append: Agrupe os eventos com uma transação.
Backpressure e DLQ para processadores de projeção; mede a liga (tempo e número de mensagens).
Indexação Event Store: acesso rápido por '(aggregate _ tipo, agregate _ id)' e hora.
Testes
Especification tests para agregados: cenário de «comandos → eventos esperados».
Projation tests: forneça um fluxo de eventos e verifique o estado/índice materializado.
Replayability tests: Reestabeleça as projeções «do zero» no estande - verifique se o resultado é igual.
Chaos/latency: Insira atrasos e duplicações, verifique a idempotidade.
Exemplos de domínio
1) Pagamentos
Eventos: 'payment. initiated`, `payment. authorized`, `payment. captured`, `payment. refunded`.
Invariantes: Não se pode 'capture' sem 'autorized'; as quantias são irretocáveis; a moeda está inalterada.
Projeções: cartão de pagamento (KV), pesquisa de transações (OpenSearch), relatórios (OLAP).
2) Pedidos (e-commerce)
Eventos: 'order. placed`, `order. paid`, `order. packed`, `order. shipped`, `order. delivered`.
Invariantes: transições de estado no diagrama de estado; pode ser cancelado antes de 'shipped'.
Projeções: lista de pedidos do usuário, SLA-dashboards por estatais.
3) Balanços (finanças/iGaming)
Eventos: 'balance. deposited`, `balance. debited`, `balance. credited`, `balance. adjusted`.
Invariante rígido: o equilíbrio não sai <0; os comandos são idênticos por 'operation _ id'.
As operações críticas são lidas diretamente a partir da unidade (coerência rigorosa), UI a partir da projeção (eventual).
Estrutura típica da Event Store (opção de banco de dados)
events
`event_id (PK)`, `aggregate_type`, `aggregate_id`, `version`, `occurred_at`, `event_type`, `payload`, `meta`
Índice: '(aggregate _ tipo, aggregate _ id, version)'.
snapshots
`aggregate_type`, `aggregate_id`, `version`, `state`, `created_at`
Índice: '(aggregate _ tipo, aggregate _ id)'.
consumers_offsets
'consumer _ id', 'event _ id '/' posição', 'updated _ at' (para projeções e retlay).
Perguntas frequentes (FAQ)
Você precisa usar o ES em todos os lugares?
Não. ES é útil quando uma auditoria, invariantes complexos, reprodutividade e diferentes visões de dados são importantes. Para um CRUD simples é excessivo.
E os pedidos de «estado relevante»?
Leia a partir da projeção (rápido, eventual) ou - da unidade (mais caro, mas rigoroso). As operações críticas normalmente usam a segunda via.
O Kafka/strim corretor é necessário?
Event Store é a fonte da verdade; o corretor é fácil de distribuir eventos para projetores e sistemas externos.
O que fazer com o «direito ao esquecimento»?
Minimizar o PII, criptografar campos sensíveis e aplicar cripto-apagado/redação em projeções.
Como migrar os dados antigos?
Escreva um script de geração de eventos retrospectiva («re-highstori») ou comece com «estado-como-é» e publique eventos apenas para novas alterações.
Antipattern
Event Surcing «por hábito»: torna o sistema mais difícil sem benefícios de domínio.
Fat events: payload inchado com PII e duplas - freios e problemas de complacência.
Ausência de concurrency optimístico: perda de invariantes nas corridas.
Projeções impermeáveis, sem réplica/snapshot → fixação manual.
CDC crus como eventos de domínio, fuga de esquemas de BD e conectividade severa.
Misture eventos internos e de integração: Publique uma «vitrine» estabilizada.
Folha de cheque para produção
- Unidades, invariantes e eventos definidos (nomes, versões, esquemas).
- A Event Store fornece uma ordem dentro da unidade e da concurrency optimística.
- O plano de reencaminhamento está incluído.
- As projeções são idimpotentes, há DLQ e métricas de laje.
- Os esquemas são validados em CI, a política de versões é documentada.
- PII minimizado, campos criptografados e estratégia de «esquecimento».
- Réplicas de projeções testadas no estande; Há um plano de recuperação de emergência.
- Dashboards: taxa de apêndio, lâmina de projeção, erros de aplicação, proporção de retais.
Resultado
O Event Surcing faz da história do sistema um artefacto de primeira classe: nós registramos os factos, a partir deles reproduzimos o estado e construímos livremente qualquer visão. Isso permite uma auditoria, resistência a mudanças e flexibilidade dos analistas - desde que os circuitos sejam disciplinados, controlados de forma competitiva e com dados sensíveis.