Peeling Back the Onion Architecture


Download the code for this article.

I recently started a consulting project as an architect on an ASP.NET MVC application and quickly found myself immersed in the world of N* open source tools.  MVC(which stands for Model-View-Controller) lends itself to an Agile development methodology where TDD and BDD (Test-Driven and Behavior-Driven Development) are important components.  Writing applications that are testable requires that you separate business logic from presentation logic so that they can be independently tested.  This is a concept known as separation of concerns (SoC), which, in addition to testability, provides other benefits, such as greater application longevity and maintainability.  The life of an application is extended because loose coupling makes it easer to upgrade or replace components without affecting other parts of the system.

This is where the “Onion Architecture” comes in.  The term was first coined by Jeffery Palermo back in 2008 in a series of blog posts.  It is intended to provide some insurance against the evolution of technology that can make products obsolete not long after they are developed (the technical term is “deprecated”).  A classic example is Microsoft’s data access stack, which tends to change every few years (remember the demise of LINQ to SQL?).  What Jeffery proposed (although he’s not the first) is for the application to reference interfaces so that the concrete implementation can be supplied at runtime.  If, for example, the data access layer is represented by a number of repository interfaces, you can swap out LINQ to SQL with Entity Framework or NHibernate (or your favorite ORM) without breaking other parts of the application.  This same approach is used to decouple things like configuration and logging so they become replaceable components.
 
onion-arch
 
In this depiction, the “core” of the onion is the object model, which represents your domain. This layer would contain your POCO entities. Surrounding your domain entities are repository interfaces, which are in turn surrounded by service interfaces.  Representing your repositories and services as interfaces decouples consumers from concrete implementations, enabling you to swap one out for another without affecting consumers, such as client UI’s or tests.  The data access layer is represented in the outer layer as a set of repository classes which implement the repository interfaces.  Similarly the logging component implements a logging interface in the service interfaces layer.

 
onion-proj
 
Here is the project structure for a Visual Studio solution I created to demonstrate the Onion Architecture.  I inserted solution folders and aligned project and folder names for ease of use.  Infrastructure.Data uses NHibernate to implement repositories for ICategoryRepository and IProductRepository in Domain.Interfaces.  Infrastructure.Logging uses NLog to implement ILogging in Infrastructure.Interfaces.  The Web.Ui project has a ProductService class that implements IProductService in Services.Interfaces. (In a future post I will incorporate WCF into the project structure, but the Service implementation would go in a Service.Core project, with a Web.Services project for the service host.)

You may be asking, “How are concrete implementations of repositories and services created?” If components in the outer layer were to create instances directly, they would be tightly coupled to those implementations, defeating the whole purpose of the Onion Architecture and jeopardizing the application’s long-term viability.  The answer isDependency Injection (also known as Inversion of Control, or IoC).  Components on the outer rim of the diagram have constructors that accept service or repository interfaces, and it’s the job of the DI framework to serve up a concrete instance, based on some initial configuration or setup.  For example, the ProductController class in the ASP.NET MVC application has a constructor that accepts an IProductService, which has methods to get categories and products.  The controller doesn’t care about how the interface is implemented and what API the data access component uses.
 
public class ProductController : Controller
{
    // Services will be injected    
private
IProductService _productService;
   
public ProductController(IProductService productService)    
{        
_productService = productService;    
}
    //    
   // GET: /Product/     

public ActionResult Index()
    {
        // Get products        
var products = _productService.GetProducts(selectedCategoryId);
       
       // Rest of method follows ...
    
}
}
 
There are a number of DI / IoC containers out there.  One of my favorites is Ninject, which can be added using the NuGet package manager and has an extension for ASP.NET MVC applications.  Installing the Ninject.MVC3 package places a bootstrapper class in the App_Start folder.  There is a private RegisterServices method where you can bind local service implementations and load Ninject modules.
 
private static void RegisterServices(IKernel kernel)
{
    // Bind local services    
   
kernel.Bind<IProductService>().To<ProductService>();
   
   // Add data and infrastructure modules    

   var
modules = new List<INinjectModule>
        {
            new RepositoryModule()
        };
    kernel.Load(modules);
}
 
The RepositoryModule class resides in a separate DependencyResolution assembly, which references the Infrastructure.Data assembly and binds IProductRepository to ProductResository.  The assembly containing the Ninject modules references the Data assembly, so that web client doesn’t have to, keeping the web client ignorant of the actual repository implementation.  Once the bindings are set up, Ninject serves up the appropriate instance wherever the interface is used.
 
public class RepositoryModule : NinjectModule
{
    public override void Load()
    {
        // Bind and get config service        
       
Bind<IConfigService>().To<ConfigService>();
        var configService = Kernel.Get<IConfigService>();
           
        // Bind repositories        
       
Bind<IProductRepository>().To<ProductRepository>()              .WithConstructorArgument("connectionString",configService.NorthwindConnection);
    }
}
 
As you can see, dependency injection is the glue that holds everything together.  An integration test, for example, would also use the DI container to get instances of interface implementations, without having to reference assemblies containing those classes.
 
[TestFixture]
public class RepositoryTests
{
    // Ninject kernel    
    private
IKernel _ninjectKernel;
 
    public RepositoryTests()
    {
        // Init Ninject kernel        
       
_ninjectKernel = new StandardKernel(new RepositoryModule(), new LoggingModule());
    }
   
[Test]
public void Should_Get_All_Categories()
    {
        // Arrange        
      
var categoriesRep = _ninjectKernel.Get<ICategoryRepository>();

        // Act        
       var
categories = categoriesRep.GetCategories();
 
        // Assert        
       Assert
.That(categories != null);
       Assert.That(categories.Count() > 0);
    }
}
 
Unit tests would likely combine DI with a mocking tool, such as Moq, as shown here.
 
[TestFixture]
public class RepositoryTests
{
    // Ninject kernel    
    private
IKernel _ninjectKernel;
   
    public
RepositoryTests()
    {
        // Init Ninject kernel        
       
_ninjectKernel = new StandardKernel();
    }
 
    [TestFixtureSetUp]
    public void FixtureSetup()
    {
        // Init categories        
       var
categories = new List<Category>
        {
            new Category { CategoryId = 1, CategoryName = "Beverages" },
            new
Category { CategoryId = 2, CategoryName = "Condiments" },
            new
Category { CategoryId = 1, CategoryName = "Confections" }
};

// Set up mock categories repository
var mockCategoriesRep = new Mock<ICategoryRepository>(); mockCategoriesRep.Setup(m => m.GetCategories()).Returns(categories); _ninjectKernel.Bind<ICategoryRepository>().ToConstant(mockCategories.Object);
}

[Test]
public void
Should_Get_All_Categories()
{

            // Arrange var categoriesRep = _ninjectKernel.Get<ICategoryRepository>();

            // Act var categories = categoriesRep.GetCategories();

            // Assert

            Assert
.That(categories != null);
            Assert.That(categories.Count() == 3);
    }
}
 
Jeffrey summarizes the key tenets of the Onion Architecture as follows:
    • The application is built around an independent object model.
    • Inner layers define interfaces; outer layers implement interfaces.
    • Direction of coupling is toward the center.
    • All application core code can be compiled and run separate from infrastructure.
The sample application (which you can download here) provides a reference architecture based on these principles.  To use it you’ll need to download our good old friend, the Northwind sample database.  While the Onion Architecture does not propose anything new in terms of object-oriented and domain-driven design patterns, it provides guidance on how to decouple infrastructure from business and presentation logic, without introducing too much complexity or requiring redundant code. Enjoy.