Source Code on GitHub
Background
Any application should be fairly tested before it is shipped to the customer. The 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 have a presentation layer that does not directly depend on the application logic and the database logic. Application logic should be independent of the UI such that both can be tested independently of each other. For example you should not call DBContext methods on the 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. Another 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 code (fetching records or inserting and so on) in the controller itself. Your application may be up and running but in this pattern, you cannot unit test the controller without the database. Another problem could be, you may have duplicates in the database code in various 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. Another problem is your controller is aware of the underlying database source and if in the 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 application that has database logic tightly coupled with the application itself.
Let us summarize the 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
The usual approach we follow is the Layered Architecture. In a Layered Architecture the presentation layer 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 applications 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 the application at 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 a 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 that 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.
- Code that may change often should be part of the external layers.
I would recommend you to watch the Steve Smith course on the same topic at Pluralsight for a better understanding of the concepts of the Onion Architecture. I will not go much into the discussion of the Onion Architecture and will instead jump into showing you refactoring an ASP.NET MVC application to use the Onion Architecture.
Create N-Tier application using C# by Steve Smith
Setup
Let us implement the Repository Pattern in a 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 a Package Manager Console run the command update-database. Go ahead and press F5 to run the application. Navigate to the Movie Controller and you should able to see a list of movies and can edit, delete and create movies. We will refactor this application to adhere to the Repository Pattern. Before proceeding, let us examine the code in MoviesController.
As you see in the code snippet above that inside the controller we are directly accessing the database. All the logic to fetch data from the 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 the Index action in isolation of the database.
Refactoring to Repository Pattern
Use the following procedure to refactor the existing application.
Create two class library projects and provide them the names MVCMovie.Infrastructure and MVCMovie.Core. In the project MVCMovie.Infrastructure all the database related operation will be placed. In this example Entity Framework is used. So all the Entity Framework dependency will be a part of the MVCMovie.Infrastructure project.
Setting up Infrastructure Project
Let's start with moving the MovieDBContext class to the MvcMovie.Infrastructure project. The Infrastructure project will contain all the database related classes and the operations. Since the application is using the Entity Framework, we need to add the Entity Framework reference to the Infrastructure project. From the Nuget manager add an Entity Framework package to the project. The MovieDBContext class will look such as follows.
MovieDBContext.cs
- using System.Data.Entity;
-
- namespace MvcMovie.Infrastructure
- {
- public class MovieDBContext : DbContext
- {
-
- public DbSet<Movie> Movies { get; set; }
-
- }
- }
Next create a MovieRepository class in the Infrastructure project. This class will contain all the database operations. Essentially we will move operations that are directly working with MovieDbContext class from the MoviesController to MovieRepository class. After moving the Entity Framework codes the MovieRepository class will look as 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 the MovieRepository class is created, refactor it to extract an Interface from this class. You need to move the extracted interface to the MvcMovie.Infrastructure project.
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 as follows in the 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 the Solution Explorer the MvcMovie.Infrastructure project should look as follows:
Setting up Core Project
Move the
Movie class from the MvcMovie project to the MvcMovie.Core project. To work with data annotations, add a reference of
System.ComponentModel.DataAnnotaions to the MvcMovie.Core project. The Movie class should look as follows in the 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:
Compile the MvcMovie.Core project and add its reference to the MvcMovie and the MvcMovie.Infrastruture project. After adding a reference of MvcMovie.Core to the MvcMovie.Infrastructure project, you should be able to successfully compile the MvcMovie.Infrastructure project.
Refactor MoviesController Previously in the MoviesController class we were directly creating an instance of MovieDBContext and performing database operations. To follow Onion Architecture, the controller will only know about the IMovieRepository and perform database operations using the IMovieRepository. First let's create an instance of the IMovieRepository.
- private IMovieRepository db = new MovieRepository() ;
Next any database operation will be done on an 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
- Edit Movie
- Remove Movie
Replace the various database operations in MoviesController with the code as shown above. As of now you should be able to successfully run the application.
Inverting the Dependency
We are directly creating an instance of MovieRepository in the MoviesController and that makes it tough to do unit testing of the Controller. 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 the Nuget Package Manger. In the Unity.Config (located in the App_Start) folder register the type as given below:
- container.RegisterType<IMovieRepository, MovieRepository>();
Once the 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 refractor Controller as shown below:
If you remember in the beginning we were directly creating the instance of the MovieDBcontext inside the controller hence the controller was fully dependent on the database and was making it tough to test it in isolation.
The MvcMovie application has been refactored to the Onion Architecture. The next task you may want to do, is to download the code discussed in this post from GitHub and start writing the 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 the database and database logic are part of the external layers. They depend on the
central layers like the core. The UI, or in this case the MVC application, is also an external layer and
depends on the Core project. All layers are interacting with each other using the interfaces rather than the concrete definitions. You can very easily write a Unit Test against the controllers using Fake or Mock without hitting the
database. In the future you can easily change data access technology from
Entity 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.Create N-Tier application using C# by Steve Smith.Happy Coding.