15

Our current project has ran into a circular dependency issue. Our business logic assembly is using classes and static methods from our SharedLibrary assembly. The SharedLibrary contains a whole bunch of helper functions, such as a SQL Reader class, Enumerators, Global Variables, Error Handling, Logging and Validation.

The SharedLibrary needs access to the Business objects, but the Business objects need access to SharedLibrary. The old developers solved this obvious code smell by replicating the functionality of the business objects in the shared library (very anti-DRY). I've spent a day now trying to read about my options to solve this but i'm hitting a dead end.

I'm open to the idea of architecture redesign, but only as a last resort. So how can i have a Shared Helper Library which can access the business objects, with the business objects still accessing the Shared Helper Library?

gfoley
  • 175
  • 1
  • 2
  • 7
  • 3
    The obvious question is: Why does the Shared Library need access to the Business Objects? If you can answer that, you'll have a solution. – Aaronaught Apr 07 '10 at 22:58
  • The SharedLibrary has a abstract global variable class filled with static properties. These properties are created from values from the database hence the need for business objects, this is just one example of many. And of course the Business objects need access to those constants. – gfoley Apr 07 '10 at 23:05
  • 2
    This is why I never use vague terms like "shared" to describe a library. What does it actually do? What you call a shared library clearly has far too many responsibilities, and perhaps the business object library does too. Typically, these solutions are solved by putting the truly *independent* classes/interfaces into their own library. – Aaronaught Apr 07 '10 at 23:23

3 Answers3

25

You could create a separate project only for value objects (no logic) and interfaces.

Have your shared library classes implement the interfaces, and the Business library depend on the interfaces (do I hear more testable and decoupled code here? Not to mention you remove the dependency from the Shared Library).

Ideally, you could have the business objects on which your shared library depend on this extra project too. If the business objects are too complex, you could also transform them into interfaces.

You will have both projects not depending on each other, but only on another project with only "dummy" objects (no logic):

Business ---> Interfaces and value objects <--- Shared Library

Now those are decoupled =)

Samuel Carrijo
  • 17,449
  • 12
  • 49
  • 59
  • If you're going to go this far to architect away the dependency why stop 5 feet short of the finish line and not just architect away all of it? At that point you'd have no more code that's dependent on the business entities and can leave the core truly shared code in the shared project. Which would be my answer. – Chris Marisic Apr 07 '10 at 23:17
  • @Chris That's not a huge change. That will require some refactoring, but no big redesign or need to remodel stuff. – Samuel Carrijo Apr 07 '10 at 23:23
  • OK, so i understand the concept here but i'm struggling to understand how to implement it. So if SharedLibrary Referenced BusinessObjects and both reference the Interface Layer then i would do, eg: //sharedlibrary public abstract class MSSqlReader{ // various methods } //interface layer public interface IMSSqlReader {} //business object IMSSqlReader obj = new IMSSqlReader(); Of coarse this will not work, obj doesn't have access to the MSSqlReader. How can BusinessObject get access to objects in the SharedLibrary using interfaces? – gfoley Apr 08 '10 at 02:00
  • I think you should brush up your general SOLID design knowledge http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod this case specifically the I which is Interface segregation principle. You will most likely also want to implement this using D which is Dependency Inversion portion. – Chris Marisic Apr 08 '10 at 13:53
  • 1
    @gfoley As Chris mentioned, here dependency injection is key. You shouldn't need new IMSSqlReaderImpl(), but rather, get it in the constructor and simply use it in your class – Samuel Carrijo Apr 08 '10 at 16:09
  • Alright i understand that better now, thanks. But for my situation it still won't work, So i have a abstract class with static properties in the shared library(SL) (global variables), but the SL can't access the business logic. So I could create and interface for the business object but then every time i want one of my static properties i would have to instantiate my business object and pass it in. Hardly seems practical. I'm starting to think that anything i have in the SL which is dependant on busonessobjects is in the wrong place, and should be an object in the business library. – gfoley Apr 09 '10 at 05:05
  • @gfoley seems like you have some global state. Please tell me these properties don't change over time. If that's true, you just need to inject them once. If that's not true, then you'll probably have to deal with the ghost of global state and mysterious bugs every so often – Samuel Carrijo Apr 09 '10 at 15:38
  • Amazing solution, I love it. – Pavel Matuska Mar 05 '13 at 10:30
3

Anytime you have a "shared" library you absolutely must not ever reference your business entity project. Doing so will cause this issue to happen as you obviously see.

The solution to this is remove all of the business entity dependent code from the shared library and either rearchitect away the need for that helper code or place that helper code inside the business entity project itself.

Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
1

One solution would be to put a facade pattern in between. Here you would avoid direct access/dependency to your business objects from the shared library. Instead, you would be using a layer that acts as a facade between your lib and BOs. This way you can pack your shared libraries clean and decoupled.

bragboy
  • 34,892
  • 30
  • 114
  • 171
  • 1
    I'd just say 'abstraction' without specifying concrete pattern name (it could as easily be adapter, strategy, bridge) because design patterns main intent is not in solving issues like this one. – Artem Govorov Apr 07 '10 at 23:32