Event Sourcing
Architectural pattern storing application state as a sequence of immutable events, enabling full audit trails and temporal reconstruction.
Updated on January 9, 2026
Event Sourcing is an architectural pattern that persists application state not as a single snapshot, but as a chronological sequence of immutable events. Every change is captured as a descriptive event that records what happened. This approach transforms the database into a complete audit log, where the current state is derived by replaying all historical events.
Fundamentals of Event Sourcing
- Immutable events: each state change is recorded as an historical fact that cannot be modified
- Source of truth: the event store becomes the authoritative source, with current state being a calculated projection
- Complete traceability: preservation of full history with temporal context and causality of changes
- Temporal reconstruction: ability to rebuild system state at any point in the past
Strategic benefits
- Audit and compliance: immutable history meeting regulatory requirements (GDPR, SOX, finance)
- Advanced debugging: exact bug reproduction by replaying events under identical conditions
- Multiple projections: creation of materialized views optimized for different use cases from the same events
- Model evolution: adding new features by replaying history with enriched logic
- Read scalability: natural CQRS separation with asynchronous projection replication
Practical example: Banking system
Consider a bank account where instead of storing only the current balance, we record each transaction as an event:
// Event definitions
interface AccountEvent {
eventId: string;
accountId: string;
timestamp: Date;
version: number;
}
interface AccountOpened extends AccountEvent {
type: 'AccountOpened';
initialDeposit: number;
currency: string;
}
interface MoneyDeposited extends AccountEvent {
type: 'MoneyDeposited';
amount: number;
source: string;
}
interface MoneyWithdrawn extends AccountEvent {
type: 'MoneyWithdrawn';
amount: number;
destination: string;
}
// Aggregate reconstructing state
class BankAccount {
private balance: number = 0;
private status: 'open' | 'closed' = 'open';
private version: number = 0;
private id: string = '';
// State reconstruction via replay
rehydrate(events: AccountEvent[]): void {
events.forEach(event => this.apply(event));
}
private apply(event: AccountEvent): void {
switch (event.type) {
case 'AccountOpened':
this.balance = event.initialDeposit;
this.status = 'open';
break;
case 'MoneyDeposited':
this.balance += event.amount;
break;
case 'MoneyWithdrawn':
this.balance -= event.amount;
break;
}
this.version = event.version;
}
// Command generating an event
withdraw(amount: number): MoneyWithdrawn {
if (this.balance < amount) {
throw new Error('Insufficient funds');
}
return {
type: 'MoneyWithdrawn',
eventId: crypto.randomUUID(),
accountId: this.id,
timestamp: new Date(),
version: this.version + 1,
amount,
destination: 'ATM'
};
}
getBalance(): number {
return this.balance;
}
}Technical implementation
- Model business events: identify meaningful immutable facts (past tense naming: 'OrderPlaced', 'PaymentProcessed')
- Choose an event store: specialized database (EventStoreDB, Apache Kafka) or adapt existing DB (PostgreSQL with append-only tables)
- Implement aggregates: business objects reconstructing their state by replaying events with optimistic concurrency control
- Create projections: materialized views computed asynchronously for queries, updated via event handlers
- Manage snapshots: periodic state saves to accelerate loading of large aggregates
- Setup versioning: event evolution strategy (upcasting) for backward compatibility
Architecture tip
Combine Event Sourcing with CQRS to clearly separate writes (events) from reads (projections). Use snapshots every N events (e.g., 100) to avoid replaying thousands of events on each load. Implement a projection rebuild mechanism to regenerate views in case of bugs or feature additions.
Associated tools and frameworks
- EventStoreDB: specialized database for Event Sourcing with native projections
- Axon Framework: comprehensive Java/Spring framework for CQRS and Event Sourcing
- Marten: .NET library transforming PostgreSQL into an event store with LINQ projections
- Apache Kafka: distributed streaming platform usable as persistent event log
- Eventsauce: lightweight PHP library for Event Sourcing with code generator
- Equinox: F#/.NET toolkit for Event Sourcing with CosmosDB and DynamoDB support
Event Sourcing delivers significant business value in domains requiring traceability, compliance, and temporal analysis (finance, e-commerce, healthcare). While more complex than traditional CRUD architecture, it offers exceptional evolution flexibility and naturally solves complex synchronization and audit problems. The initial investment pays off particularly for long-lived systems with strict regulatory requirements.
