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:
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 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.
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.
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.
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 now run the application and
there is no Effect 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