Step by Step implementing Onion architecture in ASP.NET MVC Application

Source Code on the GitHub

Learn Creating N-Tier Applications in C#, Part 1 by Steve Smith here – I learnt lot from this course hence sharing
Recently I gave a talk on Onion Architecture in MVC application to the audience of 300 at the C-Sharpcorner conference 2015. Talk was well received and I had a many request to write a step by step blog post on the same. So here I am writing that. In this blog post is written in simplest words and will help you in implementing onion architecture in ASP.NET MVC application. Even though theme of this post is ASP.NET MVC, you can use the core, infrastructure and the test project with other kind of applications as well like WCF or WPF.

You may want to read: Refactoring the ASP.NET MVC Application to the Onion Architecture

Source Code on the GitHub

What is onion architecture

Onion Architecture is the preferred way of architecting application for better testability, maintainability and dependability on the infrastructures like databases and services. This term was first coined by Jeffery Palermo in his blog back in 2008.

image

Advantages of Onion architecture
  • In the Onion Architecture layers talk to each other using the Interfaces. Any concrete implantation would be provided to application at the run time.
  • Any external dependency like database access and the web service call are part of the external layers
  • UI is part of the external layers
  • Objects representing domain are part of the internal layers or they are the centers
  • External layers can depend on the layers internal to it or central to it
  • Internal layer should not depend on the external layers.
  • Domain object which is at the core or centre can have access to the both the UI and the database layers.
  • All the coupling are towards the centre
  • Codes which may change often should be part of the external layers

Project structure

Let us start with creating the solution structure. We are going to work with the four projects.

  1. Core project (Class library )
  2. Infrastructure project (Class library)
  3. Test project (Unit Test project)
  4. Web project (MVC project)

Core project will contain the domain entities and the repositories interfaces. Infrastructure project will contain the classes (in this case EF data context) which will work with the database. The Test project will have the tests and the MVC project will contain the MVC controllers and the view.

Solution structure should look like as listed in the below image.

image

Core project

The core project is the inner most layer of the architecture. All the external layers like infrastructure, web etc. will use and depend on the core layer. However core project does not depend on any other layers.

Usually the core project should contain,

  • Domain entities
  • Repositories interfaces

It should not have any external dependencies. For example we must not have the following references in the core project,

  • Reference of the ORM like LINQ to SQL or EF
  • Reference of ADO.NET libraries
  • Reference of Entity Framework etc.
Create Entity

Let us go ahead and create BloodDonor entity class.


public class BloodDonor
    {
       public string BloodDonorID { get; set; }
       public string Name { get; set; }

       public DateTime Dob { get; set; }

       public string BloodGroup { get; set; }

       public string City { get; set; }

       public string Country { get; set; }

       public int PinCode { get; set; }

       public string PhoneNumber { get; set; }

       public string Email { get; set; }

       public bool IsActive { get; set; }

       public bool IsPrivate { get; set; }

       public bool IsVerified { get; set; }

    }

We may have a requirement to have some restrictions on the entity properties. For example maximum length, required etc. We do have two choices to achieve this,

  1. Using System.ComponentModel.DataAnnotations
  2. Use Entity Framework fluent API.

Both above approaches have their own advantages. However to keep core project without having any external dependencies like EntityFramework, I prefer to use DataAnnotations. To use DataAnnotations add System.ComponentModel.DataAnnotations reference in the core project. We can modify the entity class as shown in listing below,

using System;
using System.ComponentModel.DataAnnotations;

namespace LifeLine.Core
{
   public class BloodDonor
    {
       [Required]
       public string BloodDonorID { get; set; }
       [Required]
       [MaxLength(50)]
       public string Name { get; set; }
       [Required]
       public string BloodGroup { get; set; }
       [Required]
       public string City { get; set; }

       public string Country { get; set; }
       [Required]
       public int PinCode { get; set; }
       [Required]
       public string PhoneNumber { get; set; }

       public string Email { get; set; }

       public bool IsActive { get; set; }

       public bool IsPrivate { get; set; }

       public bool IsVerified { get; set; }

    }
}

As of now we have created the BloodDonor entity class with the data annotations. Next we need to add the repository interface.

Create Repository interface

We are going to follow the repository pattern. Roughly repository pattern allows us to replace the database access codes (class) without affecting the other layers. In the repository interface we will put definition of all the database operations need to perform.


using System.Collections.Generic;

