Event Sourcing
Architectural pattern storing application state as a sequence of immutable events, enabling full audit trails and temporal reconstruction.
Updated on March 30, 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.
Let's talk about your project
Need expert help on this topic?
Our team supports you from strategy to production. Let's chat 30 min about your project.

