3

I've been researching how to design a MVC 4 web solution that follows the Dependency Inversion Principle and utilizes a Dependency Injection (DI) container that is configured fluently (i.e. with compile time type checking).

Many examples of ASP.NET MVC 4 Dependency Injection I've found focus on the specifics for implementing DI into the entry points the MVC framework provides. I find myself gravitating towards the resulting layered approach (dependencies shown with red arrows): enter image description here Which, unfortunately, repeats the conventional dependency model where high-level modules depend on low-level ones. Following the principle of Dependency Inversion the IService interface is moved into the WebProject. Unfortunately this creates a circular reference between the two projects: enter image description here To avoid the circular reference the CompositionRoot is moved into it's own Project: enter image description here

Leaving me with the problem of how to bootstrap the container (now I can't directly reference it from within the WebProject)?

With a little help from the best way to get the directory from which an assembly is executing initialization can be achieved through reflection.

var assembly = System.Reflection.Assembly.LoadFile(Helper.AssemblyDirectory + "/DependencyInjectionProject.dll");
var type = assembly.GetType("DependencyInjectionProject.Bootstrapper");
IDependencyResolver resolver = (IDependencyResolver)type.GetMethod("Initialise").Invoke(null, null);
DependencyResolver.SetResolver(resolver);

To make building easy I've set the DependencyInjectionProject's build target to the WebProject bin directory. I've met my objectives; inverted dependencies and compile time checked container configuration but I'm not completely happy with this approach due to the frequent build target collisions that occur when running the WebProject in IIS Express.

I am very interested in hearing other experiences and approaches towards meeting these requirements. Should I forgo compile time configuration and adopt a text based one? Is there an obvious structuring of the layers that avoids the pitfalls of circular dependencies that I'm not seeing?

Community
  • 1
  • 1
rtev
  • 1,102
  • 12
  • 24

2 Answers2

0

Composition Root is different with DI Container. Composition root is the very early entry level of executable, and DI Container are placed there.

One of the reason DI Container put there is, because the entry level are not being called by any other class. So when you create the DI Container there, it will make sure that DI Container is built at the earliest call, and prevent null reference exception. There may be other benefit but I just don't know it.

What I usually use is layered design, consist of:

  1. domain model (Entity)
  2. interfaces (reference 1)
  3. services (reference 1 and 2)
  4. dal (reference 1 and 2)
  5. UI (reference 1,2,3,4)

This design must prefer good SOC and can prevent the UI to access the repository directly. The dal do not know about service and UI. The service does not know about DAL and UI.

One cons I know is this design allow UI to access DAL directly without service.

I still don't know the other cons of this design though.

Fendy
  • 4,565
  • 1
  • 20
  • 25
  • I may be muddying the water with my use of the term Composition Root. Substituting Container Configuration for it might convey my intent better. The dependencies that you outline here follow the more conventional approach; one that is not "Inverted". I've considered separating the interfaces on their own layer but run into the same cyclic dependency issues. – rtev May 24 '13 at 04:01
  • Well, I'm sorry if that's the case, I can't help. In my point of view, the inverted approach provide no benefit at all (feel free to correct me) and it can add complexity flow (UI -> SL -> DI Container -> UI again). It is a cyclic dependency, and using service locator just hide the dependency, not eliminate it. – Fendy May 24 '13 at 04:10
-1

You would never want to move IService into the web project. Apart from the circular reference (which isn't necessarily a deal breaker, though VS won't let you do it.. it can be done in several other ways) it's just bad design.

There's absolutely nothing wrong with higher level modules depending on lower level ones. I'm not sure why you think this is a problem. Dependency inversion doesn't really refer to module or assembly dependencies, it refers to interface dependencies. (i'm speaking generically here, not specifically about the interface keyword). In fact, you can have DI without any separate assemblies at all... it could all be in the same assembly.

There are, however, certain restrictions if you do choose to split implementations up into separate assemblies. These are largely implementation issues of the assembly system.

The problems with module dependencies don't really show themselves with only two modules. It's more of a problem when you have 3 or more.

