Event-Driven Architecture
In an event-driven architecture, services don't call each other. They emit events and react to events. Looser coupling, more resilience, and a different way of thinking about state.
From requests to events
The traditional way: when something happens, you call the next service directly. The order service calls the payment service, which calls the inventory service, which calls the shipping service. A chain of synchronous calls. If any link breaks, the whole flow fails.
The event-driven way: when something happens, you emit an event. OrderPlaced. Other services that care subscribe and react. The order service doesn't know who is listening. It doesn't wait. It moves on.
The shift in thinking
Events describe facts: "user signed up", "payment captured", "shipment dispatched". They are immutable. They happened. Other services consume them, possibly storing their own derived state.
This is a deep shift. In the request world, you have one shared truth in a database. In the event world, each service has its own view of the world built from events. Loose coupling at the cost of harder reasoning.
Choreography vs orchestration
Choreography. Each service listens for events and reacts. No central coordinator. Decentralized, resilient, but the overall flow is hard to see in any one place.
Orchestration. A central workflow service tells each step what to do. Easier to visualize and debug. Re-introduces coupling to the orchestrator. Tools: Temporal, AWS Step Functions, Camunda.
Most real systems use a mix. Choreography for natural domain events; orchestration for complex multi-step business processes (especially those that need rollback).
Event sourcing (briefly)
A specific style of EDA: store all changes as events, never overwrite. The current state is derived by replaying events. Powerful for audit, time-travel, and debugging. Heavy in terms of complexity. Pick deliberately.
CQRS (Command Query Responsibility Segregation)
Often paired with event-driven design. Writes (commands) and reads (queries) take separate paths. Writes go through a strict, validated model. Reads come from optimized projections built from events. Lets read and write models evolve independently.
What can go wrong
- Eventual consistency confusion. User just placed an order; refresh; order isn't there yet (still propagating). UI must handle this.
- Debugging is harder. The flow is no longer one stack trace. It's many services reacting to many events. Distributed tracing matters.
- Schema drift. Events outlive code. Consumers must handle old event shapes for years. Use a schema registry.
Event-driven design is a powerful pattern for building resilient, loosely coupled systems. Used well, it makes new features easier to add (just subscribe to existing events). Used everywhere, it turns a coherent system into an unstructured pile of events. Apply with judgment.