2

My code base makes pretty heavy use of interfaces/abstractions, factories, repositories, dependency injection and other design patterns in an attempt to write good, maintainable code. My DI container is SimpleInjector. However, I've come across an indirect circular dependency problem that I'm struggling to see how to break without violating good design (and my own principles!).

The following semi-pseudo-code represents a simplification of the problem (note: in the interest of brevity, I don't detail the interfaces here, but they can be inferred from the classes that implement them, and I also don't show all the trivial stuff like the c'tor's setting backing fields - that's implied). Also note I use constructor injection throughout.

class A : IA
{
  A(int id, IBRepository br);
  IEnumerable<IB> GetBs(); // uses _br and _id to find all "child" B's.

  int _id;
  IBRepository _br;
}

class B : IB
{
  B(int id, int aId, IARepository ar);
  IA GetA(); // uses _ar and _aId to find "parent" A.

  int _id;
  int _aId;
  IARepository _ar;
}

class ARepository : IARepository
{
  ARepository(IAFactory af);
  IA FindById(int id); // uses _af to create A if Id found.

  IAFactory _af;
}

class BRepository : IBRepository
{
  BRepository(IBFactory bf);
  IB FindById(int id); // uses _bf to create B if Id found.

  IBFactory _bf;
}

class AFactory : IAFactory
{
  AFactory(IBRepository br);
  IA Create(); // _br fed in to A c'tor

  IBRepository _br;
}

class BFactory : IBFactory
{
  BFactory(IARepository ar);
  IB Create(); // _ar fed in to B c'tor

  IARepository _ar;
}

And here's some sample usage code:

// Sample usage code
IARepository ar = Container.GetInstance<IARepository>();
IA a = ar.FindById(123);
IEnumerable<IB> bs = a.GetBs(); // get children.
IB b = bs.First();
IA a2 = b.GetA() // get parent.
Debug.Assert(a.Id == a2.Id);

The container gets wired up (not shown) by registering the concrete classes with each of their respective interfaces (using RegisterSingle<T> for singleton/non-transient lifetime).

The problem is that I've introduced an indirect circular dependency as shown here (in reality the problem manifests itself as an exception as the application bootstraps - the SimpleInjector DI container does a nice job of telling you the problem!):

// Circular dependency chain
AFactory
  BRepository
    BFactory
      ARepository
        AFactory // <-- circular dependency!

As you can see, I'm already using Id's on A and B in an attempt to mitigate circular dependency issues that could arise from storing direct object references on each class (I've been bitten by that before).

I considered breaking the parent/child relationships (A.GetBs() and B.GetA()) out of the domain model's completely and pushing that functionality in to some kind of separate lookup service, but that seems like a code smell to me as the domain models should encapsulate their own relationships. Besides, the repositories already serve the purpose of such lookup functionality (which A and B already hold).

Most importantly though, this would make client code of A and B more complicated (consumers are used to being able to seamlessly "dot" their way through object graphs). Additionally, this would hurt performance as rather than caching a parent/child object reference on A and B, client code calling some separate service to do lookups would require constant trips to the backing data store (assuming no repository caching) and joins on the Id's.

As the saying goes, most software problems can be solved by another level of abstraction or indirection, and I'm sure that will be the case here, but I haven't been able to figure it out yet.

I've reviewed the following, and while I think I grasp what they're telling me, I'm struggling to relate it to my problem set:

So to summarize, how do I break the circular dependency problem while still using constructor DI, adhering to and utilizing established design patterns and best practices, and ideally maintaining a nice API for consumers of A and B to "dot" their way through the relationships? Any assistance would be greatly appreciated.

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
prlcutting
  • 379
  • 2
  • 12
  • Why do you have factories and repositories? Without knowing what those do, it's hard to suggest a solution. You have factories that depend on your repositories and repositories that depend on your factories. – CoderDennis Feb 15 '15 at 05:16
  • Try using `Func` and `Func` instead of `IBFactory`/`IAFactory` - this way you remove circular dependency for price of one extra redirection (Unity registers `Func` at the same time as class/interface, possibly your container need some extra code) – Alexei Levenkov Feb 15 '15 at 06:41
  • @CoderDennis In reality, `A` and `B` take additional dependencies (not shown/discussed previously), hence the factories. So the factories take these additional dependencies in as dependencies of their own and supply them to the c'tors of `A` and `B` each time `Create()` is called. The repositories load data from XML sources and serve up domain objects, hence the repositories take a dependency on the factories. E.g. `GetById()` interrogates an XML source and uses the factories to convert the query result in to an `IA`, `IB` instance etc. Thanks for helping out. – prlcutting Feb 15 '15 at 08:19
  • @AlexeiLevenkov Sorry, I don't quite follow... where exactly would I use `Func`/`Func`? – prlcutting Feb 15 '15 at 08:23
  • @prlc - I've added comment with code as an "answer" (as there is not way to put decently formatter code in comment). – Alexei Levenkov Feb 16 '15 at 06:18

3 Answers3

1

Class B doesn't need an A factory or repository. If Bs are children of a given A, then just pass the instance of A (or IA) into B's constructor.

The sample usage in your question would work perfectly.

You're going to have to change something because your dependency graph is circular. You'll have to decide which objects truly depend on each other and which don't. Requiring that Bs can only be created when there is an available instance of the parent A is one way to do that.

I don't really understand what your factories and repositories do. Having both seems like one too many layers to me. But would it work to have both repositories depend on both factories? Then each factory doesn't have any constructor dependency.

The factory Create methods could then receive repository instances as method parameters if needed.

CoderDennis
  • 13,642
  • 9
  • 69
  • 105
  • That's perfectly reasonable if you can assume that you always have an `A` (or `IA`) instance with which to create a `B`. However, client code typically gets an `IB` instance by calling methods on `IBRepository`, e.g. `IBRepository.FindById()`, hence is not responsible for instantiating the `B` instance and therefore can't inject an `IA`. – prlcutting Feb 15 '15 at 05:08
  • If you look at the dependency graph in my original post, you'll see that's why `BRepository` has an indirect dependency on `ARespository` (and subsequently `AFactory`); so that if the user calls `IB.GetA()` internally in `B`'s implementation we call `_ar.FindById(_aId)` method to get an `IA` instance, (or `A` concrete instance) constructed using `AFactory`. – prlcutting Feb 15 '15 at 05:09
1

Long comment:

You can delay resolution of the objects causing the circle by "adding a level of inderectoin". Since normally you'd not need factory in constructor you can postpone actual resolution to the point you need it by using Func<T> instead of T itself, where function is something like ()=> container.Resolve(typeof(T)):

class AFactory : IAFactory
{
  AFactory(Func<IBRepository> br);
  IA Create()
  {   
      var _br = _lazyBr();
      // _br fed in to A c'tor
      ...
  }

  Func<IBRepository> _lazyBr;
}

Note that Unity DI automagically registers Func<T> along with T, I'm not sure how to impement it in container you are using, but it should be similar to:

 container.Register<Func<IBRepository>>( 
       () => container.Resolve<IBRepository>());
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
0

After experimenting with the very helpful suggestions by @CoderDennis and @AlexeiLevenkov, in the end I re-evaluated my domain model design and broke the circular dependency problem by no longer giving child classes a property to access their parent. So I simply avoided the problem I was having originally rather than really solving it, but the change in design ended up working well for me.

Thanks to both guys for their efforts in helping me solve the problem and helping me come to the realization that perhaps the best idea was to change my approach to the problem.

prlcutting
  • 379
  • 2
  • 12