Software Engineering
Event Sourcing and CQRS
Audit log, change history, production incident debugging, temporal queries ('what was the order state yesterday at 14:32?'). All of these are solved straightforwardly in Event Sourcing and require complex retrofitting in traditional CRUD. The system's history is not an afterthought - it is the primary data.
- **LinkedIn**: CQRS with Kafka as event bus - events from the write store propagate to dozens of different read stores (search, recommendations, analytics). Each projection is optimized for its specific query pattern.
- **Axon Framework**: Java framework for Event Sourcing + CQRS used in banks and insurance companies where auditing all state changes is a regulatory requirement - not a feature, a legal obligation.
- **Kafka as Event Store**: many companies use Kafka not only as a message broker but also as an append-only event log. Consumer groups create independent projections from the same event stream.
Event Store
Event Sourcing is a pattern where system state is stored not as a current snapshot but as a sequence of events. Instead of UPDATE orders SET status='confirmed', the system appends an OrderConfirmed event. Current state is derived by replaying all events from the beginning.
Event Store is an append-only storage for events. Operations: Append (add an event to the end of a stream) and Read (read events from position N). Standard databases work, but specialized solutions exist: EventStore DB, Kafka (as an event log). Each event has: eventId, aggregateId, type, payload, timestamp, version (optimistic concurrency).
In Event Sourcing, a user changes their email. How is this correctly stored?
Projections
A Projection (read model) is a derived representation of data built from events. From the same set of events, multiple different projections can be built: current order state, order history timeline, analytics by product, customer purchase report.
Replay is reproducing all events from the beginning to build or update a projection. Snapshots optimize replay: instead of replaying 10,000 events, the snapshot from event 9,900 is loaded and only 100 recent events are replayed. Snapshots are created periodically and stored alongside the event stream.
A new analytical projection is needed for data that has existed in the system for 2 years. How is this done in Event Sourcing?
CQRS
CQRS (Command Query Responsibility Segregation) - separating write operations (Commands) and read operations (Queries) into different models. Write side accepts commands, applies business logic, stores events. Read side builds optimized projections for specific query patterns.
Write side: accepts Commands (CreateOrder, AddItem, Confirm), validates, applies business logic, stores events. Read side: subscribes to events, builds denormalized projections optimized for specific read scenarios. No JOINs on the read side - data is pre-computed.
In a CQRS system, a Command updates the write store, but the read store is updated asynchronously through events. A user submits a command and immediately reads - they see old data. What should the team do?
Eventual Consistency
Eventual Consistency is a guarantee that in the absence of new updates all data replicas will eventually reach the same state. 'Eventually' means: after a period of time whose maximum can be bounded. DNS propagation, CDN cache invalidation, NoSQL replication - all use eventual consistency.
In Event Sourcing and CQRS, eventual consistency is a natural state. Events are published to a bus (Kafka), projection handlers subscribe and update their read stores. The lag between write and read update is the window of inconsistency. Idempotency is critical: the same event may be delivered multiple times (Kafka at-least-once).
Event Sourcing and CQRS are always one unit and cannot be used separately
Event Sourcing and CQRS are independent patterns. CQRS can be applied without Event Sourcing (separate read/write models with a traditional database). Event Sourcing without CQRS - events are the source of truth but there is only one model for reads and writes.
Using them together is common because they complement each other well - but each adds its own complexity. Teams should evaluate each pattern's trade-offs independently before combining them.
An event handler receives the same OrderShipped event twice (due to Kafka retry). How does idempotency protect against double processing?
Key Ideas
- **Event Store**: append-only log of immutable facts. Never UPDATE, never DELETE. State is derived by replaying events.
- **Projections**: derived read models built from events. Same events -> multiple projections for different query patterns. Replay builds new projections from historical data.
- **CQRS**: separate write and read paths. Write side: business logic + events. Read side: denormalized projections optimized for queries. Trade-off: eventual consistency.
Related Topics
Event Sourcing and CQRS connect naturally with other patterns:
- Domain-Driven Design — Domain Events from DDD are the building blocks of Event Sourcing. Bounded Contexts define the scope of each event stream
- Microservices: Patterns — Saga pattern for distributed transactions uses events - direct application of Event Sourcing's event propagation model
Вопросы для размышления
- Event Sourcing complicates the system. For which domains is this justified, and for which is CRUD sufficient?
- How to handle event schema versioning: the event schema changed but old events are already in the store?
- At what event volume does replay performance become a problem, and how do snapshots solve it?