Various topics from software architecture part 3: the unit of work

Introduction

In the previous post we looked at a basic implementation of the Repository pattern. We said that this pattern is often used in large layered projects where persistence ignorance of the domain layer is a requirement. It is also very useful in unit testing where you want to avoid calling a real data store and instead create mocks or stubs for the repository.

In this post we’ll look at another concept that comes up in conjunction with data access: the unit of work. We’ll also refer back to the previous post on the Repository pattern here and there as the Unit of Work pattern is often used in conjunction with repositories.

This post will be more concerned with abstractions. The next post will continue with some implementation stubs.

Some of the explanations are taken directly from the series on domain driven design so they might look familiar if you’ve seen that.

The unit of work

The purpose of a Unit of Work is to maintain a list of objects that have been modified by a transaction. These modifications include insertions, updates and deletions. The Unit of Work co-ordinates the persistence of these changes and also checks for concurrency problems, such as the same object being modified by different threads.

This may sound a bit cryptic but if you’re familiar with EntityFramework or Linq to SQL then the object context in each technology is a good example for an implementation of a unit of work:

DbContext.AddObject("Customers", new Customer());
DbContext.SaveChanges();

DbContext.Customers.InsertOnSubmit(new Customer());
DbContext.SubmitChanges();

In both cases the DbContext, which takes the role of the Unit of Work, first registers the changes. The changes are not persisted until the SaveChanges() – EntityFramework – or the SubmitChanges() – Linq to SQL – method is called. Note that the DB object context is responsible for registering AND persisting the changes.

The Unit of Work pattern can be represented by the following interface:

public interface IUnitOfWork
{
    void RegisterUpdate(IDomain aggregateRoot);
    void RegisterInsertion(IDomain aggregateRoot);
    void RegisterDeletion(IDomain aggregateRoot);
    void Commit();
}

…where we saw IDomain in the previous post:

public interface IDomain
{
}

It simply marks your domain objects, like here:

public class Customer : IDomain
{
	public int Id { get; set; }
	public string Name { get; set; }
	public Address Address { get; set; }	
}

Notice that we have separated the registration and commit actions. In the case of EntityFramework and similar ORM technologies the object that registers and persists the changes will often be the same: the object context or a similar object.

Necessity

An important question to ask whether it’s even necessary to deal with the unit of work pattern in modern .NET applications. After all chances are high that you’ll have MS SQL as your backing store and EntityFramework is a modern tool whose object context already IS a unit of work so why would you ever abstract it away from the repository layer? One important advantage is that you can switch between data stores if you’ve abstracted away their units of work.

However, will that ever happen in reality in an enterprise project? Software projects tend to be quite rigid as far as their data stores are concerned. A data store change could potentially weeks or even longer to implement. All the data in your current data store would need to be transferred into another storage mechanism without data loss. You’ll also have to write the new data access logic in code to accommodate the alternative technology. Switching between the technologies will then be easy, you just provide a different concrete implementation for the IUnitOfWork interface, but the way to that is cumbersome. If you search for “unit of work” then you’ll find see that the benefits of this pattern are often debated.

Having a unit of work can still be beneficial in the following cases:

  • Your storage mechanism doesn’t have an implemented object context like that in EntityFramework. E.g. MongoDb .NET provides a lot of tools to work with MongoDb files but not a true unit of work implementation
  • You are not sure what data store you’d like to use in a new project. You can then build 2 or more concrete repository layers with a few domain objects and test their performance

Keep in mind that Persistence Ignorance can be achieved with the repository pattern only. You’ll need to decide whether the added complexity of an IUnitOfWork interface and its advantages are worth the effort.

The unit of work repository

In fact we can still go a step further and totally abstract away the mechanism that will commit the changes registered by the unit of work. We mentioned above that registering the changes in a domain and committing these changes are separated in the IUnitOfWork interface. In EF the object context can handle the same like we saw in the code example:

DbContext.AddObject("Customers", new Customer());
DbContext.SaveChanges();

However, this is not always the case. It is reasonable that you are not fond of these automation tools and want to use something more basic where you have the freedom of specifying how you track changes and how you persist them: you may register the changes in memory and persist them in a file. Or you may still have a lot of legacy ADO.NET where you want to move to a more modern layered architecture. ADO.NET lacks a DbContext object so you may have to solve the registration of changes in a different way.

For those scenarios we need to introduce another abstraction, the IUnitOfWorkRepository:

public interface IUnitOfWorkRepository
{
	void PersistInsertion(IDomain domain);
	void PersistUpdate(IDomain domain);
	void PersistDeletion(IDomain domain);
}

…which can be stored in a common Infrastructure layer – a normal C# class library – so that all your other projects will have access to it.

Here’s an updated IUnitOfWork interface which takes into account the unit of work repository:

public interface IUnitOfWork
{
	void RegisterUpdate(IDomain domain, IUnitOfWorkRepository repository);
	void RegisterInsertion(IDomain domain, IUnitOfWorkRepository repository);
	void RegisterDeletion(IDomain domain, IUnitOfWorkRepository repository);
	void Commit();
}

The above Unit of Work pattern abstraction can be extended as above to give way for the total separation between the registration and persistence of aggregate roots.

Like in the case of the unit of work pattern you may ask yourself whether this is really necessary or it’s overkill.

That’s enough for now. We’ve seen some possible abstractions for the unit of work pattern. We’ll look at an implementation stub for each of them in the next post.

View the list of posts on Architecture and Patterns here.

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

2 Responses to Various topics from software architecture part 3: the unit of work

  1. Larisa says:

    Hi, Andras!

    Thanks a lot for your list of posts.

  2. Pingback: Architecture and patterns | Michael's Excerpts

Leave a comment

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.