<h2>O que é Event Sourcing</h2>
<p>Event Sourcing é um padrão arquitetural onde você armazena todas as mudanças de estado de uma aplicação como uma sequência imutável de eventos. Em vez de persistir apenas o estado atual (como faz um banco de dados tradicional), você registra cada ação que ocorreu. Isso significa que o histórico completo está sempre disponível, e você pode reconstruir qualquer estado anterior simplesmente reproduzindo os eventos.</p>
<p>A principal diferença em relação ao paradigma CRUD tradicional é que aqui não você <em>atualiza</em> um registro — você <em>registra que algo aconteceu</em>. Essa abordagem traz benefícios como auditoria natural, debugging facilitado, e possibilidade de projeções múltiplas do mesmo dado. No entanto, adiciona complexidade operacional que deve ser bem compreendida antes de aplicar em produção.</p>
<h3>Por que usar Event Sourcing?</h3>
<p><strong>Auditoria e Conformidade</strong>: Cada mudança fica registrada permanentemente com timestamp. Para setores como financeiro e healthcare, isso é ouro puro. <strong>Debugging e Troubleshooting</strong>: Você pode reproduzir exatamente o que aconteceu em um dado momento. <strong>CQRS (Command Query Responsibility Segregation)</strong>: Separar escrita (comandos) de leitura (queries) fica naturalmente elegante. <strong>Event-Driven Architecture</strong>: Permite comunicação assincrônica entre microsserviços através de eventos.</p>
<h2>Arquitetura e Componentes Principais</h2>
<p>Um sistema com Event Sourcing típico possui: o <strong>Event Store</strong> (banco imutável que armazena eventos), o <strong>Aggregate</strong> (entidade que processa comandos e emite eventos), <strong>Projections</strong> (visões processadas dos eventos para consulta rápida), e <strong>Event Handlers</strong> (reagem aos eventos para efeitos colaterais).</p>
<p>O fluxo é simples: usuário executa uma ação → comando é validado → aggregate processa e emite evento → evento é persistido → projeções são atualizadas → subscribers reagem. A garantia de imutabilidade do event store é crítica: você nunca deleta ou altera eventos, apenas adiciona novos.</p>
<h3>Event Store</h3>
<p>É o coração do sistema. Você precisa garantir que cada evento seja persistido de forma ordenada e recuperável. Existem soluções especializadas como EventStoreDB e Axon Framework, mas você também pode implementar com PostgreSQL ou MongoDB.</p>
<pre><code class="language-javascript">// Exemplo simples de Event Store em Node.js com PostgreSQL
const { Pool } = require('pg');
class EventStore {
constructor() {
this.pool = new Pool({
connectionString: 'postgresql://user:password@localhost/eventstore'
});
}
async appendEvent(aggregateId, eventType, eventData, metadata = {}) {
const query = `
INSERT INTO events (aggregate_id, event_type, event_data, metadata, created_at)
VALUES ($1, $2, $3, $4, NOW())
RETURNING *
`;
const result = await this.pool.query(query, [
aggregateId,
eventType,
JSON.stringify(eventData),
JSON.stringify(metadata)
]);
return result.rows[0];
}
async getEventsByAggregateId(aggregateId) {
const query = 'SELECT * FROM events WHERE aggregate_id = $1 ORDER BY created_at ASC';
const result = await this.pool.query(query, [aggregateId]);
return result.rows;
}
}
module.exports = EventStore;</code></pre>
<h3>Aggregates e Projeções</h3>
<p>Um <strong>Aggregate</strong> é uma entidade que encapsula lógica de negócio. Ele recebe comandos, aplica regras e emite eventos. Uma <strong>Projeção</strong> é uma visão construída a partir de eventos, otimizada para leitura rápida.</p>
<pre><code class="language-javascript">// Aggregate de Conta Bancária
class BankAccountAggregate {
constructor(accountId) {
this.accountId = accountId;
this.balance = 0;
this.status = 'active';
this.changes = [];
}
deposit(amount) {
if (amount <= 0) throw new Error('Invalid amount');
this.recordEvent('MoneyDeposited', { amount, timestamp: new Date() });
}
withdraw(amount) {
if (amount > this.balance) throw new Error('Insufficient funds');
this.recordEvent('MoneyWithdrawn', { amount, timestamp: new Date() });
}
recordEvent(eventType, eventData) {
this.changes.push({ eventType, eventData });
this.applyEvent(eventType, eventData);
}
applyEvent(eventType, eventData) {
if (eventType === 'MoneyDeposited') {
this.balance += eventData.amount;
} else if (eventType === 'MoneyWithdrawn') {
this.balance -= eventData.amount;
}
}
loadFromHistory(events) {
events.forEach(event => {
this.applyEvent(event.event_type, JSON.parse(event.event_data));
});
}
}
// Projeção para leitura rápida
class AccountBalanceProjection {
constructor() {
this.accounts = new Map();
}
handleMoneyDeposited(accountId, eventData) {
const account = this.accounts.get(accountId) || { balance: 0 };
account.balance += eventData.amount;
account.lastUpdated = eventData.timestamp;
this.accounts.set(accountId, account);
}
handleMoneyWithdrawn(accountId, eventData) {
const account = this.accounts.get(accountId) || { balance: 0 };
account.balance -= eventData.amount;
account.lastUpdated = eventData.timestamp;
this.accounts.set(accountId, account);
}
getBalance(accountId) {
const account = this.accounts.get(accountId);
return account ? account.balance : 0;
}
}
module.exports = { BankAccountAggregate, AccountBalanceProjection };</code></pre>
<h2>Desafios e Boas Práticas</h2>
<p>Event Sourcing não é uma solução universal. O principal desafio é a <strong>eventual consistency</strong>: quando você escreve um evento, as projeções podem levar tempo para atualizar. Isso exige que sua aplicação aceite inconsistência temporária. Além disso, o <strong>crescimento do event store</strong> é inevitável — com milhões de eventos, reconstruir um aggregate do zero fica lento. A solução é usar <strong>snapshots</strong>: periodicamente salve o estado calculado para não precisar reprocessar todos os eventos.</p>
<p>Outro ponto crítico: <strong>versionamento de eventos</strong>. Requisitos mudam, e você não pode simplesmente deletar eventos antigos. Implemente versioning desde o início, com lógica de migração para lidar com esquemas desatualizados.</p>
<pre><code class="language-javascript">// Boas práticas: Snapshot e Versionamento
class SnapshotStore {
constructor() {
this.snapshots = new Map();
}
saveSnapshot(aggregateId, version, state) {
this.snapshots.set(aggregateId, { version, state, timestamp: new Date() });
}
getSnapshot(aggregateId) {
return this.snapshots.get(aggregateId);
}
}
// Handler com tratamento de versões
function handleEvent(eventType, eventVersion, eventData) {
if (eventType === 'MoneyDeposited') {
if (eventVersion === 1) {
// Lógica legada
return { amount: eventData.amount };
} else if (eventVersion === 2) {
// Nova lógica com taxa
return { amount: eventData.amount, fee: eventData.fee || 0 };
}
}
}</code></pre>
<h2>Quando Usar Event Sourcing</h2>
<p>Aplique Event Sourcing em domínios onde <strong>auditoria é crítica</strong> (financeiro, saúde, compliance), <strong>histórico é valor</strong> (análise temporal, machine learning), ou <strong>comunicação entre sistemas</strong> é complexa (microsserviços). Evite em aplicações CRUD simples, sistemas com latência crítica (real-time gaming), ou quando o overhead não se justifica.</p>
<p>Um e-commerce, por exemplo, se beneficia: rastrear pedidos, devoluções, reembolsos. Uma aplicação de to-do list? Provavelmente não.</p>
<h2>Conclusão</h2>
<p>Event Sourcing é poderoso, mas não é bala de prata. O grande aprendizado é entender que <strong>estado é derivado de eventos, não o inverso</strong>. Isso muda fundamentalmente como você pensa sobre persistência. Segundo, reconheça que <strong>eventual consistency é uma troca aceitável</strong> por auditoria, escalabilidade e simplicidade operacional. Terceiro, <strong>comece simples</strong>: implemente event store básico antes de adicionar snapshots, projeções complexas ou CQRS completo.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://eventstore.com/docs/" target="_blank" rel="noopener noreferrer">EventStoreDB Documentation</a></li>
<li><a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/event-sourcing" target="_blank" rel="noopener noreferrer">Microsoft - Event Sourcing Pattern</a></li>
<li><a href="https://martinfowler.com/eaaDev/EventSourcing.html" target="_blank" rel="noopener noreferrer">Martin Fowler - Event Sourcing</a></li>
<li><a href="https://docs.axoniq.io/reference-guide/" target="_blank" rel="noopener noreferrer">Axon Framework Guide</a></li>
<li><a href="https://www.oreilly.com/library/view/building-event-driven-microservices/9781492057321/" target="_blank" rel="noopener noreferrer">Building Event-Driven Microservices - O'Reilly</a></li>
</ul>