The Onion Architecture is a way of structuring applications to maintain separation of concern and loose coupling (example project at: http://onionarch.codeplex.com/). Dependency Injection/Resolution is a key aspect of this architecture, since it is used to tie all the layers together.
The above link contains an example application on how to structure an ASP.NET MVC using the Onion layering. I really like it, but most of these examples use Ninject (which we all know is pretty slow). I was wondering if someone could perhaps eloborate on how to integrate a different DI tool (like SimpleInjector, Unity or Autofac) into an Onion Project.
It is key that all layers only have 1 dependency (including the MVC project), namely the Core layer. Except for the Dependency Resolution layer, this layer can reference all the layers.
I'm having a hard time setting the MVC project as a startup project, using DI, and not including a reference to the DI tool in the MVC layer.
Your question is
"how to integrate a different DI tool (like SimpleInjector, Unity or Autofac) into an Onion Project?"
I’m using StructureMap instead of Ninject, the way it’s integrated should be ok for any other DI framework.
As you said, only the Dependency Resolution Layer should reference all the other layers, it’s the outermost layer of your Onion Architecture. Well, to do so, I’ve created a project called BootStrapper. This is the only project where I reference StructureMap assemblies. In the App_Start folder of this project, I have a file named StructureMapMvc.cs which looks like this:
[assembly: WebActivator.PreApplicationStartMethod(typeof(XXXX.BootStrapper.App_Start.StructuremapMvc), "Start")]
namespace XXXX.BootStrapper.App_Start
{
public static class StructuremapMvc
{
public static void Start()
{
IContainer container = IoC.Initialize();
System.Web.Mvc.DependencyResolver.SetResolver(new StructureMapDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new StructureMapHttpDependencyResolver(container);
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
}
}
}
The interesting line is:
[assembly: WebActivator.PreApplicationStartMethod(typeof(XXXX.BootStrapper.App_Start.StructuremapMvc), "Start")]
According to the nugget package’s description:
WebActivator is a NuGet package that allows other packages to execute some startup code in web apps.
Pretty cool, huh? The last thing you have to put in place is to be sure that the BootStrapper project assembly will be pushed to the /bin folder of your web application (easy to set up using a post build action or OutputTo nugget package). This will avoid you to reference the BootStrapper project in your MVC project and break the Onion Architecture principle.
So, with all this in place, it adheres totally with the Composition Root Pattern and when your app will start, modules will be composed together.
Hope this helps!
Please note that I think the Onion architecture (or at least the sample implementation you pointed at, as @MystereMan correctly pointed out in the comments) has a problematic spot that you should be aware about.
Although the architecture seems to favors small/focused interfaces (often with one member), the naming of these services seems to indicate otherwise. In the reference architecture for instance, there is an IShippingService
class. It has one member and it therefore adheres to the Interface Segregation Principle (which is good). The name 'shipping service' however, indicates that it should contain all methods that are related to shipping. That will easily be dozens. Adding members to this interface however breaks the Interface Segregation Principle, Single Responsibility Principle (SRP) and Open Close Principle (OCP). The implementation will get big and ugly with lots of methods with little or no relationship (SRP). Implementing a new shipping requirement means adding a member and this breaks the OCP. The interface has many members while consumers often only need to call one of those members (low cohesion) and it will make unit testing harder.
Breaking it up in interfaces with all one member does solve part of the problem (and the architecture might have this intent), but this leaves you with a large amount of interfaces that have no relationship with each other, making it hard to apply cross-cutting concerns (logging, monitoring, audit trailing, validation, transactions, fault tolerance, etc) to them.
Whether or not this is a problem or not depends on a lot of factors, but the violation of one of the SOLID principles is always something to watch out for.
So as an addition to the Onion architecture, I advise you to read this article. It describes a solution to this possible shortcoming and it can be applied to the Onion architecture.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With