Event sourcing architecture
Event store, projections, snapshots, and eventual consistency.
Event sourcing flips the database inside out: instead of storing the current state of an entity, you store every state change as an immutable event. An Order moves from Pending to Confirmed to Shipped — and your database contains three 'OrderEvent' records: OrderCreated, PaymentProcessed, OrderShipped. This gives you audit trails, temporal queries, and the ability to rebuild the entire system from the event log.
The architecture has four parts: the EventStore (an immutable append-only log), DomainEvents (the facts), Aggregates (entities that apply events to compute their state), and Projections (read-optimized materialized views built from events). The SnapshotStore is an optional optimization: instead of replaying years of events, you cache the state at a checkpoint.
When to use this template
- Audit and compliance systems — financial transactions, legal holds, or security logs where you need to prove what happened and when.
- Complex domain logic — if your state machine is intricate (Orders with many status transitions, Subscriptions with downgrades, multi-step fulfillment), event sourcing makes all the branches visible.
- Temporal and historical analysis — if you need to answer "what was the customer's balance on this date?" or "replay this event sequence in testing", event sourcing makes it trivial.
How to adapt it
The core pattern is the same, but the details depend on your domain and scale:
- Add a Command handler between the user and the EventStore — commands (CreateOrder, ProcessPayment) are validated, then events are appended.
- Insert a Dead letter queue in the Projection update path to catch events that fail to process and require manual review.
- Add schema versioning to DomainEvent — events evolve, so you need a version field and migration logic when the shape of an OrderCreated event changes.
Visual edits regenerate clean code, so you can sketch how a specific domain entity — Orders, Subscriptions, Accounts — flows through the architecture.
Mermaid code
Copy it anywhere Mermaid is supported — GitHub, Notion, or your docs.
classDiagram
class EventStore {
+append(event)
+getEvents(aggregateId)
+subscribe(handler)
}
class DomainEvent {
+aggregateId
+eventType
+timestamp
+data
}
class Aggregate {
+state
+applyEvent(event)
+getChanges()
}
class Projection {
+materialize(events)
+query()
}
class SnapshotStore {
+save(aggregateId, snapshot)
+load(aggregateId)
}
EventStore --> DomainEvent
Aggregate --> EventStore
Aggregate --> SnapshotStore
Projection --> EventStore
Frequently asked questions
- What is event sourcing?
- Instead of storing the current state of an entity in a database, event sourcing stores the complete history of state-changing events. Your Orders table becomes an immutable log: 'OrderCreated', 'PaymentProcessed', 'ItemsShipped'. The current state is rebuilt by replaying all events. This gives you an audit trail for free and makes temporal queries and system rebuilds trivial.
- Why use Projections and Snapshots?
- Replaying 10 years of events every time you need the current state is slow. Snapshots cache the state at a point in time ('Order state at 2026-07-10'), so you only replay events after the snapshot. Projections materialize views optimized for queries — e.g., 'all orders by customer' — and update when new events arrive. Together they make event-sourced systems performant.
- What is eventual consistency in this architecture?
- When an event is appended, the aggregate's state updates immediately, but projections update asynchronously. This means a query against a projection might lag behind the latest event by milliseconds or seconds. This is acceptable for most business logic; strong consistency is reserved for the event store itself.
- How do I implement this pattern?
- Start with the EventStore: an append-only log (PostgreSQL table, DynamoDB stream, or EventStoreDB). Next, build an Aggregate that applies events to its state. Then create a Projection that reads all events and maintains a read-optimized view. Visual edits regenerate clean code, so you can sketch the flow of a specific event through your system in the editor.
Related templates
Message queue retry logic
Publish-subscribe with exponential backoff and dead-letter queues.
Queue topology and message routing
Message broker architecture with topic subscriptions and dead-letter handling.
Real-time notification delivery system
Web sockets, message broker, and delivery handlers in action.