Event-Driven Architecture Patterns
Event-driven architecture uses events as the primary way systems communicate. Instead of services calling each other directly, they publish events that other services react to. This decoupling enables systems that scale better and fail more gracefully.
Event Notification
The simplest pattern: when something happens, publish an event. Interested services subscribe and react:
Order Service publishes: "OrderCreated"
Inventory Service: reserves items
Email Service: sends confirmation
Analytics Service: records the sale
Each service operates independently. If the email service is down, orders still process. The email sends when the service recovers.
Event Sourcing
Instead of storing current state, store the events that led to that state:
Traditional: User { balance: 150 }
Event Sourced:
AccountOpened { initial: 100 }
Deposited { amount: 100 }
Withdrawn { amount: 50 }
→ Current balance: 150
You can rebuild current state by replaying events. You get a complete audit trail. You can ask "what was the balance last Tuesday?" by replaying events up to that point.
Event sourcing is powerful but adds complexity. Use it when audit trails matter or when you need to understand how state evolved.
CQRS: Command Query Responsibility Segregation
Separate your read and write models:
Commands (writes) → Write Model → Events → Read Model ← Queries (reads)
The write model is optimized for consistency and business rules. The read model is optimized for query performance — maybe denormalized, maybe in a different database entirely.
This lets you scale reads and writes independently and optimize each for its purpose.
The Saga Pattern
Distributed transactions across services are hard. Sagas break them into steps, each publishing an event for the next:
1. Order Service: CreateOrder → publishes OrderCreated
2. Payment Service: ProcessPayment → publishes PaymentCompleted
3. Inventory Service: ReserveItems → publishes ItemsReserved
4. Shipping Service: ScheduleShipment → publishes ShipmentScheduled
If step 3 fails, compensation events undo previous steps: RefundPayment, CancelOrder. It's eventual consistency rather than immediate, but it works across service boundaries.
Handling Failures
Events can fail to process. Design for this:
- Idempotency: Processing the same event twice should be safe
- Dead letter queues: Failed events go somewhere for investigation
- Retry policies: Automatic retries with backoff