Onion Architecture Using MVC and Database First Approach

Finally I have my article related to Onion Architecture. The main purpose of this article is try to explain the Onion Architecture and provide sample sample code that is loosely coupled and easily maintainable. So to learn this concept efficiently, our application would consist of the following:

  • An MVC application using the Onion Architecture.
  • Entity Framework with a Database First Approach.
  • Dependency injection using a Unity container.
  • Repository pattern with a repository per entity.

So let's start with some of the basics.

What the Onion Architecture is

This architecture term was coined by Jeffrey Palermo back in 2008. A long time ago and it has become very popular these days. When you get into the details of this architecture, you might feel that you were already have been using it for a long time but may not be aware of that.

Just a quick introduction to it. The Onion Architecture is a way to design your application layers in such a way that, like an Onion, the innermost layer or the core component, we can say, is independent of the layer above it. As we move away from the core, the layers/components can depend on the layer towards the core but not on the layers above itself. For example, if we have 3 layers of architecture, and Layer1 is the innermost or the core layer, then Layer2 and then Layer3. So, to create an Onion Architecture based application would have Layer1 as the innermost layer, independent of Layer2 and Layer3. Then Layer2 comes above Layer1 and depends on only Layer1 and not Layer3. Finally, Layer3 is the outermost layer and can depend on both Layer1 and Layer2.

So the most important point in the Onion Architecture is that, as we move from the core to the top-most layer, the dependency towards the inner layers increases. But there should not be any inter-dependency among the layers, in other words Layer2 should not depend on Layer3. So we should try to design the core layer as generic as possible, though not necessarily always. In such a case, our architecture layers would look like the following:

Let's try to understand by creating a new MVC application. We will create an application with tight coupling and try to convert it into an Onion Architecture with a system of loosely-coupled components. Fire up your Visual Studio and add a new project of type MVC.

Next we will display some sample data on a page, from a database. For this, we will create a sample database and use the Entity Framework database first approach. So let's add an .edmx and update it from a sample database. But, adding this edmx to the same project does not make good sense, since the models will be tightly coupled to the UI layer. So what we will do is we will add a new project layer of type class library, with the name SampleArchitecture.Infrastructure and add the .edmx to it. See the code below:

Next, in order to display the data from the table, we create a new class called UsersRepository.cs, in the same Infrastructure project that will have the methods for CRUD operations related to the Users table. For our example, we will add a simple method GetAllUsers that will get all the records from the Users table. See the code below:

Now let's add a controller, its corresponding view, and make a call to the repository GetAllUsers method. In order to access the repository, we need to add a reference to the Infrastructure project. So add the reference to it and call the method as:

Now bind the Model to the View, run the application and see the results.

Looks good so far. But there are a few issues in this code.

  1. Violation of Single Responsibility Principle: The controller class is dependent on the concrete UserRepository class. The issue is that the Controller method is now responsible for multiple tasks, in other words instantiating the repository and returning the view with the data. This violates the S of the SOLID principles that says that each class/function should be responsible for only one task.
  2. Tight coupling between the Web and the Infrastructure project: Our web project is directly dependent on the entities. This includes not only the Controller class but also the View (since the View will use the Model property to bind the data). So in the future, for some reason, we need to remove the use of the Entity Framework, it will not only affect the infrastructure of the project but also the web application since we directly refer to the entities in the Controller and View.
  3. Concrete Repository class: UserRepository is a concrete class in the project. As a result, whereever, it will be used in the project, it will increase the coupling among the components.

So let's start by reducing the tight coupling caused by the use of the Entity Framework with the View and Controller. So what can we do here? We can introduce plain POCO classes that will carry the data from the data access layer, using a repository, to the Controller and then finally the View. But a very important thing to be decided is where to add these POCO classes. The reason being, we are trying to use the Onion Architecture. We can add them to the Infrastructure project. But the point is that these classes are just container classes to carry the data and bind with the UI. So why not isolate them to another independent layer?

So we add another layer called SampleArchitecture.Core and add its reference to the Infrastructure and the web layer. This will also be synchronized with the Onion Architecture, where:

  1. Core layer will be independent of the Infrastructure and Web layers. This becomes Layer1 of our earlier discussion.

  2. Infrastructure layer will be independent of the Web layer, but will be dependent on the Core layer, for the POCO classes. This becomes Layer2 of our earlier discussion.

  3. Web layer will be dependent on both the Core and the Infrastructure layers, for the use of POCO classes and the repository classes. This becomes Layer3 of our discussion earlier.

So let's add a new project of type ClassLibrary and add a POCO class called UserUI. Another advantage of these classes is that we can add the validations of the model here itself.

Next, we change the repository method to use the POCO class and return it to the Controller and bind it with the View.

Run the application and now you can see the results.

So now, even if you need to remove the Entity Framework, you just need to make changes in the Infrastructure layer. There is no need to change the Controller method or the View binding and hence no changes in the Core and Web layers.

We will now try to remove the process of creating the instance of the repository class, within the controller. For this, we will use the concept of Inversion of Control. This means, we will invert the controller's responsibility of initializing the repository from the Controller, to any other location. This will be done with one of the dependency injection containers named Unity. So basically this dependency injection container will be responsible for creating the required dependency and passing it to the controller. As per MSDN, Unity is:

Unity is a general-purpose container for use in any type of Microsoft.NET Framework-based application. It provides all of the features commonly found in dependency injection mechanisms, including methods to register type mappings and object instances, resolve objects, manage object lifetimes, and inject dependent objects into the parameters of constructors and methods, and as the value of properties of objects it resolves.

In order to use it, we need to add a reference to the Unity DLLs using the Nuget Package Manager. This is to be added to the Web project since we need to inject the dependency on the controller methods. Let's start by adding the reference to the Unity DLLs.

Next, we add an interface named IUserRepository and add the definition of the GetAllUsers method. Implement this interface on the UserRepository class. Again the same question, where to add the interface? And the same concept to answer this question, this is a re-usable component and should be an independent component. So we add it to the SampleArchitecture.Core project. Makes sense, doesn't it ? So the code changes to:

Now the controller needs the instance of the UserRepository that will be provided by the UnityContainer. As the name suggests, the Unity container is like a container responsible for providing the required dependency to a class. In order to use the Unity container, we add references to the Unity DLLs using the Nuget Package Manager.

In our example, we need the UserRepository instance. So the Unity container will provide it to us. But before it can do that, we need to register the required dependencies with the container. Registering the dependency is like telling the container that when I am using this interface, provide me an instance of the concrete class that implements this interface. At run time, get the required dependency from this container and inject it into the required method using the Constructor, Property or Method injection process.

So for this, we add a class named UnityContainerRegistration and register the dependencies. We add this class to the main web project since this is where we need the dependencies to be resolved.

Next, we initialize this container in the Global.asax file. So when the application is begun, it will register all the dependencies in the container.

Finally, we change our Controller to receive the dependency of UserRepository, using the Constructor, in the form of IUserRepository (as it implements this interface). This dependency gets resolved using the Unity container we registered above.

So now run the application and there is no affect on the results. But in the application code, our the dependency follows what we aimed at, in other words the Onion Architecture. Our SampleArchitecture.Core is the innermost layer that is independent of everything. Then we have the SampleArchitecture.Infrastructure that is dependent on the Core layer and independent of the web layer. Finally we have the web layer that is dependent on both the Core and the Infrastructure layer. This is what we aimed at:

So this is what we discussed in the starting. Wasn't that easy to learn? I hope you enjoyed reading it. Happy coding!

Similar Articles