namespace LifeLine.Core.Interfaces
{
    public interface IBloodDonorRepository
    {
        void Add(BloodDonor b);
        void Edit(BloodDonor b);
        void Remove(string BloodDonorID);
        IEnumerable<BloodDonor> GetBloodDonors();
        BloodDonor FindById(string BloodDonorID);

    }
}

As of now we have created the domain entity and the repository interface. Next let us go ahead and create the infrastructure project.

Infrastructure project

In the infrastructure project we perform operations related to outside the application. For example,

  • Database operation
  • Accessing outside service
  • Accessing File systems

Above operations should be part of the infrastructure project. We are going to use the Entity Framework to perform the database operations. We are working with the Entity Framework code first approach, so we need to perform the following steps,

  1. Create the data context class
  2. Implement the repository class
  3. Create the data base initializer class

Before we go ahead and create the data context class, let us go ahead and add reference of the Entity Framework. To add the reference right click on the infrastructure project and select manage Nuget package. From Nuget package manager select the Entity framework package to install in the project.

DataContext class

Let us create the data context class. In the EF code first approach, we create the data context class which will represent the database. Since we have only one business entity, so we will create one table BloodDonors.


using System.Data.Entity;
using LifeLine.Core;

namespace LifeLine.Infrastructure
{
   public class BloodDonorContext : DbContext
    {
       public BloodDonorContext()
           : base("name=BloodDonorContextConnectionString")
       {
           var a = Database.Connection.ConnectionString;
       }

       public DbSet<BloodDonor> BloodDonors { get; set; }
    }
}

Connection string

Optionally either you can pass the connection string or rely on the Entity Framework to create the database. We will set up the connection string in app.config of the infrastructure project. Let us go ahead and set the connection string as shown in the below listing,


  <connectionStrings>
    <add name="BloodDonorContextConnectionString" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=BloodDonors;Integrated Security=True;MultipleActiveResultSets=true" providerName="System.Data.SqlClient"/>
  </connectionStrings>

Database initialize class

We want database to initialize with some initial values. It can be done as shown in the below listing,

using System;
using System.Data.Entity;
using LifeLine.Core;

namespace LifeLine.Infrastructure
{
   public class BloodDonorInitalizeDb : DropCreateDatabaseIfModelChanges<BloodDonorContext>
    {

       protected override void Seed(BloodDonorContext context)
       {
           context.BloodDonors.Add(
           new BloodDonor
           {
               Name = "Rahul Kapoor",
               City = "Gurgaon",
               BloodGroup = "A+",
               BloodDonorID = "BD1",
               Country = "India",
               IsActive = true,
               IsPrivate = false,
               IsVerified = true,
               PhoneNumber = "91+7378388383",
               PinCode = 122002,
               Email = "Rahul@abc.com"

           });
           context.BloodDonors.Add(
               new BloodDonor
               {
               Name = "Salman Khan",
               City = "Mumbai",
               BloodGroup = "A-",
               BloodDonorID = "BD2",
               Country = "India",
               IsActive = true,
               IsPrivate = false,
               IsVerified = true,
               PhoneNumber = "91+84848484",
               PinCode = 25678,
               Email = "Salman@abc.com"
               }
       );
            base.Seed(context);
       }
    }
}

We are setting the value that if the model changes recreate the database. We can explore the other options of the entity framework also.

Repository class implementation

Next we need to implement the repository class. Repository class will access the database using the LINQ to Entity. To create the BloodDonorRepository class let us go ahead and create a class which will implement the IBloodDonorRepository interface.


using System.Collections.Generic;
using System.Linq;
using LifeLine.Core;
using LifeLine.Core.Interfaces;

namespace LifeLine.Infrastructure
{
   public class BloodDonorRepository : IBloodDonorRepository
    {
       BloodDonorContext context = new BloodDonorContext();
        public void Add(BloodDonor b)
        {
            context.BloodDonors.Add(b);
            context.SaveChanges();
        }

        public void Edit(BloodDonor b)
        {
            context.Entry(b).State = System.Data.Entity.EntityState.Modified;
        }

        public void Remove(string BloodDonorID)
        {
            BloodDonor b = context.BloodDonors.Find(BloodDonorID);
            context.BloodDonors.Remove(b);
            context.SaveChanges();
        }

        public IEnumerable<BloodDonor> GetBloodDonors()
        {
            return context.BloodDonors;
        }

