Finally I am up with my
article related to onion architecture. The main aim of this article
is try to learn the onion architecture and have a sample sample code
which is loosely coupled and easily maintainable. So to learn this concept
efficiently, our application would consist of following:
§ An mvc application using Onion
architecture.
§ Entity framework with database first approach.
§ Dependency injection using Unity container.
§ Repository pattern with a repository per
entity.
So let's start with some
of the basics.
What is Onion architecture ?
This architecture term
was coined by Jeffrey Palermo back in 2008. Long time since then and it has become very
popular these days. When you get into the details of this architecture, you
might feel that you were already using from long time but may not be aware of
that.
Just a quick introduction
to it. Onion architecture is the way of designing 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 core to the top
most layer, the dependency towards the inner layers increases. But there should not be any inter-dependency
between the layers i.e. Layer2 should
not depend on Layer3. So we
should try to keep the core layer as much generic as possible, though
not necessary 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 into Onion architecture with
the 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 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 makes a good sense, as the models will be
tightly coupled to the UI layer. So what we will do is that we 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, which will have the methods for
CRUD operations related to the Users table. For our example, we will add a simple method GetAllUsers, which 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 reference to the Infrastructure project. So add the reference to it and call
the method as:
Now bind the Model in the
View, run the application and see the results.
Looks good so far. But
there are 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, i.e. instantiating
the repository and returning the view with the data. This violates the S of the SOLID principles, which says that each
class/function should be responsible or 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 (as the View will use the Model property to bind the data). So in future, for some reason, we need to remove the use of entity framework, it will not only affect the Infrastructure project but also the web application as we directly refer the entities into the Controller and View.
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 (as the View will use the Model property to bind the data). So in future, for some reason, we need to remove the use of entity framework, it will not only affect the Infrastructure project but also the web application as we directly refer the entities into the Controller and View.
3. Concrete Repository class: UserRepository is concrete class in the project. As a result, where-ever,
it will be used in the project, it will increase coupling
between the components.
So let's start by
reducing the tight coupling caused by the use of the entity framework with View and Controller. So what can we do here ? We introduce
plain POCO classes, which will carry the data from the data access layer,
through repository, to Controller and then finally the View. But a very important thing to be decided,
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 required to be container classes, 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 in the Infrastructure and the web layer. This will be also in sync
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 the same 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, just need to make changes in the Infrastructure layer. No need to change the Controller method or the View binding and hence no changes in the Core and Web layers.
Now, we will 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 controllers' responsibility of initializing the
repository from the Controller, to any other location. This will be done by one of the dependency injection container
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.
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 reference to the unity dll's using the nuget package manager. This
is to be added in the Web project, as we need to inject the dependency on the controller
methods. Let's start by adding the reference to the unity dll's.
Next, we add
an interface named IUserRepository add the definition of GetAllUsers method. Implement this interface on
the UserRepository class. Again the same question, where to add
the interface ? And 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, which will be provided by the UnityContainer. As the name suggests, unity container is
like a container which is responsible for providing the required dependency to
a class. In our example, we need
the UserRepository instance. So 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 to the required method
using the Constructor, Property or Method injection process.
So for this, we add a
class named UnityConainerRegistration and register the dependencies. We add
this class in the main web project as 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 started, 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 through 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 i.e. the onion architecture. Our SampleArchitecture.Core is the innermost layer which is independent of
everything. Then we have the SampleArchitecture.Infrastructure, which is dependent on the Core layer and independent of the web layer.
Finally we have the web layer which 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. Hope you enjoyed reading
it. Happy coding...!!!
No comments:
Post a Comment