Event Sourcing
Storing the sequence of events that led to current state rather than current state alone.
Overview
Event Sourcing stores the history of state changes as an immutable sequence of events rather than the current state alone. The current state is derived by replaying all events. This gives you a complete audit trail, temporal queries (what was the state at time T?), and the ability to derive new read models from historical data.
Origin
Greg Young formalised Event Sourcing as a pattern around 2010 through talks and writing, often paired with CQRS. The concept has antecedents in accounting ledgers and financial transaction logs, the original event stores.
Examples
Event-sourced bank account
class BankAccount
attr_reader :id, :balance, :events
def initialize(id)
@id = id
@balance = 0
@events = []
end
def deposit(amount, reference:)
apply_event({ type: 'deposited', amount: amount, reference: reference, at: Time.current })
end
def withdraw(amount, reference:)
raise "Insufficient funds" if amount > @balance
apply_event({ type: 'withdrawn', amount: amount, reference: reference, at: Time.current })
end
def self.reconstitute(events)
account = new(events.first[:account_id])
events.each { |e| account.apply_event(e, persist: false) }
account
end
private
def apply_event(event, persist: true)
case event[:type]
when 'deposited' then @balance += event[:amount]
when 'withdrawn' then @balance -= event[:amount]
end
@events << event if persist
end
endReplaying events from any point in history gives you the state at that moment. The event log is the source of truth; the balance is a derived projection.
Use Cases
- 01Financial systems requiring complete, immutable audit trails
- 02Collaborative editing where conflict resolution requires full history
- 03Debug-friendly systems: replay events to reproduce any past state
- 04Deriving new read models from historical data without re-collecting it
When Not to Use
- //Simple CRUD where audit trails are not required, the complexity far exceeds the benefit
- //High-volume systems without careful event storage design, event logs grow unboundedly without snapshotting
- //Teams without deep familiarity with eventual consistency and CQRS
Technical Notes
- Snapshotting: for aggregates with long event histories, store periodic snapshots and replay only events after the snapshot
- Event schema evolution is the hardest operational challenge: old events in the log must still be replayable with new code
- EventStoreDB and Apache Kafka are purpose-built event stores. PostgreSQL with an events table works for moderate-scale systems
- Event sourcing and CQRS (see below) are complementary but independent. Use one without the other if it fits the problem
More in Architecture