        public BloodDonor FindById(string BloodDonorID)
        {
            var bloodDonor = (from r in context.BloodDonors where r.BloodDonorID == BloodDonorID select r).FirstOrDefault();
            return bloodDonor;
        }
    }
}

To implement the repository class, we are using the LINQ to Entity for the database operations. For example to add a blood donor,

  1. Create object of context class
  2. Use context.entiy.Add(entity)
  3. Use context.SaveChnages()

As of now we have implemented the infrastructure project. Make sure to build the project to confirm everything is fine.

Directly MVC project or should we write test?

After creation of core project and the infrastructure project, we need to take a decision that whether we want to directly create the MVC project or write the unit test for the repository class. Better approach would be to write the Unit tests for the repository class such that we would be sure that database related code has been implemented correctly.

Test Project

We have created a test project by selection Unit Test project template from the Test project tab. To start writing test, very first in the unit test project we need to add following references

  1. Reference of the core project
  2. Reference of the infrastructure project
  3. Entity framework package
  4. Reference of the System.LINQ

In the test project, I have created a class called BloodDonorRepositoryTest. Inside the unit test class initialize the test as shown in listing below,

 


BloodDonorRepository repo;
        [TestInitialize]
        public void TestSetUp()
        {

            BloodDonorInitalizeDb db = new BloodDonorInitalizeDb();
            System.Data.Entity.Database.SetInitializer(db);
            repo = new BloodDonorRepository();
        }

In the test setup, we are creating instance of BloodDonorInitalizeDb class and then setting up the database with the initial values. Also in the test setup, instance of repository class is created. Next let us create test to validate whether data base is initialized with the right number of data or not.

[TestMethod]
        public void IsRepositoryInitalizeWithValidNumberOfData()
        {
            var result = repo.GetBloodDonors();
            Assert.IsNotNull(result);
            var numberOfRecords = result.ToList().Count;
            Assert.AreEqual(2, numberOfRecords);
        }

In the test above we are calling the GetBloodDonors method of the repository class and then verifying the number of records. In ideal condition above test should be passed. You can verify the test result in Test-Window-Test Explorer

clip_image002

We can right tests for add, edit, and delete also. As of now we have written test to verify that database is getting created along with the initialized data. We can be sure about the implementation of the repository class after passing the test. Now let us go ahead and implement the web project.

Web Project

We are going to create MVC project which will use the core project and the infrastructure project. Add following reference in the MVC project

  1. Reference of infrastructure project
  2. Reference of core project
  3. Entity framework package

After adding all the references build the web project. If everything is find we should get successful build. Next let us write click on the Controller folder and add a new controller. Select create controller with Entity Framework with view option. Give name of the controller as BloodDonorsController.

Also we need to select the following options,

  1. BloodDonor class from the core project as the Model class
  2. BloodDonorContext class as the data context class from the Infrastructure project

image

We have created the controller along with the view using the scaffolding. At this point if we notice the web project, we will find BloodDonors controller and a BloodControllers subfolder in the view folder.

image

 

As of now we have created the controller using the model and data context class. Next we need to create instance of BloodDonorInitalizeDb class and set the database. We need to write this code inside the global.asax .


BloodDonorInitalizeDb db = new BloodDonorInitalizeDb();
            System.Data.Entity.Database.SetInitializer(db);

Last we need to copy the connection string from the app.config of infrastructure project to web.config of the web project. So let us go ahead copy the connection string in the web.config


<connectionStrings>
    <add name="BloodDonorContextConnectionString" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=BloodDonors;Integrated Security=True;MultipleActiveResultSets=true" providerName="System.Data.SqlClient"/>
  </connectionStrings>

At this point if we run the application we should able to perform the CRUD operations.

image

 

Dependency Injection using Unity

We have a running application but there is a problem. On noticing the controller class closely, we will find that object of the data context class is directly created inside the controller. We used scaffolding to create the controller using the entity framework and it causes creation of data context object inside the controller. Right now the controller and the data context class are tightly coupled to each other and if we change database access program then controller will be affected also.

We should not have the BloodDonorContext object inside the controller.

clip_image002[6]

We have already created the repository class inside the infrastructure project. We should use the repository class inside the controller class. We have two options to use the repository class

  1. Directly create the object of repository class in the controller class
  2. Use dependency injection to resolve repository interface to the concrete repository class object at the run time using the unity container.

We are going to use the second approach. To use the unity container add Unity.Mvc5 package in the web project using the manage Nuget package.

image

