The onion architecture is nothing new, in fact, it has been around for a while. Some may call hexagon architecture or ports and adapters pattern. They all essentially refer the same layering structure which promises more maintainable applications since it embraces the separation of concerns throughout the system.
Initially, it may seem a variation of an N-tier (traditional layering) architecture; at the end of the day, you are dealing with the bunch of small project inside the solution. I have developed numerous application with this top-down layering, and most of them are still working rock solid. However, the issue is that each layer coupled to the layers below it. Since the UI is the top layer and each application have different UI requirements, the reusability is not quite there. More importantly, when you need a quick fix, it is so tempting to skip some layers and directly access to lower layers, when you do not suppose to do that. It may not seem a big deal if you need a quick and dirty fix, but it becomes an issue after a while. The technology changes but software cannot adopt it, because there are some logics errors, wrong coupling, skipped layers. When you need to introduce a major update to your application, the ultimate solution is rewriting everything from scratch with the promises to each other "this is the last time, and we will never rewrite it again" until the next big release!
Indeed, nothing can protect you from spaghetti code if you do not truly commit to any pattern. However, I found that onion architecture set the right foot in the beginning and make it harder to mess the code up. Equally importantly, it gives excellent reusability for new projects or switching technologies in a project.
Peeling the Onion
Although, there are many different ways of implementing the onion architecture to your projects, I found the following is more adaptable and comfortable for especially for newcomers. Below are my interpretation and implementation of the onion architecture.
Start off with a new empty solution in Visual Studio. Then, I create four solution folder (they also represents action folder structure in the file system): Core, Data, Presentation, and Tests. At first glance, it may look like traditional layering application.
Core solution folder consists of three class library projects: Core, Entities, and Services. Reusable code pieces reside in Core.Core where all common features application may share: base design pattern objects, caching mechanisms, type converters, configuration (setting) features, data related interfaces, event publishers, IO, custom exception object, persistent collections, HTML context helper, and some common helpers.
I do not favor the idea of including third party component in my core. The reason being that, when a component receives an update, it may break something inside my core, rather pass those dependencies to other layers which are more flexible and welcomes to changes. When I fire up a new project all I do is importing the Core project from other projects I have developed. Instantly, I am ready to start writing code for my project.
Some may call this layer as Domain. I witness some even put these POCO entities inside the Core project. I prefer to keep them separate and reference to Core. Additionally, I put each related entity in their respective folders. For instance, everything related to user entity (user, login, user roles, etc.) goes under the user folder. I follow same practice throughout the application. This way when I need a module (say User login and permission) I simply copy from previous projects to respective places. One last note: every entity inherits from BaseEntity abstract class which resides under the Core.
Services are pieces of codes that do something, including but limited to computing, database operations or regulate relationships with another object. Almost all of them implements an interface whether it is within the Services project or the Core project.
Data, as the name applies, is the database project. Any ORM (such as Entity Framework), migrations, mappings, object context, etc. resides in this project. It is relatively small project however it performs every critical job: database operations. Service layer communicates with data layer to do the CRUD operations at the minimum. Generics are a great way to implement in here.
The actual project whether MVC or ASP.Net Form application. All the other projects are referenced to this project. Besides the UI, the project coordinates what to receive from a user, or what to show to the user. It interacts in a great deal of Services layer.
I found extremely useful to having a language test projects. This project helps to check out new features of the language or to perform performance comparisons. Embedding language tests inside the project, also assist the other team members to learn and understand your code better.
It's more beneficial to separate each project to own unit test projects. However, a relatively small project can be tested inside one project. NUnit is a great tool, however, when it comes to simplicity and ease of use, MS Test is hard to beat.