I like to use the onion architecture myself. In this method, you have a separate assembly that contains your interfaces and common types (such as entities). This assembly does not have a lot of code in it, it's mostly just definitions.

Above that is your data layer, and your business layer. These depend on your common assembly for entities, and interfaces. Then you have your UI layer, which depends on your business (or service layer, which is often just a façade over your business layer) and the common layer.

With this approach, The UI cannot talk to the DAL and vice versa. Business layer talks to the DAL and UI. And everything depends on the common interface assembly that has very little functional code, just entities, DTO's and interfaces. As such, the common depends on nothing other than core .NET stuff.

    UI <--------> Service/Business <--------> DAL
     |                   |                     |
     +----------> Common (IService) <----------+

No circular references, and everyone gets what they want and only talks to whom they need to.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • Which projects reference the container and where is it bootstrapped? – qujck May 24 '13 at 09:23
  • 1
    Respectfully; I think you have a common misinterpretation of the Dependency Inversion principle. Please consider: "While programming to interfaces rather than implementations represents good design practice, the Dependency Inversion Principle is not merely concerned with the use of interfaces, but the decoupling of high-level components from dependency packages." - http://aspiringcraftsman.com/2008/12/28/examining-dependency-inversion/ – rtev May 24 '13 at 12:47
  • @RichardTeviotdale - This is a confusing topic because the terminology is often misused, overused, or overloaded. You are talking about dependencies required by the "system" (ie assembly references). These articles are much more finer grained and talk about "components" and "packages" in the UML sense, which does not necessarily mean at the assembly level in .net. Packages in the UML sense can exist simultaneously within the same assembly. Don't confuse these two separate issues. When reading articles by Martin or others they are often not technology specific. – Erik Funkenbusch May 24 '13 at 14:40
  • @RichardTeviotdale - Keep in mind is that Dependency Injection is talking about things at the class/interface level. This is complicated by technology specific issues, such as assembly references, but they're not an issue of DI itself. DI does not change the fact that objects are dependent upon other objects in the sense that they must communicate with them (even if it's through a weakly coupled interface via DI). You must couple these objects together in your DI container. And this still creates a dependency at the application (or composition root) level, just not at the class level. – Erik Funkenbusch May 24 '13 at 14:46
  • @RichardTeviotdale - Dependency Injection, Dependency Inversion, and IoC are also not interchangeable terms. They all have slightly different meanings, and some are more broad than others. For instance, Injection and Inversion are both IoC, but IoC is not necessarily either of them. – Erik Funkenbusch May 24 '13 at 14:52
  • @MystereMan I agree with you closely but please consider http://stackoverflow.com/a/1113937/1216466. If a HigherLevel is the "owner" of the interface (e.g. [Consumer → ILogger] ⇐ Logger) subsequent organization of "modules" into assemblies necessitates that the Interface and Logger not be in the same assembly (for run-time substitutions and easier maintenance). The Logger has become dependent on the Interface. If module dependencies have truly been inverted then shouldn't any layered assembly (that separates modules into different assemblies) establish dependencies that reflect that inversion? – rtev May 24 '13 at 16:46
  • @RichardTeviotdale - Again, you're conflating assembly reference dependencies with component or module dependencies. It's pointless to make Logger depend on the Consumer assembly because it makes it impossible to use Logger in any other component. This is my point that these are different things. What if you had three components, Consumer1 and Consumer2 and Logger. How would Consumer2 use Logger if Logger can only be used with Consumer1 because of it's required dependency? – Erik Funkenbusch May 24 '13 at 20:27
  • I was using the logger example as it mirrored the example given in the answer I referenced. If I was using a real Logger component it would most likely specify it's own interface (at least an API). In this situation my "Logger" would really be an adapter that encapsulates the given real Logger component. P.S. I really appreciate the time you are taking to converse with me. I've been studying the Onion Architecture. It's very interesting. I notice that in every example given by J Palermo the interface for a given implementation sits higher in the dependency hierarchy. – rtev May 24 '13 at 23:58