After installing the unity package, we need to register the type. To register the type open the UnityConfig class inside the App_Start folder. As highlighted in in the below image add the register.

clip_image002[8]

 

And inside the global.asax register all the components of the UnityConfig as shown in below image.

clip_image002[10]

So far we have added the unity container reference and register the type. Now let us go ahead and refactor the controller class to use the repository class. To start with

  1. Create a global variable of the type IBloodDonorRepository
  2. Create controller class constructor with a parameter as shown in below image

image

Once this is done we need to refactor the controller class to use functions of repository class instead of the data context class. For example Index method can be refactored as shown below,

image

After refactoring the BloodDonorController class will look like as shown in below listing,

 


using System.Linq;
using System.Net;
using System.Web.Mvc;
using LifeLine.Core;
using LifeLine.Core.Interfaces;
namespace LifeLine.Web.Controllers
{
    public class BloodDonorsController : Controller
    {
        IBloodDonorRepository db; 

        public BloodDonorsController(IBloodDonorRepository db)
        {
            this.db = db;
        }

        // GET: BloodDonors
        public ActionResult Index()
        {
            return View(db.GetBloodDonors().ToList());
        }

        // GET: BloodDonors/Details/5
        public ActionResult Details(string id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            BloodDonor bloodDonor = db.FindById(id);
            if (bloodDonor == null)
            {
                return HttpNotFound();
            }
            return View(bloodDonor);
        }

        // GET: BloodDonors/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: BloodDonors/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "BloodDonorID,Name,BloodGroup,City,Country,PinCode,PhoneNumber,Email,IsActive,IsPrivate,IsVerified")] BloodDonor bloodDonor)
        {
            if (ModelState.IsValid)
            {
                db.Add(bloodDonor);
                return RedirectToAction("Index");
            }

            return View(bloodDonor);
        }

        // GET: BloodDonors/Edit/5
        public ActionResult Edit(string id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            BloodDonor bloodDonor = db.FindById(id);
            if (bloodDonor == null)
            {
                return HttpNotFound();
            }
            return View(bloodDonor);
        }

        // POST: BloodDonors/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "BloodDonorID,Name,BloodGroup,City,Country,PinCode,PhoneNumber,Email,IsActive,IsPrivate,IsVerified")] BloodDonor bloodDonor)
        {
            if (ModelState.IsValid)
            {
                db.Edit(bloodDonor);
                return RedirectToAction("Index");
            }
            return View(bloodDonor);
        }

        // GET: BloodDonors/Delete/5
        public ActionResult Delete(string id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            BloodDonor bloodDonor = db.FindById(id);
            if (bloodDonor == null)
            {
                return HttpNotFound();
            }
            return View(bloodDonor);
        }

        // POST: BloodDonors/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(string id)
        {
            BloodDonor bloodDonor = db.FindById(id);
            db.Remove(bloodDonor.BloodDonorID);
            return RedirectToAction("Index");
        }

    }
}

 

Conclusion

This is it. These are the steps we need to follow to create a MVC application following to onion architecture.

Source Code on the GitHub

Have something to add? Please add in the comment section.

Refactoring the ASP.NET MVC Application to the Onion Architecture

Source Code on GitHub

Background

Any application should be fairly tested before it shipped to the customer. Biggest challenge in the testing is tightly coupled behavior of the UI with the Application Logic and further to the Database logic .Good testable systems are the one in which presentation layer does not directly depends on the application logic and the database logic. Application logic should be independent of UI such that both can be tested independent of each other. For example you should not call DBContext methods on click event of a button from the UI. If you do so, then the UI is tightly coupled with the database layer and any change in the database logic will impact the UI. Other disadvantage is that to unit test the database logic you need the UI. Testing of various components of the application is as important as building the components of the application. You should able to do the Unit Testing without the need of the database. Now let us assume, you are creating an ASP.NET MVC application and you have written all the database codes (fetching records or inserting etc.) in the controller itself. Your application may be up and running but in this pattern, you cannot unit test the controller without database. Other problem could be, you may have duplicate the database codes in different controllers and certainly you would not like that. There are challenges with duplicate codes. One change in database logic may require you to do multiple changes in the code. Other problem is your controller is aware of the underlying database source and if in future you change the data source, the controller will be affected and again this is not the best practice to create an ASP.NET MVC application. These problems are not only restricted to MVC but can exist in any kind of applications which is having database logic tightly coupled with the application itself.

