Thoughts On Event-Driven Architectures
Understanding the basics of EDA and its downsides
I was recently pondering about event-driven architectures, their benefits, downsides, and most common use-cases.
We’re all familiar with different communication styles between micro-services, where the two notable ones are: synchronous request-response calls and asynchronous interactions, based on messaging and event-driven communication.
So, the most obvious question we can ask ourselves is what communication style to apply in what scenarios?
In order to answer this question, let’s briefly recap what event-driven-architectures are.
Event-Driven-Architecture Basics
Asynchronous messaging and event-driven communication are commonly used when propagating changes across multiple micro-services.
In most simple terms, when a micro-service “A” performs an action that micro-service “B” is potentially interested in, it publishes an event to the message broker. Following the well-known pub-sub design pattern, micro-service “A” is a publisher and micro-service “B” is a subscriber that processes the event once received from the broker.
To make things a little bit more explicit, most of the message brokers support two modes of messages delivery: queue and pub/sub. The difference is that with queuing, events are processed by one consumer and with the pubs/subs, multiple consumers can subscribe to specific types of events and consume them as necessary.
The diagram below from AMQ/JMS tutorial by Richard Monson-Haefel depicts those differences:

Before we jump into a discussion about the benefits and downsides of event-driven architectures, let’s define what “event” is and how it’s different from “command” for example.
In his wonderful talk about the meaning of event-driven-architectures, Martin Fowler gives an example of “customer management” service that interacts with the downstream “insurance quoting” service to trigger the re-quoting process, either by calling it directly or by emitting “Address Changed” event as shown below:


And the interesting question Martin raises is when do we have events or commands?
In the case of “ command” language, the intent could be that “customer management” service instructs “insurance quoting” service to re-quote insurance. This means that the “customer management” system has the knowledge of what should be done and it is explicitly telling the “quoting” system what to do.
When we frame it as an “ event”, the semantics change: the “customer management” system notifies about the “address change” and does not necessarily expect any response.
So it boils down to our need to accurately describe how the system works by correctly naming it and then translating it to the correct communication style.
As we can see, events-based communication has many potential benefits:
- Extensibility — we can easily hook up additional services by listening to the
addressChanged
event without the “customer management” service knowing about it. This is a very attractive ability, especially if the service we want to integrate with is developed by another group, department, or even company. - Elasticity — we can adjust system throughput by adding or removing producers and consumers to handle the required load. This capability could be crucial for scalability and cost-effectiveness.
- Resilience: if an event consumer temporarily stops working, the event could be kept and re-consumed when the consumer gets back operating. In a synchronous request-reply case, we need to develop comprehensive “retry” mechanisms to make it happen, while in event-driven cases, most of the events brokers provide this functionality out-of-the-box.
- Analytics: since interactions between services are generated and recorded as “events”, we can add logging at the places of interest and extract useful insights from it. I remember a case where my team and I have added a simple log message with each event, later submitted and processed by Logz.io system to provide us system usage statistics and business intelligence.
- Consistency: we can persist a stream of events and reconstruct the latest system’s state by “replaying” events from the past. See the “Events Sourcing” pattern here.

All of us are intimately familiar with event-sourcing based systems — version control :)
You can find here a really interesting discussion about it.
Event-Driven-Architectures (EDA) Downsides
All right, now that we have some understanding of what EDA are, let’s talk about the trade-offs and downsides we should keep in mind when we opt to use them.
- First, from my experience with relatively large event-driven system I’ve designed in the past, it’s difficult to understand what is going on in the system. This is the side-effect of loose dependency between the services that use event notifications: you actually need to watch over the event and the system behaviour, digging into the flows that are triggered as a result of events consumption and their processing. And that makes it very difficult to understand the system by just reading the code.
- Another thing that EDA might make more difficult, is managing the overall system state. There are cases, where we can choose to carry the state of service “A” to service “B” in each event notification, so that service “B” could replicate it, to avoid the need to additional communication between them. This replication has many benefits, such as improved performance, system throughput and its availability.

And that of course may force us to deal with data consistency problems, conflicts resolutions, and other data-related complexities.

- Now, the next thing I’d like us to think about is how we actually structure our events. We have this tension of being generic enough on one side and on the other side we want them to convey as much information and context as possible. In the case of “Events Sourcing”, we need to be able to rebuild the application state by replaying the events we persisted in, which in turn demands them to contain information, necessary for such “replaying”.
- What else could go wrong you ask? Events modifications and versioning. When we change a method name or refactor it by changing its signature, we’re forced to go over all places that reference the same method and change it. That’s not the case with events. With events, we need to start looking for all subscribers to the relevant queues and topics and understand one-by-one if they have been affected by the same change. This is especially challenging in “Events Sourcing” based systems, where replaying events with different versions may make things really hairy.
- Lastly, I’d like to talk about synchronous flows. My conclusion from using event-driven architecture in a real-time application (command & control system), is that when you have a flow of method calls, spread across different services, and you really want to make sure that the flow behaves in “all or nothing” fashion, facilitating this synchronous behavior will be really difficult. In those cases, we can turn to the regular REST or gRPC calls and combine them with the event-driven parts of the system.
I’ll conclude and say that as with any other pattern, we should be careful and use event-driven architectures only if it fits the nature and the requirements of the problem at hand.
Otherwise, we might end up with the “Law of Instrument” trap which states that:
If you only have a hammer, you tend to see every problem as a nail”
-Abraham Maslow