3

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.

6
  • 1
    Yes is the answer. At a certain point you need to make a decision about what methods go where. You need to have a reason to split your code into differnt dlls and reusability is the core one. Just make sure you know why you need more than 1 dll. Do not have more than 1 simply because you think it might be the right thing to do. Commented Dec 22, 2024 at 4:54
  • 1
    Please give a specific example: what functionality is in your libraries and what do they reuse from each other. Commented Dec 22, 2024 at 16:39
  • @TobyAllen Thank you for responding. I have started splitting a single dll project with 30 to 40 classes in it, into separate dll projects and separate solutions. This is not easy! Commented Dec 22, 2024 at 23:24
  • @nik0x1 I will - I am in the process of splitting a single dll project with 30 to 40 classes in it into separate dll projects and separate solutions. As part of that process I will definitely encounter what I described (or not if I am full of stuff)! When/if I do, I will show it here. Bare with me. Commented Dec 22, 2024 at 23:25
  • @nik0x1 Ok, so here's the first example: Class "Resource" - mostly a class carrying shared/static information. "Resource" wants to carry the instance of the "AppLog" class associated with the App. There are many more references (than shown here) to m_AppLog in "Resource". Public Class Resource Private Shared m_AppLog As SEDLAK.AppLog = Nothing Public Shared Property AppLog() As SEDLAK.AppLog Get Return m_AppLog End Get Set(ByVal value As SEDLAK.AppLog) m_AppLog = value End Set End Property End Class Commented Dec 23, 2024 at 14:03

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

9 Comments

Well, I tried strategy 1 and learned that this way makes it very difficult to keep track of the order of builds on the different projects in the different solutions. In strategy 2, what does "Interface B" residing inside of "Component 1" represent in code / structure? Does it mean that "Interface B" should be coded inside of the project for "Component 1"? Also, in your diagram is A Implementing "Interface B" or is B Implementing "Interface B"?
Hello @BandannaMan, A and Interface B inside Component 1, B inside Component 2. A depends on Interface B; B implementing Interface B.
Thank you. Yes, I have A referencing Interface B in one solution, and B Implementing Interface B in a separate solution. Anything wrong with placing Interface B is a separate solution and project dll?
@BandannaMan You can move the Interface B to Component 3, which will also solve the problem with the cycle.
Using an Interface helps if I don't have to instantiate the object, only refer to it. But what if I have to instantiate the objects? In the following, If I make an Interface for either AppSettings or Totem, or both, I still have to instantiate the other, which I cannot do with an Interface. Public Class AppSettings Public Function Foo Dim T1 as Totem = New Totem() ... End Function End Class Public Class Totem Public Function F002 Dim A1 as AppSettings = New AppSettings() ... End Function End Class
|
0

You should, indeed, avoid circular references. This is known as the Acyclic Dependencies Principle (ADP). You can read more about it in APPP by Robert C. Martin.

That book is one of many that go into details about this kind of question related to object-oriented architecture.

One of the most effective ways to attain the goal of the ADP is another principle, the Dependency Inversion Principle (DIP), which says that implementation details should depend on abstractions, and not the other way around.

Robert C. Martin has written extensively about the DIP and other topics of decoupling of architectures. So have other people. Another good fundamental resource is Alastair Cockburn's Hexagonal architecture, also known as Ports and Adapters.

If you want to read more about Ports and Adapters in a C# context, one place to start is my article Layers, Onions, Ports, Adapters: it's all the same.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.