You may want to read on Falafel blog :

Use Types from a Project without Referencing It
Three steps to use jQuery UI in ASP.NET MVC 5
Work with ASP.NET MVC 5 Areas from different projects
Types of ASP.NET MVC Views

Let us summarize problems we discussed above:

  • Duplicate database access codes
  • Hard to maintain the codes
  • Difficulties in Unit Testing
  • Hard to replace type of data sources
  • Tough to put centralized database access related policy

Usual approach we follow is the Layered Architecture. In layered architecture the presentation layer is transitively depends on the database layer and that does not solve the problem we discussed earlier in an absolute way.

Onion Architecture is the preferred way of architecting application for better testability, maintainability and dependability on the infrastructures like databases and services. This term was first coined by Jeffery Palermo in his blog back in 2008.

Learn more about the Onion Architecture in the series of blog posts by Jeffery Palermo

  • In the Onion Architecture layers talk to each other using the Interfaces. Any concrete implantation would be provided to application at the run time.
  • Any external dependency like database access and the web service call are part of the external layers
  • UI is part of the external layers
  • Objects representing domain are part of the internal layers or they are the centers
  • External layers can depend on the layers internal to it or central to it
  • Internal layer should not depend on the external layers.
  • Domain object which is at the core or center can have access to the both the UI and the database layers.
  • All the coupling are towards the center
  • Codes which may change often should be part of the external layers

I would recommend you to watch Steve Smith course on same topic at Pluralsight for better understanding on the concepts of the Onion Architecture. I will not go much into discussion of Onion architecture and jump into showing you refactoring ASP.NET MVC application to use the Onion Architecture.

Create N-Tier application using C# by Steve Smith

Setup

Let us implement Repository Pattern in MvcMovieRTM ASP.NET MVC Application. You can download this app from official Microsoft site here . After downloading, open the app in Visual Studio and in Package Manager Console run the command update-database. Go ahead and press F5 to run the application. Navigate to Movie Controller and you should able to see list of movies, can edit, delete and create new movie. We are going to refactor this application to adhere the Repository Pattern. Before we move ahead, let us examine the code in MoviesController.

image

As you see in the above code snippet that inside the controller we are directly accessing the database. All logics to fetch data from database is written right inside the controller and that makes the controller and the database access tightly coupled to each other. You cannot unit test Index action in isolation of database.

Refactoring to Repository Pattern

Follow the following steps to refactor the existing application:

Create two class library projects and give them name as MvcMovie.Infrastructure and MvcMovie.Core. In the project MvcMovie.Infrastructure all database related operation will be placed. In this example Entity Framework is used. So all Entity Framework dependency will be part of MvcMovie.Infrastructure project.

Setting up Infrastructure Project

Let’s start with moving the MovieDBContext class to the MvcMovie.Infrastructure project. Infrastructure project will contain all the database related classes and the operations. Since application is using Entity framework, we need to add the Entity framework reference in Infrastructure project. From Nuget manager add Entity framework package in the project. MovieDBContext class will look like as follows:

MovieDBContext.cs

 


using System.Data.Entity;

namespace MvcMovie.Infrastructure
{
    public class MovieDBContext : DbContext
    {

        public DbSet<Movie> Movies { get; set; }

    }
}

Next create MovieRepository class in the Infrastructure project. This class will contain all the database operations. Essentially we will move operations which are directly working with MovieDbContext class from the MoviesController to MovieRepository class. After moving the Entity framework codes the MovieRepository class will look like follows:

MovieRepository.cs


using MvcMovie.Core;
using System;
using System.Collections.Generic;
using System.Data.Entity;

namespace MvcMovie.Infrastructure
{
    public class MovieRepository : IMovieRepository, IDisposable
    {
        MovieDBContext db = new MovieDBContext();
        public IEnumerable<Movie> GetMovies()
        {

                return db.Movies;

        }

        public void Add(Movie m)
        {

                db.Movies.Add(m);
                db.SaveChanges();

        }

        public void Edit(Movie m)
        {

                db.Entry(m).State = EntityState.Modified;
                db.SaveChanges();

        }

        public void Remove(int id)
        {
                Movie movie = db.Movies.Find(id);
                db.Movies.Remove(movie);
                db.SaveChanges();

        }

        public void Dispose()
        {
            db.Dispose();

        }
    }
}

Once MovieRepository class is created refactor it to extract Interface from this class. You need to move extracted interface to the MvcMovie.Infrastructure project.

