| 1 | The Problem It Solves | 2 | Pattern Structure |
| 3 | When to Use | 4 | When Not to Use |
| 5 | Trade-offs | 6 | Implementation Approach |
| 7 | Anti-Patterns to Avoid | 8 | Cloud-Specific Implementations |
| 9 | References |
The Problem It Solves
Traditional data storage keeps only the current state. You know that an order is cancelled but not that it was first placed, then modified, then disputed, then cancelled. Audit trails are afterthoughts added alongside the current state table. Debugging production issues requires log correlation rather than replaying the sequence of events that led to the problem.
Event sourcing makes the history of state changes the primary data model. The current state is a derived view, not the authoritative record.
Pattern Structure
%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'Inter, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%% flowchart TD START([Command Received]) START --> LOAD[Load Aggregate\nReplay events from event store\nApply each event to build state] LOAD --> VALIDATE_ES[Apply Business Rules\nValidate command against\ncurrent aggregate state] VALIDATE_ES --> RESULT{Command\nValid?} RESULT -->|No| REJECT[Reject command\nReturn domain error\nNo event written] RESULT -->|Yes| APPEND[Append new event\nto event store\nImmutable, ordered, timestamped] APPEND --> NOTIFY[Notify subscribers\nProject to read models\nTrigger downstream sagas] NOTIFY --> SNAPSHOT{Aggregate event\ncount exceeds threshold?} SNAPSHOT -->|Yes| SNAP_WRITE[Write snapshot\nCurrent state at event N\nSpeedup future replays] SNAPSHOT -->|No| DONE_ES([State Updated via Event Log]) SNAP_WRITE --> DONE_ES style START fill:#4f8ef7,color:#fff style DONE_ES fill:#10b981,color:#fff style REJECT fill:#fef3c7 style SNAP_WRITE fill:#e0f2fe
When to Use
- Domains requiring a complete, auditable history of all state changes — finance, healthcare, legal
- Systems where the ability to replay events to rebuild state or debug issues has significant operational value
- Architectures already using CQRS — event sourcing provides the event stream that powers read model projections
- Systems requiring temporal queries — what was the state of this order at 14:32 last Tuesday?
- Event-driven architectures where the event log also serves as the integration bus between services
When Not to Use
- Simple CRUD domains where state history has no business value
- Teams new to event sourcing — the learning curve is steep and the operational model differs significantly from traditional databases
- Systems requiring complex reporting queries against current state — pure event sourcing makes ad-hoc queries difficult without projections
- High-frequency, high-volume state changes where replay time becomes impractical without aggressive snapshotting
Trade-offs
| Benefit | Cost |
|---|---|
| Complete audit trail by design — no separate audit table | Reading current state requires replaying events — mitigated by snapshots |
| Temporal queries — reconstruct state at any point in time | Changing event schemas after events are written is complex |
| Natural integration event stream — events published to other services | Steeper learning curve for teams new to the pattern |
| Bug reproduction — replay the exact event sequence that caused the issue | Event store becomes large over time — archival and retention policies needed |
Implementation Approach
Design events as facts, not deltas. An event describes what happened — OrderPlaced, ItemAdded, PaymentProcessed — not a diff of what changed. Events are named in the past tense. They carry all the data needed to understand what occurred without referencing external state.
Aggregate identity determines the event stream. All events for a given order are stored in the same stream, keyed by order ID. Loading the aggregate means reading all events in that stream in order. The aggregate applies each event to rebuild its current state.
Implement snapshotting for long-lived aggregates. After N events (typically 50–200), write a snapshot of the current state. Future loads start from the most recent snapshot and replay only the events after it. This bounds the replay time regardless of how long-lived the aggregate is.
Treat the event store as append-only infrastructure. Events are never modified or deleted after writing. Schema changes are handled through event versioning and upcasting — transforming older event formats to the current schema on read.
Anti-Patterns to Avoid
Modifying or deleting events after they have been appended to the store to correct a mistake. The event store's value comes entirely from its immutability and completeness. Mutating history undermines the audit trail and breaks any projection built from those events.
Append a compensating event — OrderCancelled corrects an OrderPlaced that should not have occurred. The log reflects what actually happened including the correction.
Designing events that describe what should happen rather than what did happen. PlaceOrder is a command. OrderPlaced is an event. Storing command-shaped events means replaying them re-executes business logic, producing different results as business rules change.
Events describe facts about the past. They are named in the past tense and contain the data that was true at the moment the event occurred — not instructions for what to do with that data.
Cloud-Specific Implementations
- AWS: DynamoDB Streams provides a 24-hour ordered event log per table — a practical event sourcing store for most workloads. Run EventStoreDB on ECS Fargate for a dedicated event store. See Event-Driven Architecture on AWS for the EventBridge + Lambda consumer pattern.
Flowchart
References
- Young, Greg — Event Sourcing. eventuate.io/exampleapps.html
- Fowler, Martin — Event Sourcing Pattern. martinfowler.com/eaaDev/EventSourcing
- Vernon, Vaughn — Implementing Domain-Driven Design. Addison-Wesley, 2013.
- EventStoreDB — Purpose-built event sourcing database. eventstore.com