2025-10-05
In a traditional request-response architecture, services call each other directly. Service A sends a request to Service B and waits for a response. This creates tight coupling. If Service B is slow or unavailable, Service A is affected. Event-driven architecture takes a different approach by having services communicate through events.
Instead of calling another service directly, a service publishes an event when something significant happens. Other services that care about that event subscribe to it and react accordingly. The publisher does not know or care who the subscribers are.
For example, when a user places an order, the order service publishes an OrderPlaced event. The inventory service listens for it and reserves the items. The notification service listens for it and sends a confirmation email. The analytics service listens for it and records the sale. Each service handles its own concern independently.
An event broker (or message broker) sits between publishers and subscribers, handling the delivery of events. Popular options include Apache Kafka, RabbitMQ, and cloud services like AWS EventBridge or Google Pub/Sub.
Kafka is well-suited for high-throughput systems where events need to be retained and replayed. It stores events in an ordered log, and consumers can read from any point in the log. This makes it possible to reprocess historical events when adding a new service or fixing a bug.
RabbitMQ is more focused on traditional message queuing where messages are consumed and acknowledged. It supports complex routing patterns and is simpler to operate for smaller-scale use cases.
There are two common patterns for what an event contains.
Event notification carries minimal data, just enough to tell subscribers that something happened. The subscriber then queries the source for details if needed. This keeps events small and avoids coupling subscribers to the publisher's data model.
{
"type": "OrderPlaced",
"orderId": "abc-123",
"timestamp": "2025-10-05T14:30:00Z"
}Event-carried state transfer includes the relevant data in the event itself. Subscribers have everything they need without making additional calls. This reduces inter-service communication but means subscribers might receive more data than they need.
Loose coupling is the primary benefit. Services can be developed, deployed, and scaled independently. Adding a new subscriber does not require changes to the publisher. If a service goes down temporarily, events wait in the broker and are processed when the service recovers.
This architecture also enables better scalability. Each service can scale based on its own load rather than being constrained by the slowest service in a synchronous chain.
Event-driven systems are harder to reason about than synchronous ones. The flow of a request is not visible in a single call stack. Instead, it is spread across multiple services reacting to events asynchronously. Debugging requires good observability, including correlation IDs that link related events together.
Eventual consistency is another consideration. Since events are processed asynchronously, there is a window where different services may have different views of the current state. This is acceptable for many use cases, but for operations that require strong consistency, a synchronous approach might still be necessary.
Ordering and idempotency also need attention. Events might arrive out of order or be delivered more than once. Services should be designed to handle duplicate events gracefully, usually by checking whether the event has already been processed before applying its effects.