Yogesh Verma

Repository and Unit of Work Patterns

By Yogesh Verma

Last updated cal_iconDecember 16, 2021

“A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. ” – Martin Fowler

 Repository acts as a mediator between the data source and the business layer of the application. It provides data to the business layer from the underneath data source, this source could be database, web service, file system or any other data source, which only repository is aware of. The upper layers of the application do not have information about where the data is coming from, they just call repository whenever they need data for application.

In its simplest implementation, we can conceptualize repositories like this, where we a repository for every business entity.

Benefits of Repository Pattern:

  • It centralizes the data logic
  • It provides a nice separation if concern for data access
  • It helps in eliminating duplicity of code
  • It provides a substitution point for the unit tests
  • It provides a flexible architecture that can be adapted as the overall design of the application evolves

Unit of Pattern

“A Unit of Work keeps track of everything you do during a business transaction that can affect the database. When you’re done, it figures out everything that needs to be done to alter the database as a result of your work” – Martin Fowler

When the data is pulling out of a database, it’s important to keep track of what has been changed, similarly we would wish to add new objects or delete some. Now with unit of work implemented, the unit takes care of tracking the changes and updates the database with all the changes in single commit.

A unit of work looks like this, where for a context the unit tracks changes, and all the repositories operates for that same context.

Trivia: If you have worked with ADO.Net you can relate UoF with disconnected Data table and Datasets. A user keeps on making changes to datasets and save the changes in one go when done with it.

Benefits of Unit of Work:

  • Separation of concerns
  • Making application more testable
  • Efficient data access
  • Reduces the database calls
  • Keeps business logic free from tracking changes
  • Manage concurrency problems
  • Manages transactions

Code Works:

Let’s look at some code works using C# and Entity Framework and see how it’s all tied together.

Repositories:

To start with, let’s create an interface for generic repository. This will have function definitions which are common to other domain entity repositories and it will act as a base repository for them.

public interface IRepository<TEntity> where TEntity : class
{
TEntity Get(int id);
IEnumerable<TEntity> GetAll();
IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
void Add(TEntity entity);
void Remove(TEntity entity);
}

If you notice, the get methods, these are returning IEnumerable and not IQueryable. Repositories should not return IQueryable. This is because you do not want to wrong impression to the upper layer that they could build queries, which is not correct, and goes against using the repository at the first place.

Also you will notice that we do not have any methods like Save in our repository. Then how are we going to save our data? Remember Mr. Martin’s definition of repositories; A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection…; this is where the unit of work pattern comes into picture, and we’ll see it later.

Now let’s implement the generic repository interface. I have implemented it in context of entity framework.

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext Context;
private DbSet<TEntity> dbSet;

public Repository(DbContext context)
{
Context = context;
dbSet = Context.Set<TEntity>();
}
public TEntity Get(int id)
{
return dbSet.Find(id);
}

public IEnumerable<TEntity> GetAll()
{
return dbSet.ToList();
}

public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return dbSet.Where(predicate);
}

public void Add(TEntity entity)
{
dbSet.Add(entity);
}

public void Remove(TEntity entity)
{
dbSet.Remove(entity);
}
}

Next we will create repositories for our entities, and define functionalities which are specific to these entities.

Interface

public interface IPersonRepository : IRepository<Person>
{
IEnumerable<Person> GetAwesomePersons();
}

…and its implementation

public class PersonRespository : Repository<Person>, IPersonRepository
{
public PersonRespository(DbContext context)
: base(context)
{

}
public IEnumerable<Person> GetAwesomePersons()
{
var persons = Find(p => p.IsAwesome);
return persons;
}
}

Unit Of Work:

Now we will design our unit of work. This will have properties for our entity repositories, so that it can communicate to database through them, and will update database with the changes made on these repositories.

Interface

public interface IUnitOfWork : IDisposable
{
IPersonRepository Persons { get; }

//TODO: Properties for other repositories

void Commit();
}

…and its implementation

public class UnitOfWork : IUnitOfWork
{
//The context property is your DBContext object
//The repositories will execute on this context
private readonly MyAppContext context;

public IPersonRepository Persons { get; private set; }

//…more properties for other repositories

public UnitOfWork(MyAppContext context)
{
this.context = context;
Persons = new PersonRespository(this.context);
}
public void Commit()
{
this.context.SaveChanges();
}

public void Dispose()
{
if (this.context != null)
{
this.context.Dispose();
}
}
}

And when execute a use case we will execute it within a unit, all changes to database, and committing it to database when done. 

public class PersonController
{
public ImportantMethod()
{
using (var unitOfWork = new UnitOfWork(new MyAppContext()))
{
unitOfWork.Persons.Add(new Person { IsAwesome = true });

// TODO: More operations
//…some more operations

//Commit all at once
unitOfWork.Commit();
}
}
}

The examples in the post are kept simple with focus on getting the patterns right. Things can vary in different contexts and the implementation can be improved, like using IOC container for resolving dependencies etc.

Get In Touch

How Can We Help ?

We make your product happen. Our dynamic, robust and scalable solutions help you drive value at the greatest speed in the market

We specialize in full-stack software & web app development with a key focus on JavaScript, Kubernetes and Microservices
Your path to drive 360° value starts from here
Enhance your market & geographic reach by partnering with NodeXperts