This article is about Implementing Architecture for MVC. The code used in this article is from Github Project.
Before starting any development of system, one should take some time on deciding the architecture to build the system. The reason I’m concentrating on this is that lot of developers either underestimate the software architecture or if you are beginner then most of you don’t know about it.
Software architecture defines the systems structural design. I recommend to spend some time for deciding the structural design, because later on it will become so expensive to change the architecture. In general Software architecture means dividing the system into different element, which must fulfill its responsibility. These elements interact with each other to complete the system.
So, Here I’m writing about how I’m Architecting solution structure for my ECommerce Project? And why?
Some of you might be wondering (Mostly beginners), MVC itself provides good separation of concerns then why there is need of architecture for it? Yes, I agree with you all somewhat. I myself developed my first MVC project without thinking about architecture. But later on realized that the code should be more manageable, and should be separated as per the responsibilities.
In ECommerceMVC, I need the domain entity classes (Domain Layer), services (Business Logic Layer) and UI (Presentation Layer). In ASP .NET MVC we can separate Model, View and Controller as per our need, we don’t need to follow the same default architecture generated by Visual Studio, having all Models, Views and Controllers in root folder.
For ECommerceMVC the solution structure includes three projects. As for Initial stage I will limit to 3 project. I have arranged the project in such a way that if there is need of moving part of already developed code into another project, I can move it without any extra work.
The three projects are:
- Core Project (Class library): This project will have all Entity classes, Domain models, Business logic and database related files in organized manner. One can also divide this into separate, but I personally prefer to keep in single project, and move them later on as required. I will show it later in this article
- Web Project (MVC Project): This project will be startup project, having Models (for ViewModels), Views and controllers and other UI related files.
- Test Project (Unit Test Project): This project will have Tests.
All these projects created and added to blank solution. The solution looks like following:
Adding prefix i.e. ECommerce to project name allows me to differentiate from other project. Above diagram shows 3 Projects.
- ECommerce (class library): I have created three different folder in this project. As I said earlier, I like to write all business logic, DAL, entity class in one project initially and later on move it to another project if needed. In VS, when we create new class the namespace automatically includes the path where class is generated. So if I create Entity.cs class in Data folder, the namespace will be Ecommerce.Data. This type of structure allows me to move Entity.cs to another project named Ecommerce.Data, There is no need to do additional work.
- ECommerce.Web (MVC Project): it’s just simple MVC project, created by Visual Studio.
- ECommerce.Test (Unit Test Project): The unit test goes here.
Let’s discuss each one separately.
1) ECommerce (Class Library):
The Class library (ECommerce) has 3 folders namely Data, Services and Common.
Data folder will contain all Entity classes. Services folder will contain DAL and business logic. Common folder will contain common classes required by all the projects.
Why I’m not using Repository Pattern?
Answer: Because I’m Using Entity Framework
Repository pattern is one of the most popular DDD. Repository pattern hides the implementation of Data Access logic. That’s really cool feature to add in project. But Entity Framework (EF in further article) does the same work.
EF is ORM which eliminates the need for writing data access code.
I personally don’t think there is need to wrap EF in Repository, it’s like abstraction over abstraction. Entity framework already implements repository pattern. Repository pattern has Unit of Work & Repository, so does EF has DbContext (UoW) & DbSet (Repository), so there is no need to implement another layer on top of EF. And if we wrap EF in Repository, then we will lose the lots features of EF. You can read more here.
NOTE: There is nothing wrong with Repository pattern, you can use it if you want.
Let’s focus on structure of class library here.
All entity class will be created here. This folder will again contain sub-folders to organize entity.
Let’s add ProductEntity Class in folder Product.
In above code Key (Primary key in table), Required (Mandatory field in View Form),ScaffoldingColumn (whether render the property in view or not) etc. are Data Annotation specified in
These Data Annotation are used while scaffolding the View and generating table for database.
This folder will contain all the business logic classes along with DbContext, As already discussed, there is no need of another layer for wrapping Entity framework. I Like to write services directly which will return values, rather than just queryable.
While adding DbContext, One should also keep in mind that it’s concrete implementation and for testing mock implementation of DbContext needed.
To achieve this I’ve made some changes as specified in article from msdn Testing with your own doubles (EF6 onwards) for DBContext. I have installed Entity Framework 6 for ECommerce Project from NuGet package mangager. I’m going to use EF Code-First Approach. The DBContext class is as follow.
UPDATE: I have updated the DBContext and for testing by mocking DBContext.
The problem with above approach is that I’m using fake DBContext class, so that means I have to maintain two DBContext class one for ecommerce and other one for test project. To avoid this, I’m going to mock the DBContext from ECommerce project, and use the mock object in testing, without creating fake class. (As suggested by egenesis on reddit)
I’m going to create generic DbContext as I’ll be using it directly in services. Before creating concrete implementation I have created interface for DbContext IDBContext.
The concrete implementation of above Interface is:
From above implementation, you can see that I have override SaveChanges method also. I’ll call this method from controller, rather than services.
DBConString is the connection string which I have added in app.config file class library project. After enabling Migration I can create tables from entity classes to database referred by DBConString.
Let’s add Simple ProductServices class in Product folder.
The above code looks good, but the problem is its concrete implementation, it’s always good to have Interface for these classes. Now for above class extract interface, right click on class name> Quick Action -> Extract Interface. After extracting above Interface, following interface will be created. (The above class will be implementation of following Interface.)
UPDATE: I have put updated ProductServices class to use generic DbContext.
To use features of new DBContext, I also changed the ProductServices class.
Updated ProductServices class is as follow:
This approach will now allow me to use same DbContext for Services as well as Tests.
If all services (or most of them) have common function, then it’s good to have generic abstract class for those functions. I always prefer to use generic classes whenever possible.
c) Common: This folder will contain some utility code or common code which are used by all projects.
2) ECommerce.Web(MVC Project):
This project will be our Startup Project.
It contains three root folder Controllers, Models and Views. As I have moved the business logic layer and data access layer to another layer, the Model folder will only contain ViewModels.
I have added class library project ECommerce as reference to this project from Reference Manger as shown below.
Let’s add Controller and view for ProductEntity class using Scaffolding feature. Right click on Controller folder, Select New Scaffolded Item.
Select MVC 5 Controller with Views, using Entity Framework. Which will generate controller as well as Views for ProductEntity class.
After VS does Scaffolding the solution structure will look like this for ECommerce.Web.
For ECommerceMVC project, there is requirement of Admin Panel.
If I add admin panel’s Controller and Views in ECommerce.Web along with our Front End UI, that will become more unmanageable.
I can add another project say ECommerce.Admin for this, but I don’t want to create two MVC project and do all the configuration stuff which I already did for ECommerce.Web.
For this situation there is one more feature provided in ASP .NET MVC called Areas, using which I can create new area where I can place all Admin Panel related code.
Adding Area to ECommerce.Web is simple, just right click on project name, then choose Areas and name the Area in this case “Admin“. New folder “Areas” will be added to the ECommerce.Web.
Areas folder structure looks like this:
The areas creates MVC sub-project within Main MVC Project. Above diagram shows MVC structure for Area Admin, where I will write code for admin panel.
The routes for this area can be customized in AdminAreaRegistration.cs file.
To register routes for areas, I added following code to the Global.asax file in Application_Start method.
I have also added Connection String from app.config of class Library project in Web.Config of MVC project.
This will be structure for ECommerce.Web for project ECommerceMVC.
3) ECommerce.Test (Unit Test Project):
This project is created using Unit Test Template from New Project Window.
I personally prefer to write unit test first for every Services, to be sure that the business logic is implemented correctly.
Before writing any test, I have added following references to this project.
- Reference to Class Library Project (ECommerce)
- Reference to MVC Project (ECommerce.Web)
- Entity Framework
UPDATE: Following approach is not recommended.
For services, we will required to implement fake DBContext, which will be used to mock DBContext used in Services. To achieve this I have added two Classes TestContext and TestDbSet refered from here. Let’s add simple unit test for ProductService named TestProductService.cs.
UPDATE: Following changes made to tests to use new generic DbContext.
Now, the test for ProductServices is changed to mock DBContext using Moq.
Above Test calls GetProductByCode method of ProductServices class, which will return the object of ProductEntity having equal code passed to method. After running the above unit test, test-explorer shows the result.
I hope this article is helpful. If you have any suggestions/queries feel free to comment below.