Project Architecture

Saturday, October 4, 2008 11:13 AM

I read and loved the series of posts called The Onion Architecture by Jeffrey Palermo. As he put it:

The main premise is that it controls coupling. The fundamental rule is that all code can depend on layers more central, but code cannot depend on layers further out from the core. In other words, all coupling is toward the center. This architecture is unashamedly biased toward object-oriented programming, and it puts objects before all others.

Sweet! Everything a good architecture astronaut such as myself would love. Well, I thought I’d see how well it performed down here where the oxygen is breathable. I’m currently working on a side project for a buddy at work and thought that it would be the perfect test bed as there isn’t a hard deadline for it.

So, here is our “Agenda Management System”. Basically it’s going to expose city council voting records in new and interesting ways.

This is the actual solution. Two of the projects you see exist in every project I create no matter what the chosen architecture is: _build and Tests. _build contains deploy scripts and all of my project dependencies that aren’t “out of the box”. The Tests project contains all of my unit tests (there aren’t many – yes, I suck at this still). The other three projects make up the “onion”.

The most interesting parts of the solution to me are the dependencies. You can see here that Core doesn’t reference anything. That means everything my domain objects and services could possibly need have to live within the Core project. Sounds reasonable until you realize they will at some point need data from the database and to log information. The details of those concerns aren’t “domain” type things of course so they live in the infrastructure project. But Core doesn’t reference Infrastructure! Oh my.

This is where the Dependency Inversion principle comes into play. Basically, anything the domain layer could need that isn’t a “domain” type of concern is split into a contract and implementation. The contract (interface) lives within Core and the implementation lives within Infrastructure. The two are tied together only once at application startup.

Full Solution

Hopefully the image to the left isn’t too tiny to see, but basically there are three players required to make this work smoothly. The class IoC in Core is a static container for the interface of an inversion of control container that will resolve interfaces for me. The InversionOfControlContainer in Infrastructure is the implementation of the container defined in Core. At application startup in the Global.asax, I tie the two together.

So in Global.asax.cs you’ll see this:

protected void Application_Start()
IoC.Initialize(new InversionOfControlContainer());

You can probably imagine that all I'm doing in "Initialize" is setting a private static variable that will be referenced throughout the rest of the application’s lifetime. For example, a method on IoC is:

public static T Resolve<T>() { return iocContainer.Resolve<T>(); }

And I use IoC like this:


Of course, note that in this situation, both the interface IUnitOfWork and it's implementation are in Infrastructure, but the use of IoC is the point :)

Well, hopefully that conveys my implementation of The Onion Architecture. I must say, this is something that I’m finding extremely pleasant to work with. Due to the fact that Core doesn’t reference Infrastructure, I’m forced to find solutions strictly in terms of my domain and the repositories. Very cool stuff indeed!

Happy coding!

Tags: .net, architecture, ioc
Comments (2)
Hillbilly Coder Hillbilly Coder 10/4/2008 8:43 PM

Excellent post! Your example addresses an issue that I am currently wrestling with in my project. I am using the classic n-tier architecture with each layer only knowing about the layers below it but something seem a bit smelly to me. The strict layering forces the assemblies containing the service implementations to also contain the interfaces. According to Robert Martin in his book Agile Software Development it is the clients that tend to own the interfaces which their services implement. And this is exactly what you have achieved in your implementation of the Onion Architecture. I guess where I am getting caught up is what if you have an assembly that is shared among multiple solutions it can't possibly know all of its dependents. Therefore, would you not want the shared assembly to define its own interfaces? Ah, I simply need an assembly containing classes that adapt the interface of the shared assembly to those that core is dependent upon. OK, now that is cool! I'm sold!

Jeremy Sharp Jeremy Sharp 10/19/2008 9:06 PM

I was looking into this for the consumer re-write that starts up well last week .... I only have a 6 week window so as a result feared from strayng too far away from waht i know.... UGGGG words from a frustrated programmmmmmmer ..... Quality VS Quanity ... Someday I will make my self choose quality! Jeremy