Event-Driven Architecture, CQRS & Sagas
The shift from request/response to event-driven architectures is a paradigm change. As systems grow in complexity and scale, traditional synchronous communication becomes a bottleneck β pushing architects toward more resilient, scalable, loosely coupled designs.
Event-Driven Architecture
Event-driven architecture (EDA) treats services as reactive systems responding to events rather than direct commands. When a user completes a purchase, instead of the orders service calling inventory, payment, and notification services directly, it publishes an OrderCompleted event. Other services subscribe to events they care about and react independently.
Benefits
Costs
CQRS: Command/Query Responsibility Segregation
When microservices use EDA, traditional CRUD operations often become a bottleneck. CQRS separates read and write operations, often using different data models for each.
In an e-commerce system: a Command Service optimized for fast order placement, plus separate Read Services optimized for customer order history, analytical reporting, and real-time inventory display. Each read service maintains its own data model perfect for its query patterns.
Event Sourcing: Events as Source of Truth
Event sourcing goes further: store events as the source of truth, rather than current state. Instead of βJohnβs account balance is $500,β you store βJohn deposited $1,000, then withdrew $500.β The current balance is derived by replaying events.
Synergy with EDA
Event sourcing naturally generates events that drive cross-service communication. CQRS lets each service build its own optimized read view from the events of others. One business transaction generates events consumed by many services, each maintaining its own optimized data β eliminating cross-service joins while keeping data consistent through eventual-consistency patterns.
Sagas: Distributed Transactions Without 2PC
In a monolith, you can use database transactions for consistency. In microservices, data is distributed across multiple databases owned by different services. Two-phase commit doesnβt work at scale. Sagas are the pattern.
A saga is a sequence of local transactions where each service publishes events or sends commands to trigger the next step. If any step fails, compensating transactions undo the work of completed steps.
E-commerce example
If the payment fails: the saga triggers compensation. Order Service un-reserves the stock, Shipping Service cancels the delivery.
Two implementation styles
Event Streaming as Architecture
Platforms like Apache Kafka unlock new architectural patterns. Services publish events to streams; other services consume from them. Events become the nervous system of your architecture, carrying business events that any number of services can consume.
This creates natural extension points: new features = new consumers of existing event streams, without modifying existing systems. It also enables real-time analytics β instead of batch-processing data warehouses, dashboards update in real time as events flow through.
Real-World Patterns
A typical multi-tenant SaaS event flow:
[ Order Service ]
β publishes OrderCreated
βΌ
βββββββββββββββββββββββββββββββββββ
β Kafka topic: orders.events β
βββββββ¬ββββββ¬βββββββ¬βββββββββββββββ
β β β
βΌ βΌ βΌ
Inventory Email Analytics
Service Service Service
(reserves) (sends) (records)
Each consumer is independent. Add a new fraud-detection service tomorrow β attach a new consumer; no producer changes. Onboarding a new tenant might mean attaching new consumers without touching anything upstream.
When to Use This
EDA isnβt free. It pays off when:
- You have multiple consumers of the same state changes (including consumers you havenβt built yet).
- You need to scale services independently with very different load profiles.
- Audit trail and compliance are hard requirements.
- You need to decouple teams shipping in different rhythms.
Itβs overkill when:
- You have one service producing and one consuming.
- Strong consistency is the dominant requirement.
- Your team doesnβt have the operational maturity to run Kafka or equivalent reliably.
Recap
- EDA shifts from βtell others what to doβ to βtell others what happenedβ β producing loose coupling and natural extensibility.
- Costs: ordering, duplicate delivery (idempotency!), schema evolution, eventual consistency.
- CQRS separates read and write models for optimized query and command paths.
- Event sourcing stores events as source of truth; current state is derived.
- Sagas handle distributed business transactions through compensating actions; choose choreography or orchestration based on saga complexity.
- Event streaming (Kafka, etc.) becomes the nervous system of modern multi-tenant SaaS β enabling real-time processing and natural extensibility.