Question

I am just starting out with DI & unit testing and have hit a snag which I am sure is a no brainer for those more experienced devs :

I have a class called MessageManager which receives data and saves it to a db. Within the same assembly (project in Visual Studio) I have created a repository interface with all the methods needed to access the db. The concrete implementation of this interface is in a separate assembly called DataAccess.

So DataAccess needs a project reference to MessageManager to know about the repository interface. And MessageManager needs a project reference to DataAccess so that the client of MessageManager can inject a concrete implementation of the repository interface. This is of courser not allowed

I could move the interface into the data access assembly but I believe the repository interface is meant to reside in the same assembly as the client that uses it

So what have I done wrong?

Was it helpful?

Solution

Are you using an Inversion of Control Container? If so, the answer is simple.

Assembly A contains:

  • MessageManager
  • IRepository
  • ContainerA (add MessageManager)

Assembly B contains (and ref's AssemblyA):

  • Repository implements IRepository
  • ContainerB extends ContainerA (add Repository)

Assembly C (or B) would start the app/ask the container for MessageManager which would know how to resolve MessageManager and the IRepository.

OTHER TIPS

You should separate your interface out of either assembly. Putting the interface along with the consumer or the implementor defeats the purpose of having the interface.

The purpose of the interface is to allow you to inject any object that implements that interface, whether or not it's the same assembly that your DataAccess object belongs to. On the other hand you need to allow MessageManager to consume that interface without the need to consume any concrete implementation.

Put your interface in another project, and problem is solved.

You only have two choices: add an assembly to hold the interface or move the interface into the DataAccess assembly. Even if you're developing an architecture where the DataAccess class may someday be replaced by another implementor (even in another assembly) of the repository interface, there's no reason to exclude it from the DataAccess assembly.

I think you should move the repository interface over to the DataAccess assembly. Then DataAccess has no need to reference MessageManager anymore.

However, it remains hard to say since I know next to nothing about your architecture...

Frequently you can solve circular reference issues by using setter injection instead of constructor injection.

In pseudo-code:

Foo f = new Foo();
Bar b = new Bar();
f.setBar(b);
b.setFoo(f);

Dependency inversion is in play:

High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

The abstraction that the classes in the DatAccess assembly depend upon needs to be in a separate assembly from the DataAccess classes and the concrete implementation of that abstration (MessageManager).

Yes that is more assemblies. Personally that's not a big deal for me. I don't see a big downside in extra assemblies.

You could leave the structure as you currently have it (without the dependency from MessageManager to DataAccess that causes the problem) and then have MessageManager dynamically load the concrete implementation required at runtime using the System.Reflection.Assembly class.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top