Question

Say I have BusinessService & domain logic as part of MyProj.MiddleTier csproj which connects to MyProj.DAL DataRepository via interface IDataRepository.

I am using IOC unity container to build my dependency object graph. Composition root and BusinessService are all part of MyProj.MiddleTier (BusinessService is WCF based service consumed from WEB server and I have overridden ConfigureContainer method to build object dependency graph using my own serviceHostFactory)

I would like to understand shouldn’t IDataRepository be part of separate csproj (assembly) ? In fact shouldn’t be composition root itself be separate project/assembly holding reference of all assemblies ? If not, then I feel whole purpose of DI is defeated. Why ? Because if MyProj.MiddleTier has hard dependency reference of MyProj.DAL then I cannot simply switch to another persistence technology tomorrow without recompiling MyProj.MiddleTier. Had been MyProj.DAL just referenced that IDataRepository in separate assembly, it would have required to just deploy MyProj.DAL dll.

Also by not having it as part of separate assembly, developers can accidentally directly reference to classes and implementation of DataRepository in Business/Domain logic. Is this well known trade off ? If yes, then what are merits ?

In stack overflow I see mix of response :

This post on SO says actually there is no harm in doing so however another post says its good to have separate assembly.

Irrespective of above mixed SO answers, I was holding good belief on my thought process until I bumped upon similar text from famous book “Dependency Injection in .Net”. Here example taken is of ASP.NET MVC project where UI layer and composition root for building object graphs are part of same assembly.

However author also provided some explanation for this :

Don’t be misled into thinking that the Composition Root is part of your UI layer. Even if you place the Composition Root in the same assembly as your UI layer, the Composition Root isn’t part of that layer. Assemblies are a deployment artifact: you split code into multiple assemblies to allow code to be deployed separately. An architectural layer, on the other hand, is a logical artifact: you can group multiple logical artifacts in a single deployment artifact. Even though the assembly that holds both the Composition Root and the UI layer depends on all other modules in the system, the UI layer itself doesn’t

I think the core of my confusion is because of not understanding point around deployment artifact and logical artifact. I would be great if someone can help me understand this better.

Was it helpful?

Solution

I feel whole purpose of DI is defeated. Why ? Because if MyProj.MiddleTier has hard dependency reference of MyProj.DAL then I cannot simply switch to another persistence technology tomorrow without recompiling MyProj.MiddleTier.

No it's not defeated. Goal of DI is to abstract away dependencies, reduce coupling and facilitate testing with mocks or stubs. Switching implementations especially without recompilation is a more rare use case. Also chances are that another persistence technology is so different that it may require modifications of the interface itself.

developers can accidentally directly reference to classes and implementation of DataRepository in Business/Domain logic.

There are dependency diagrams in Visual Studio that can be used to enforce relations between architectural layers. Put repo implementation into a separate namespace and add rule that this namespace can't be consumed by business namespace. In fact it is a good idea to use this tool whether there is one assembly or few.

Also, depending where your composition root code resides, it could be possible to mark implementation classes as internal for extra protection.

To summarize, it is definitely OK to have multiple logical layers within one unit of deployment i.e. assembly. Split units of deployments when it is needed (e.g part of assembly is slow to recompile, or should be packaged and reused in different solution etc.)

OTHER TIPS

I think what you're experiencing is a sort of "dependency leak", where the "implementation details" of one layer is leaking into another layer. EntityFramework is notorious for forcing this onto unsuspecting developers. Basically the best way to avoid this is the following:

  • MyProj.DAL is made up entirely of classes that only take DTOs (or plain classes) and it internally transforms to and from your entity types
  • The outside world communicates with MyProj.DAL explicitly through interfaces that only accept DTOs for both input and output
  • BusinessService happens entirely on DTOs (you can create as many helper DTOs as you need to perform validations and business rules - this will lead to lots of transformations)
  • IDataRepository can be part of the MyProj.DAL or be a separate assembly depending on it

So because of the way the framework is designed, it is very difficult to successfully pull this off. If you go this route, you will definitely lose most of the benefits of something like EF. But, on the other hand, like you said, it will be too easy for someone to accidentally depend on some assembly they should not.

We've done this exact thing before and the biggest challenge for us was that 90% of our code base became mapping one type to another (transformations). As each service layer has it's own unique DTO and that causes you to write a lot of code that simply copies one type to another. This was all due to the fact that we used EF heavily and we could not ditch EF. If your project is using direct SQL or something simpler than EF, then it will be much easier to accomplish this. But if you're using EF and you want to have the powerful EF features, it will be very difficult. The good news is there are other ORMs that are much simpler than EF and they don't leak their implementation details.

Licensed under: CC-BY-SA with attribution
scroll top