Over a million developers have joined DZone.

Events Don’t Eliminate Dependencies

DZone's Guide to

Events Don’t Eliminate Dependencies

Learn about how using event-driven architectures can be great, but they don't get rid of dependencies and coupling.

· Java Zone ·
Free Resource

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

Event (or message) driven systems have some benefits. I’ve already discussed why I think they are overused. It has two flavors, which you can read about in this architectural patterns books. But none of these is what I’m going to write about.

I’m going to right (very briefly) about “dependencies” and “coupling”. It may seem that when we eliminate the compile-time dependencies, we eliminate coupling between components. For example:

class CustomerActions {
  void purchaseItem(int itemId) {
    purchaseService.makePurchase(item, userId);

Compared to:

class CustomerActions {
  void purchaseItem(int itemId) {
    queue.sendMessage(new PurchaseItemMessage(item, userId));

It looks as though your CustomerActions class no longer depends on a PurchaseService. It doesn’t care who will process the PurchaseItem message. There will certainly be some PurchaseService out there that will handle the message, but the former class is not tied to it at compile time. Which may look like a good example of “loose coupling”. But it isn’t.

First, the two classes may be loosely coupled in the first place. The fact that one interacts with the other doesn’t mean they are coupled – they are free to change independently, as long as the PurchaseService maintains the contract of its makePurchase method

Second, having eliminated the compile-time dependencies doesn’t mean we have eliminated logical dependencies. The event is sent, we need something to receive and process it. In many cases that’s a single target class, within the same VM/deployment. And the wikipedia article defines a way to measure coupling in terms of the data. Is it different in the two approaches above? No – in the first instance we will have to change the method definition, and in the second instance – the event class definition. And we will still have a processing class whose logic we may also have to change after changing the contract. In a way, the former class still depends logically on the latter class, even though that’s not explicitly realized at compile time.

The point is, the logical coupling remains. And by simply moving it into an event doesn’t give the “promised” benefits. In fact, it makes code harder to read and trace. While in the former case you’d simple ask your IDE for a call hierarchy, it may be harder to trace who produces and who consumes the given message. The event approach has some pluses – events may be pushed to a queue, but so can direct invocations (through a proxy, for example, as spring does with just a single @Async annotation).

Of course that’s a simplified use-case. More complicated ones would benefit from an event-driven approach, but in my view these use-cases rarely cover the whole application architecture; they are most often better suited for specific problems, e.g. the NIO library. And I’ll continue to perpetuate this commons-sense mantra – don’t do something unless you don’t know what exactly are the benefits it gives you.

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

java ,dependencies

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}