Event-Driven Architecture: CQRS and Event Sourcing Explained
A practical guide to event-driven architecture for scalable systems. Learn how Command Query Responsibility Segregation (CQRS) and event sourcing decouple writes from reads and create audit-ready domain models.
Why CRUD Breaks at Scale
Most applications start with a simple Create-Read-Update-Delete model: one database table, one service layer, one mental model. That works until your read patterns diverge wildly from your write patterns, your audit requirements grow, or multiple teams need to react to the same business event without tight coupling.
Event-driven architecture (EDA) treats domain events (things that happened in the business) as first-class citizens. Instead of mutating rows in place, you record immutable facts: OrderPlaced, PaymentCaptured, InventoryReserved. Downstream systems subscribe to those facts and build their own optimized views.
CQRS: Split Reads from Writes
Command Query Responsibility Segregation separates the model used to change state (commands) from the model used to read state (queries). Your write side enforces invariants and emits events; your read side maintains denormalized projections tailored for UI screens, search indexes, or analytics dashboards.
// Write model: validate and emit
async function placeOrder(cmd: PlaceOrderCommand) {
const order = Order.create(cmd.items, cmd.customerId);
await eventStore.append(order.id, order.pullEvents());
await eventBus.publish('OrderPlaced', { orderId: order.id });
}
// Read model: optimized projection (separate DB or materialized view)
async function getOrderSummary(orderId: string) {
return readDb.orderSummaries.findOne({ orderId });
}
Event Sourcing: State as a Sequence of Facts
Event sourcing stores the authoritative state as an append-only log of events rather than the current snapshot alone. Replaying events reconstructs any point-in-time view, a powerful capability for debugging, compliance, and temporal queries ("What did this account look like on March 1?").
The trade-off is complexity: you must handle schema evolution of events, idempotent consumers, and eventual consistency between write and read models. CQRS without event sourcing is valid; event sourcing without careful operational discipline is painful.
When EDA Is the Right Call
- High write throughput with diverse read surfaces: e-commerce catalog search vs. checkout vs. fulfillment each need different shapes of data.
- Audit and compliance: finance, healthcare, and logistics often require immutable trails.
- Integrating bounded contexts: billing, notifications, and inventory can evolve independently if they react to events.
Start with clear bounded contexts, define event contracts with versioning, and introduce projections incrementally. Event-driven architecture is not a default. It is a deliberate scaling tool when synchronous CRUD becomes the bottleneck.