Set up in Onion Architecture

Aug 22, 2013 at 11:52 PM
Edited Aug 26, 2013 at 8:54 AM
I have a newbie question to ensure I am on the right track. We are in the process setting up a new project, as part of this we have adopted an onion architecture to ensure we have rigid layering and separation of concern. Critical to this is the use of IoC container to glue things back together at run time. I did see this post, and it helped a lot. Currently our UI layer is a MVC web app, which needs it’s services injected. The injecting is done by another dependency resolution project, DependancyResolution.dll, within the solution at the composition root using the
WebActivator.PreApplicationStartMethod(typeof(SimpleInjectorWebCommon), "Start")]
Hook. So at start-up we go ahead and do the registrations.

So at start-up we go ahead and do the registrations.
      container.Register<IDbConn>(() => new DbConn(ConfigurationManager.ConnectionStrings["constring"].ToString()));
            container.Register<ICustomerRepository, CustomerRepository>();
            container.Register<ICustomerCallStatusRepository, CustomerCallStatusRepository>();
Now because we that we are running in our DependancyResolution.dll context, not the web app, to register the MVC controllers we do a..
     var path = new Uri(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase)).LocalPath;
            var assembly = Assembly.LoadFrom(path + "\\WebApp.Web.dll");
                new SimpleInjectorDependencyResolver(container));
            ServiceLocator.SetServiceLocator(() => new SimpleInjectorServiceLocator(container));
This approach works as far as I can see. I just wanted some validation that it is the correct approach and best practice.

Aug 23, 2013 at 7:17 AM
Edited Aug 26, 2013 at 8:00 AM
Your DependencyResolution project is what we call the Composition Root. the Composition Root is the startup path in the application where you wire everything together. Since each end application (such as your MVC, WCF, or Web Forms project) is different, each will get their own specific configuration; their own composition root.

If you have multiple applications that share the same code base, you'll often see that a great part of the DI configuration is identical between those applications. This logic shouldn't be duplicated and in that case it’s common to move the duplicated logic to a shared assembly. But each application will still have its own Composition Root, since there are always part of the configuration that are unique to that application.

Although possible to separate the composition root completely in its own assembly (as you are currently doing with the DependencyResolution project), this is not a common nor convenient thing to do. There are basically two ways to do this. The first way is to move everything that is not related to starting up, out of the startup assembly. In other words, your MVC project will become a thin shell, not contain much more than your DI configuration, your global.asax, a web.config and some other stuff that are really needed in the startup project. This assembly will depend on the UI project that contains your views, controllers, and everything else that's needed for your MVC app to work. Downside with MVC is that it will be much harder to wire everything up, since controllers and views, images, etc. are not in the startup project.

The second approach is to actually move the composition root to a different assembly. This means that in the MVC application_start you call some method in the composition root assembly that triggers all the wiring. This is the approach you are currently taking. Problem with this approach is that this assembly can't reference your MVC project, since your MVC project already references this assembly. That would cause a cycled assembly dependency graph, which is usually very troublesome to handle. It will cause compilation problems. So what usually happens that that you’ll end up dynamically loading that assembly, and although registering MVC controllers is the easy part (you just call RegisterMvcControllers), registering any other component that might have been registered (and that will happen pretty soon I ensure you) is much harder. You'll have to do nasty things with reflection.

Both options have their downsides and that's why I generally advice to just keep the Composition Root in the web project. Many developers don’t want their MVC assembly to depend depends on the data layer assembly, but that's not really a problem. Don't forget that assemblies are a deployment artifact; you split code into multiple assemblies to allow code to be deployed separately. A layer on the other hand is a logical artifact. It's very well possible (and common) to have multiple layers in the same assembly. In this case we'll end up having the Composition Root layer and the UI layer in the same MVC project (thus in the same assembly). And although that assembly references the DAL assembly, the UI layer itself does not reference the DAL layer. This is a big distinction. Of course, when we do this, we lose the ability for C# to check this architectural rule at compile time, but this shouldn't be a problem. Most architectural rules actually can't be checked by the compiler and there's always something like common sense. And if there's no common sense in your team, you can always use code reviews (which every team should IMO always do btw), or as last resort use other tooling (such as NDepend) to verify architectural constraints.

Long story short, although your approach works, it's brittle and I think you're trying to solve a problem that doesn't exist. Here's a Stackoverflow discussion about this subject.
Marked as answer by dot_NET_Junkie on 3/2/2014 at 10:58 AM
Aug 23, 2013 at 7:22 AM
Since you're startup up a new project and learning about architectural patterns, take a look at the concepts that this article describes.
Aug 25, 2013 at 10:15 PM
Wow, thanks for going into so much detail to answer my question. I will heed your advice, and keep the composition root in the MVC app.
Sep 26, 2013 at 11:24 PM
I have a related problem to this with the same type of setup...

I can likely get this to work if I remove Bootstrapper from the equation but I rather not do that... any thoughts?
Sep 27, 2013 at 6:59 AM
I'm sorry, but I'm not familiar with Bootstrapper.