clip_image002

Visual Studio will extract the IMovieRepository interface and put it inside the MvcMovie.Infrastructure project. Move the IMovieRepository interface to the MvcMovie.Core project. IMovieRepository will look like follows in MvcMovie.Core project:

IMovieRepository.cs


using System;
using System.Collections.Generic;
namespace MvcMovie.Core
{
   public interface IMovieRepository
    {
        void Add(Movie m);
        void Edit(Movie m);
        IEnumerable<Movie> GetMovies();
        void Remove(int id);
    }
}

Do not forget to implement the IMovieRepository interface in the MovieRepository class. At this point of time if you go ahead and build the MvcMovie.Infrastructure project, you will get compile time errors. In solution explorer MvcMovie.Infrastructure project should look like follows:

clip_image002[6]

Setting up Core Project

Move Movie class from the MvcMovie project to the MvcMovie.Core project. To work with data annotations add reference of System.ComponentModel.DataAnnotaions in the MvcMovie.Core project. Movie class should look like follows in core project:

Movie.cs


using System;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Core
{
  public class Movie
    {

        public int ID { get; set; }

        [StringLength(60, MinimumLength = 3)]
        public string Title { get; set; }

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime ReleaseDate { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
        [Required]
        [StringLength(30)]
        public string Genre { get; set; }

        [Range(1, 100)]
        [DataType(DataType.Currency)]
        public decimal Price { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
        [StringLength(5)]
        public string Rating { get; set; }
    }
}

You should have the IMovieRepository interface and the Movie class inside the MvcMovie.core project. In solution explorer the MvcMovie.Core project will look as follows:

clip_image002[8]

Compile MvcMovie.Core project and add its reference to the MvcMovie and the MvcMovie.Infrastruture project. After adding reference of the MvcMovie.Core in the MvcMovie.Infrastructure project, you should able to successfully compile the MvcMovie.Infrastructure project.

Refactor MoviesController

Previously in the MoviesController class we were directly creating instance of the MovieDBContext and performing database operations. To follow Onion Architecture, controller will only know about the IMovieRepository and perform database operations using the IMovieRepository. Very first let’s create instance of the IMovieRepository


private IMovieRepository db = new MovieRepository() ;

Next any database operation will be performed on instance of MovieRepository. Some of the operations are as follows:

Fetch Genre


            var GenreQry = from d in db.GetMovies()
                           orderby d.Genre
                           select d.Genre;

Fetch All Movies

var movies = from m in db.GetMovies()
                         select m;

Add Movie


db.Add(movie);

Edit Movie


db.Edit(movie);

Remove Movie


db.Remove(movie.ID);

Replace various database operations in MoviesController with the code as shown above. As of now you should successfully able to run the application.

Inverting the Dependency

We are directly creating instance of the MovieRepository in the MoviesController which makes Controller tough for Unit Test. In this scenario to test the Controller, you need the database. We can solve this problem by inverting the control using any DI Container. You are free to use any DI container of your choice, however I am using Microsoft provided Unity Container. To use it add the Unity reference using Nuget Package Manger. In Unity.Config (located in the App_Start) folder register the type as given below:


  container.RegisterType<IMovieRepository, MovieRepository>();

Once type is registered you need to call the RegisterComponents() method of UnityConfig in the Application_Start() method of the Global.asax.cs as shown below:


UnityConfig.RegisterComponents(); 

As the last step refactor Controller as shown below:

clip_image002[10]

If you remember in beginning we were directly creating the instance of the MovieDBcontext inside controller hence the controller was fully dependent on database and was making it tough to test it in isolation.

MvcMovie application has been refactored to Onion Architecture. Next task you may want to do, is to download the code discussed in this post from GitHub and start writing test.

Source Code on GitHub

Summary

We have refactored the application adhering to the onion architecture. Domain objects like Movie is at the center of the architecture and are the part of internal layers. The Infrastructure project in which we are working with database and database logics are part of the external layers. They depend on the centeral layers like core. UI or in this case the MVC application is also an external layer and depndes on the Core project. All layers are interacting to each other using the interfaces rather than the concrete definitions. You can very easily write Unit Test against the controllers using Fake or Mock without hitting the datbase. In future you can easily change data access technology from Enity Framework to something else without affecting the UI and the Core.

Resources

Source Code on GitHub

Learn more about the Onion Architecture in the series of blog posts by Jeffery Palermo