Event-Driven Architecture
Event-driven systems communicate via asynchronous events, enabling loose coupling and independent scaling of producers and consumers.
What is event-driven architecture?
In event-driven architecture (EDA), services communicate by producing and consuming events rather than making direct synchronous calls. A producer publishes an event when something happens; consumers react to those events independently.
Core Patterns
Producer-Consumer
One producer writes to a queue; one consumer reads and processes.
- Decouples producer from consumer
- Consumer processes at its own pace (load levelling)
- If consumer is down, messages queue up and are processed when it recovers
Pub/Sub (Publish-Subscribe)
One producer publishes to a topic; multiple consumers subscribe independently.
- Each consumer gets a copy of every event
- Consumers can be added without changing the producer
- Used for fan-out notifications, event broadcasting
Choreography vs Orchestration
Choreography
Services react to events without a central coordinator. Service A emits OrderCreated; Service B (inventory) reacts by reserving stock; Service C (notifications) reacts by sending email. No one orchestrates the flow.
- ✅ Loose coupling - services don't know about each other
- ✅ Easy to add new consumers without changing existing services
- ❌ Hard to trace and debug end-to-end flows
- ❌ Business process logic is distributed across services
Orchestration
A central orchestrator (coordinator service or workflow engine) tells each service what to do and collects responses.
- ✅ Clear visibility into process flow
- ✅ Easier error handling and rollback
- ❌ Orchestrator is a coupling point and potential bottleneck
- ❌ Reduces autonomy of individual services
Loose Coupling Tradeoffs
EDA's greatest strength is also its greatest challenge:
Benefits of loose coupling:
- Services can be deployed, scaled, and changed independently
- Adding new consumers doesn't require modifying producers
- Services can be written in different languages
Costs of loose coupling:
- End-to-end request tracing requires distributed tracing (correlation IDs)
- Debugging event flows requires good tooling
- Eventual consistency: downstream state may lag after an event is published
- Event schema changes require careful versioning (consumers may not be updated simultaneously)
Eventual Consistency Implications
When service A processes an order and emits OrderShipped, service B (notifications) may process the event 100ms later. During that window, the notification hasn't been sent yet. The system is eventually consistent - but any component that queries "has the notification been sent?" immediately after the order ships may see inconsistent state.
Design UIs and downstream logic to tolerate this:
- Show "Your order is being processed" rather than assuming instant propagation
- Use idempotent consumers (process the same event multiple times safely)
Event Schema and Versioning
Events are the API between services. Breaking changes to event schemas break consumers. Strategies:
- Add new fields as optional (backward compatible)
- Never remove or rename fields (version the event instead)
- Use
event_versionfield: consumers handle each version explicitly - Maintain event schema registry (Confluent Schema Registry for Kafka)
Technologies
| Technology | Type | Use When |
|---|---|---|
| Apache Kafka | Log-based streaming | High throughput, replay, event sourcing |
| RabbitMQ | Message broker | Traditional queuing, routing |
| AWS SQS + SNS | Managed | AWS-native, simple setup |
| Google Pub/Sub | Managed | GCP-native |
| Azure Service Bus | Managed | Azure-native |
Interview Angle
For any system with asynchronous workflows (order processing, user onboarding, notifications), propose EDA. Demonstrate understanding of choreography vs orchestration tradeoff, eventual consistency, and idempotent consumers. Mentioning event schema versioning shows senior-level thinking.