5

I came accross this question while designing a multi module application that communicates with events. All modules are loosely coupled and are not dependant on each other. They use common event-types to communicate with each other.

The question is, where to put the common event types in this scenario? Let's assume we are using classes as event types. To communicate, the sender and also the receiver needs to know how the type is structured - so they need some sort of common dependency.

What are the advantages / disadvantages of the two screnarios:

  1. Put the event into the module that contains the sender
  2. Create a common module that contains all of the events
  • 1
    I think we can give a proper answer to the question because there's so few info about the architecture and the design. Pros and cons of each approach depend on how is implemented right now and how it should be accordingly to the requirements. IMO both approaches generate coupling among modules through this common module. If it was a monolith app I wouldn't care. However, It would be totally different if we were speaking about microservices, plugins, add-ons, etc.. – Laiv Dec 23 '16 at 16:12
  • Laiv is right on. The second scenario is more flexible. The first is simpler. Choose the simplest solution you can afford. – Frank Hileman Dec 25 '16 at 19:31
1

Create a common module that contains all of the events

I would go this way. Putting the event into the sender/receiver module will create a coupling between sender and receiver which effectively kills the idea of modular design. And you might want to avoid that.

1

In rationalizing about this, I'd suggest that we first have to acknowledge that in real world systems, some coupling is totally normal. Although you typically want this to keep this on a higher abstractional level and avoid coupling on implementation.

For example, you might have a database in your system. You are not going to be able to remove the coupling of the database from all parts of the rest of the system, some other components will have to be aware of that you have a database, because they will have to interact with it is some way, either implicitly or explicitly. Your abstract components in your system can simply not live in total isolation.

You might have stakeholders that would benefit from the ability to switch out the database implementation easily, like going from MSSQL to MySql, so then you'll have to make sure abstract the database properly and keep it's implementation details hidden so that the system is not coupled to a specific database engine. And in contrast, sometime your best choice can be to choose a particular database engine and expose it fully in the system, like if you have performance requirements that overweight the requirements of modularity.

Coupling is normal, and we should reason about where to put it in the system, not solely on how to avoid it.

And when we have coupling, we should put it where it belongs contextually without trying to indirect it away.


Do you have a view component that triggers something like UserWantsToSeeDetailsForObject(id)? That's something that easily can be seen to contextually belong to the abstraction of the application itself, so I'd put that in something like a "ApplicationEvents"-class. It makes sense that many different components in your application could trigger this.

Do you have a component like NetworkAnalyzer that checks your network connectivity and triggers event like "Reconnected", "Disconnected", "Unstable" and such? Those kinds of events would not make sense to trigger somewhere outside of NetworkAnalyzer, so just avoid some indirection and place it where it belongs: Inside that very component.

Do you then get the need to implement different implementations of the NetworkAnalyzer? Put the event definition in the base class.

Do you then discover that your system not only have to be aware of network connectivity but also about things like available memory, cpu resources and temperature? Then you'll might realize that you have a new abstractional context that you could name something like "EnvironmentTelemetry", where it makes sense to collect those event definitions.

TL;DR

Does the event have a clear owner? Just put it with the owner and avoid unnecessary indirection.

Does the event make sense in the application without what very component that you just wrote? It probably belongs to the application in it's whole, so place it in a shared structural construct (e.g. an ApplicationEvents-class).

Do your specific event contextually belong with a certain group of other events, not on a application level but above a individual component? Consider creating a class containing those specific events.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.