Introduction
While developing applications if we keep in mind the advantages of loosely coupled system then we end up creating systems which are easier to maintain.Loosely coupled system is one in which the component has no or little knowledge about the rest of the components in the application.Some of the advantages of loosely coupled architecture are
- Different components can be changed in isolation without affecting the rest of the components thus making application easier to maintain
- It is more suited to the agile methodology in which changes happen very frequently thus tight coupling will not allow future changes
- Creating mock tests is easier as the actual objects can be easily replaced with the mock objects.
In contrast in a strongly coupled system an application component such as a class or a module contain s a direct reference to a concrete instance of another component. This makes the system difficult to maintain.
While working on an MVC application we should remember that MVC framework is based on a loosely coupled architecture.In MVC framework every component is based on an interface or abstract class.This means that we can easily replace any component of the framework with another one.
If we take the examples of the controller and the view ,they implement the IController and the IVeiw interfaces.We can replace them with any component that implements these interfaces.
Why to use the Repository Pattern ?
MVC applications supports creating decoupled architecture and one of the ways that we can create decoupled architecture is by using the repository pattern.
Usually model component in an MVC application is responsible for containing the business logic ,the business rules and accessing the data source for retrieval and updation of the data.Though we can put the data access logic in the model ,it has few disadvantages
The code is duplicated if we are accessing the same data from multiple places in the model.This is against the DRY (Dont Repeat Yourself) design approach.DRY is a design principle which means that every peice of information should be uniquely represented without redundancy.
The business logic is aware of the data source type ,whether the data source is SQL Server or Oracle. So if the data source changes the model also needs to be updated to reflect the change.As we discussed loosely coupled design above ,we can call this tightly coupled design.
If we don't segregate data access functionality then model has the additional responsibilities of accessing the data source and mapping the data to the business entities apart from its main responsibility of encapsulating the business logic This makes unit testing the model difficult since to test the model we need to access the data source also.
If we want to implement application data access rules then there is no central location to make that change , instead the change has to be done in multiple places which is a time consuming process and the code is also difficult to maintain.
Repository is a design pattern which mediates between the domain and data layers.It helps in both data retrieval and persistence.
Using the repository we segregate data access and mapping responsibilities ,so this makes the model and data access logic loosely coupled.
If we use a respository then the model layer asks the Resspository for any data that it wants to fetch or update. The responsibility of retrieving and updating the data in the data source is lies only with the repository. We can easily swap the backend data source without effecting the rest of the application.
Suppose our design decisions makes us change the data source ,as is common in agile methodology, later in the life cycle then we can easily do so if we are using the repository pattern.
Business logic communicates with the Repository in following manner
- Our Business logic requests for the data in the form of Business entities from the repository.
- Repository takes the request from the Business logic.It makes request to the underlying data source
- Repository receives the raw data from the data source.
- Repository maps the data to the Business entities and returns the entities.
So now the model can concentrate only on the business logic without any concern about where and how the data is stored.
Unit of Work
Unit of Work is a design pattern which maintains a list of transactions ,updates the data source with the changes and also provides solution to the concurrency problems. So if the appliation is performing multiple concurrent operations then Unit of Work is the solution.
Though Frameworks like Entity Framework internally implement the Unit Of Work Pattern there are a few reasons for implementing the Unit Of Work in the application
- We are implementing transactions in our application and want to order the database inserts, deletes, and updates.
- We want to prevent the duplicate updates in our application. In an application we can have multiple update operations and some those might be duplicate.Using Unit of Work there will be only single database update instead of multiple.
- We want more control over the database operations in our application.
For example if we want to update student details and the teacher details together the we can achieve such level of control by using the Unit of Work design pattern.Using the Unit Of Work we can have complete control over the data access operations from a central location in our application.
Using the code
In the example application we will be creating an MVC application which maintains the list of students and provides options to update the list.
We will be implementing CRUD operations uisng the Unit of Work pattern.We will creating generic repository class so it can be resused for the different types of entities in our application.
IRepository<T> and Repository<T>
We are defining the interface for our generic repository class using the interface IRepository.It contains the CRUD operations for the insert ,search,update and delete functionality.
public interface IRepository<T>
{
T GetById(int id);
IQueryable<T> GetAll();
void Edit(T entity);
void Insert(T entity);
void Delete(T entity);
}
Now that we have declared the interface for the repository we can declare a generic class implementing our generic interface IRepository.The class has two fields DbContext and DbSet which are used to communicate with the database and represent the data respectively.
Below is the complete definition of the Repository class.
public class Repository<T> : IRepository<T> where T:class
{
public DbContext context;
public DbSet<T> dbset;
public Repository(DbContext context)
{
this.context = context;
dbset = context.Set<T>();
}
public T GetById(int id)
{
return dbset.Find(id);
}
public IQueryable<T> GetAll()
{
return dbset;
}
public void Insert(T entity)
{
dbset.Add(entity);
}
public void Edit(T entity)
{
context.Entry(entity).State = EntityState.Modified;
}
public void Delete(T entity)
{
context.Entry(entity).State = EntityState.Deleted;
}
}
UnitOfWork and IUnitOfWork
The UnitOfWork class maintains the list of the repositories in the application.IUnitOfWork interface defines the operations of the UnitOfWork class.
Notice that in the Save method we are calling the context.SaveChanges() method.In the Repository above we are just setting the entity state but not actually inserting in the database.It’s only the save method that updates the entities in the database so our UnitofWork class is incharge of the database update operations.
If we are using multiple repositories and any of the update fails , we don’t call the SaveChanges() method.As the records are not updated in the database in the repositories so there is no need to revert back our changes.
public interface IUnitOfWork : IDisposable
{
IRepository<Student> StudentRepository { get; }
void Save();
}
public partial class UnitOfWork : IUnitOfWork
{
private IRepository<Student> _studentRepository;
private Context _context;
public IRepository<Student> StudentRepository
{
get {
if (_studentRepository==null)
_studentRepository = new Repository<Student>(_context);
return _studentRepository; }
}
public UnitOfWork()
{
_context=new Context();
}
public void Save()
{
_context.SaveChanges();
}
public void Dispose()
{
throw new NotImplementedException();
}
}
We have a single controller StudentsController that access the UnitofWork classs to perform differnt operations depending on the user interaction.
We can perform different CRUD operations when we execute the sample application.
If we want to design a system in which ORM or the database can be easily switched and also want the ability to unit test our business logic which has external dependencies then respository pattern is the solution.
Points of Interest
Though MVC doesen't force us to follow specific design principles but since MVC applications are supposed to be flexible to future changes and testable we should use design principles which helps us create such applications.by using certain design guidelines we can create more robust and flexible applications.