Event Sourcing & CQRS Vocabulary: Patterns for Distributed Systems
Event store, projections, commands vs queries, eventual consistency, and CQRS/ES vocabulary for distributed system developers.
Distributed systems have their own dialect. When your team starts discussing event-driven architecture, you will quickly encounter a dense cluster of interconnected terms — event sourcing, CQRS, projections, sagas — that can feel overwhelming if you have only encountered traditional CRUD-based systems before. This guide breaks down the core vocabulary of event sourcing and CQRS, explains each term in plain English, and shows you the kinds of conversations these words appear in on real engineering teams.
Core Terms: Events and Commands
The entire paradigm rests on a fundamental distinction between two types of messages.
Command — a message expressing intent: a request for the system to do something. Commands are written in the imperative mood and may be rejected.
“We model user actions as commands —
PlaceOrder,CancelSubscription,UpdateShippingAddress. The system validates them before doing anything.”
“If validation fails, the command is rejected and nothing is written. That’s the beauty of it — side effects only happen when business rules are satisfied.”
Event — a message recording a fact: something that has already happened. Events are immutable and always written in the past tense.
“Once the command is accepted, we emit
OrderPlaced. That event is the source of truth — we never delete or alter it.”
“Events are facts.
UserDeactivateddoesn’t mean ‘please deactivate this user’ — it means ‘this user was deactivated at this timestamp, end of story.’”
Event sourcing — an architectural pattern where the state of an application is derived entirely from a sequence of events rather than from a mutable record in a database. Instead of storing the current state, you store every state change as an event.
“We switched to event sourcing because we kept losing audit history. Now we have the complete log of everything that ever happened to an order.”
“The tricky part of event sourcing is that you can’t just run a
SELECT— you have to replay events to reconstruct current state.”
Storage and Retrieval
Event store — a specialised database (or a purpose-built table in a relational database) optimised for append-only writes of events. Popular choices include EventStoreDB, Apache Kafka, and custom implementations on top of PostgreSQL.
“Our event store is append-only. Nobody updates rows — we only insert new events.”
“We chose EventStoreDB because it gives us built-in subscriptions and projections out of the box.”
Event stream — the ordered sequence of events associated with a single aggregate (for example, all events for Order-42). Each stream has a unique identifier and a monotonically increasing version number.
“Each order has its own event stream. When you load an order, you fetch its stream and replay it.”
“We had a bug where two processes were writing to the same stream concurrently. The optimistic concurrency check caught the conflict immediately.”
Snapshot — a serialised point-in-time representation of an aggregate’s state, stored alongside the event stream to avoid replaying thousands of events on every load.
“After 500 events on a single aggregate, replaying from scratch was taking 200 milliseconds. We introduced snapshots every 100 events and brought that down to under 5ms.”
“Snapshots are an optimisation, not a requirement. Start without them and add them when load testing shows you need them.”
Replay — the process of re-processing a sequence of historical events, either to rebuild an aggregate’s state or to reconstruct a read model from scratch.
“When we deployed the new reporting service, we replayed six months of events to populate its database.”
“Replay is one of the superpowers of event sourcing. You can derive any view of your data at any point in history.”
CQRS and Read Models
CQRS (Command Query Responsibility Segregation) — a pattern that separates the write side of a system (commands that change state) from the read side (queries that return data). The two sides can use entirely different data models, databases, and optimisation strategies.
“We adopted CQRS because our write model was heavily normalised but our dashboard queries needed denormalised aggregations. Now they’re completely separate.”
“CQRS doesn’t require event sourcing, but the two patterns complement each other extremely well.”
Write model — the part of the system responsible for processing commands, enforcing business rules, and persisting events. It is optimised for consistency and correctness, not query performance.
“The write model only cares about whether a command is valid. It doesn’t know or care how the data looks to the end user.”
Read model — a denormalised, query-optimised view of data built by consuming events from the write side. Also called a projection (see below). There can be multiple read models serving different consumers.
“We have three read models for orders: one for customer-facing status, one for warehouse picking, and one for finance reporting.”
“If the read model gets corrupted, we just drop it and replay. That’s the key advantage — it’s disposable.”
Projection — the process (or its output) of transforming a stream of events into a read model. A projection subscribes to events and updates a queryable data store accordingly.
“The projection listens for
OrderShippedevents and updates a Redis cache that the mobile app reads.”
“We had a projection fall behind by three hours during a Kafka consumer lag spike. Users saw stale data until it caught up.”
Consistency and Reliability Patterns
Eventual consistency — a property of distributed systems where, after a write, all read models will eventually reflect the new state — but not necessarily immediately. This is the trade-off you accept with CQRS projections.
“We had to educate the product team that reads are eventually consistent. If you place an order and refresh immediately, you might not see it for a few hundred milliseconds.”
“Eventual consistency is fine for 95% of use cases. For the 5% that need strong consistency, we keep those on synchronous paths.”
Idempotent handler — an event handler designed so that processing the same event multiple times produces the same result as processing it once. Essential in distributed systems where message delivery is at-least-once.
“All our projections are idempotent. If a message is re-delivered, we check whether we’ve already processed that event ID and skip it.”
“Making handlers idempotent is non-negotiable when you’re using Kafka. You will get duplicate messages.”
Saga / process manager — a long-running workflow coordinator that reacts to events and issues commands to orchestrate a multi-step business process across multiple aggregates or services. Sometimes called a process manager when it maintains explicit state.
“The order fulfilment saga listens for
PaymentConfirmed, then sendsReserveInventory, then waits forInventoryReservedbefore dispatching the shipping command.”
“Sagas replace distributed transactions. Instead of a two-phase commit, you design compensating commands for the failure paths.”
Outbox pattern — a reliability technique where commands or events are first written to an outbox table in the same local database transaction as the domain change, then a separate process publishes them to the message broker. This guarantees at-least-once delivery without distributed transactions.
“Without the outbox pattern, we had a race condition where the database write succeeded but the Kafka publish failed. We lost events.”
“The outbox pattern solved our dual-write problem. Now the database row and the event are either both committed or neither is.”
How to Use These in Conversation
Understanding these terms academically is one thing — using them naturally in standup, code review, and architecture discussions is another. Here are common scenarios.
Scenario 1 — Standup update:
“The projection for the analytics dashboard fell behind overnight. I’ve identified it was an idempotent handler bug causing it to skip events. I’m deploying a fix and will replay from yesterday’s snapshot.”
Scenario 2 — Architecture review:
“I’d propose we use the outbox pattern here rather than publishing directly to Kafka. That way the command and the event are written atomically and we get at-least-once delivery guarantees without a saga.”
Scenario 3 — Code review comment:
“This handler isn’t idempotent — if the same event is delivered twice, we’ll create a duplicate record. We should check for the event ID in the read model before applying the update.”
Scenario 4 — Onboarding a new team member:
“In this codebase, the write model lives in the
domainpackage and the read models are inprojections. The event store is EventStoreDB. When you load an aggregate, you replay its event stream — unless a snapshot is available, in which case you load that and replay only the events after it.”
Quick Reference
| Term | Type | One-line definition |
|---|---|---|
| Command | Message | Expresses intent; may be rejected |
| Event | Message | Records a past fact; immutable |
| Event sourcing | Pattern | State derived from an append-only event log |
| Event store | Infrastructure | Append-only storage optimised for events |
| Projection | Process / output | Transforms events into a queryable read model |
| Snapshot | Optimisation | Cached aggregate state to avoid full replay |
| CQRS | Pattern | Separates write model from read model |
| Eventual consistency | Property | Reads converge to correct state, but not instantly |
| Idempotent handler | Design property | Safe to process same event multiple times |
| Outbox pattern | Reliability pattern | Atomic write + guaranteed message publish via outbox table |