.netoopdllarchitecturedependencies

Project / dll architecture to avoid circular references but maintain modularity


Let's say I have two dll projects: A1.dll and B1.dll. Let's say that B1.dll references A1.dll. Now let's say that I want to add functionality similar to what is already in A1.dll, but because the added functionality must reference B1.dll, I assume I have to make A2.dll. (If I just add this functionality to the exitsing A1.dll, I will cause a circular reference, which I believe should be avoided.)

So now I have A1.dll, B1.dll that references A1.dll, and A2.dll that inherits from A1.dll and also references B1.dll. We want A2.dll to Inherit A1.dll so that all of the "A" functionality is referenceable from A2.dll. All is fine. If I now want to add functionality to B1 and if that new functionality must reference A2, then I assume I have to make a B2.dll that inherits from B1.dll and references A2.dll. This could go on for many, many levels of reference. Is this the right approach or am I missing something?

And yes, I know I can throw all the functions into a single dll, but I am trying to organize my projects by major function (A versus B) so that there is some modularity when it comes to maintenance.

Any help would be appreciated.


Solution

  • As you rightly noted, cyclic dependencies should be avoided. This is confirmed by the acyclic dependencies principle (ADP) formulated by Robert C. Martin:

    The dependency graph of packages or components should have no cycles.

    To break the cyclical dependency, two strategies are usually used:

    1. Isolate a new component (library) with common functionality for two components (you have already mentioned the corresponding solution in your question).

    enter image description here

    1. Apply the dependency inversion principle (DIP).

    enter image description here

    As an example, I gave an extreme example (similar to the one you gave in the comments to your question) in which we have two classes dependent on each other. As a result of the solution (extracting the interface B and placing it in Component 1), our dependencies were directed from Component 2 to Component 1.