TransWikia.com

Modular programming: module inter-dependency

Software Engineering Asked by TOPKAT on October 29, 2021

I’m looking to make a clean modular architecture. I hear all around how bad are circular dependencies, and I’m convinced that the less two modules are coupled, the most reusable the code will be.
But I want to handle edge cases where moduleA depends on moduleB and vice-versa.

Generally speaking, an architecture like this doesn’t sound bad to me (very simplified):

enter image description here

All modules "register" themselves at startup into the bridge service. A module can have different interaction with all other modules:

  • ẁe can do bridgeService.hasModule('moduleName') to create "weak" dependencies
  • we can call a function of a module like bridgeService.moduleName.myFunction()
  • the bridge service can handle errors and virtualize the dependencies map of the application
  • It may happen that moduleA calls moduleB and moduleB => moduleA but it wont be a circular dependency except if functionA calls functionB and in return fnB calls fnA. In that particular case, javascript throw an explicit stack size exceed which is easy to fix.

I have a minimalist example that work well and I like the way it handles modularity.
The problem is I never found an example of such an architecture in literature, that’s why I’m not so confident even if everything looks fine to me.

Is it a bad design ? What are the drawbacks or the positive about such a design ?

Thanks for your answers

NOTE: I’m working with nodeJs, thus I don’t have to deal with compilation issues.

2 Answers

In short

It looks nice. But in reality, you just shift the problem, transforming inter-module dependencies in an interface segregation issue, that might ultimately end-up in object-oriented spagetthi-code.

More details

At first sight, it seems that you have reinvented some variant of the mediator pattern:

Objects no longer communicate directly with each other, but instead communicate through the mediator. This reduces the dependencies between communicating objects, thereby reducing coupling.
- Wikipedia

The mediator is allowed to have a more specialised communication with the colleagues, using a more detailed interface than the colleague interface. So at first sight, your approach seems to implement a robust pattern.

However, the mediator aims at encapsulating how objects interact between them, and therefore hide unnecessary details about the colleagues to other colleagues. And here is the weakness of your variant: Your bridge exposes the full interface of each module. This is a real problem in view of interface segregation and dependency management:

  • Suppose you want to add a new function to module C: You'd need to provide this function also in the Bridge (change propagation) and inflate the bridge interface. But now all other modules mus now the new interface (ISP issue), and they might even use it via the brigde, even if they are not supposed to.
  • Suppose you want to change some parameters of a function in module C: you'll need to change the bidge interface (change propagation). But then you have to fin all the modules using the bridge, and see which one uses that function to change it as well (change propagation).

In the end, any change in any module might affect all the others. That's the exact contrary of decoupling. I'd call this object oriented spagetthi-code.

Answered by Christophe on October 29, 2021

It looks to me you created a synchronous messaging system. If you make BridgeService a queue you will have something very common and proven, it will be asynchronous and the decoupling will be even better.

[Edit in response to comment]

Software components that communicate with each other through message queues is a common pattern, if you want to call it that. The queue can be an object in a single process (for in-process communication) or a database table on a separate server for inter-system communication, or something in between.

This is asynchronous by nature, the "caller" puts a message in the queue and the service will read and process the message when it gets around to do it. It may then respond by posting a message back, in a queue that is to be read by the caller. The caller can wait for that message, effectively making it synchronous again from its perspective, or rely on an event mechanism to get the results, if there are any.

All of the internet works that way because the IP protocol is a message protocol. Whether this model is a good fit for your application is for you to decide. It may not be the most convenient option, it requires a lot of plumbing which could be overkill.

Answered by Martin Maat on October 29, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP