Unit of Work implementation in the context of DDD, using EF and Unity

I’m going to keep the introduction short as I believe the title is pretty self-explanatory but I do want to emphasize from the beginning that the goal here wasn’t to create a ‘One to rule them all’ implementation of the Unit of Work pattern. There is a variety of feature-rich implementations online but before going for the ‘ultimate’ one I would advise following the Yagni principle. Start simple, learn what you need and adapt.

The main goal here is to show how this pattern fits with a DDD mindset and how it can be easily implemented using the technologies mentioned. I will demonstrate this in two popular scenarios:

  • A web application using ASP.NET Web API – the UI
  • A console application – the background process

Full source code available at: https://github.com/florindpreda/UnitOfWork.EF

But Entity Framework already implements this pattern behind the scenes, as well as the repository pattern, so why bother with another abstraction on top of this?

Yes, it does but it comes with a cost. It tightly couples the application with infrastructure concerns, in this case the database access. Depending on the size and expected complexity of the application it might be a trade-off to consider but I would argue this separation of concerns can be achieved with minimal to moderate effort. To find out more about the advantages of this approach you can read about the Hexagonal and the Onion architectural patterns. My implementation is based on those principles.

Definition

Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.

More about this pattern here.

Looking at the definition we can see that Entity Framework’s DbContext already does the following:

  • Maintains a list of objects affected
  • Coordinates the writing out of changes
  • Resolution of concurrency problems

So with this in mind, there are two things left do to:

  1. Understand what a business transaction is in DDD. Where does it start and end?
  2. Use the DbContext in the appropriate components to ensure the business transaction consistency while preserving a clean separation of concerns between the application layers.

What is a business transaction?

A common sense definition would be: a group of business operations/rules that must be consistent, i.e. they either succeed or fail as a unit.

In DDD business operations/rules are enforced in Entities, Value Objects and Domain Services. The orchestrators , i.e. who is executing the business operations, are the Application Services. They provide the entry points to the application core and reflect the application use-cases. Each use-case could be invoked from multiple clients, e.g. A web interface, a background service, an external service, etc., via requests and it is expected that after each request the application maintains its consistency. This means that the request (and implicitly the use-case executed), either succeeds or fails, which implicitly means that *all business operations within the use case should be consistent.

Therefore the Unit of Work starts at the beginning of the use-case and finishes at the end of the use-case, and all this happens inside the Application Services.

*There are scenarios where Eventual Consistency makes more sense (long running processes, cross bounded-context synchronisation, etc.) if the existing infrastructure allows it but this is an entirely different topic. I’m going to assume this is not an option here.

Implementation

The chosen scenario is pretty simple. We have two Aggregates containing just the underlying Aggregate Roots, Product and ProductReview. This is how they look like:

ClassDiagram
Model

Each time a Product is deleted then all its Reviews should be deleted as well and we want this operations to be transactionally consistent. The ‘Delete’ is implemented as a logical delete (an ‘IsDeleted’ flag is updated) just because I found it easier to evaluate the results but a physical delete would work the same.

As a side note, the argument for not making a ‘ProductReviews’ collection inside the Product class is based on two assumptions:

  1. There are no true invariants between these two entities. For example, modifying the ‘Name’ of the Product does not require any change in the reviews. Same for ProductReview. If I update the ‘Text’ of a review there’s no reason to update the Product itself.
  2. The potential aggregate cost is high. Imagine a very popular Product with thousands of reviews. Even with Lazy Loading enabled, each time a review needs to be updated the whole object graph will need to be loaded (Product + ProductReviews).

So, as these are individual aggregates each will have its own repository. The interfaces will sit in the Domain layer, and the implementations in the Infrastructure layer. As we’re using EntityFramework, each repository implementation will use the DbContext object for manipulating the persisted entities. However, we won’t be committing the changes to the database in the repositories as this will be the responsibility of the Unit of Work. Here’s how they look like:

namespace UnitOfWork.EF.Infrastructure
{
	public class ProductRepository: IProductRepository
	{
		private readonly IDatabaseContext _databaseContext;

		public ProductRepository(IDatabaseContext databaseContext)
		{
			_databaseContext = databaseContext;
		}		

		public Product GetById(Guid productId)
		{
			return _databaseContext.Products.SingleOrDefault(p => p.Id == productId);
		}

		public void Update(Product product)
		{
			_databaseContext.Entry(product).State = System.Data.Entity.EntityState.Modified;
		}		
	}

	public class ProductReviewRepository: IProductReviewRepository
	{
		private readonly IDatabaseContext _databaseContext;

		public ProductReviewRepository(IDatabaseContext databaseContext)
		{
			_databaseContext = databaseContext;
		}

		public IEnumerable<ProductReview> GetByProductId(Guid productId)
		{
			return _databaseContext.ProductReviews.Where(pr => pr.ProductId == productId);
		}

		public void Update(ProductReview productReview)
		{
			_databaseContext.Entry(productReview).State = System.Data.Entity.EntityState.Modified;
		}
	}
}

 

The Unit of Work sits in the Infrastructure layer and will use the DBContext object as well but, unlike the repositories, it will only focus on transaction management, i.e. calling the SaveChanges() method.

namespace UnitOfWork.EF.Infrastructure
{
	public class UnitOfWork : IUnitOfWork
	{
		private readonly IDatabaseContext _databaseContext;
		private bool _disposed = false;

		public UnitOfWork(IDatabaseContext databaseContext)
		{
			_databaseContext = databaseContext;
		}

		public void Commit()
		{
			if (_disposed)
			{
				throw new ObjectDisposedException(this.GetType().FullName);
			}
			
			_databaseContext.SaveChanges();
		}

		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (_disposed) return;

			if (disposing && _databaseContext != null)
			{
				_databaseContext.Dispose();
			}

			_disposed = true;
		}
	}
}

 

The Application Service coordinates the business operations for each use-case using the unit of work and the repositories. Here is the implementation of the ‘Delete product’ use-case:

namespace UnitOfWork.EF.Application
{
	public class ProductService : IProductService
	{
		private readonly IUnitOfWork _unitOfWork;
		private readonly IProductRepository _productRepository;
		private readonly IProductReviewRepository _productReviewRepository;

		public ProductService(IUnitOfWork unitOfWork, IProductRepository productRepository, IProductReviewRepository productReviewRepository)
		{
			_unitOfWork = unitOfWork;
			_productRepository = productRepository;
			_productReviewRepository = productReviewRepository;
		}

		public void Delete(DeleteProductCommand command)
		{
			using (_unitOfWork)
			{
				var product = _productRepository.GetById(command.Id);
				product.Delete();
				_productRepository.Update(product);

				var productReviews = _productReviewRepository.GetByProductId(product.Id);
				foreach (var productReview in productReviews)
				{
					productReview.Delete();
					_productReviewRepository.Update(productReview);
				}

				_unitOfWork.Commit();
			}
		}
	}
}

The two business operations in the use-case above are:

  1. Deleting a Product
  2. Deleting the associated ProductReviews

You probably noticed that the UnitOfWork class implements the Basic Dispose Pattern and that a using statement wraps the _unitOfWork object. This effectively triggers the disposal of the underlying DbContext. Is this really necessary? Apparently not, but I see it as a good practice since DbContext actually implements IDisposable.

Now, the most important aspect of the implementation is to make sure that during the execution of each application use case (usually an Application Service class method) the repositories and the unit of work objects get injected with the same instance of the DbContext. This is essential in order to group all the entities changes into the same transaction when DbContext.SaveChanges() is called.

For web applications this can be achieved by using a PerHttpRequestLifetimeManager like the one below:

namespace UnitOfWork.EF.DependencyResolver
{
	public class PerHttpRequestLifetimeManager : LifetimeManager
	{
		private readonly Guid _key = Guid.NewGuid();

		public override object GetValue()
		{
			return HttpContext.Current.Items[_key];
		}

		public override void SetValue(object newValue)
		{
			HttpContext.Current.Items[_key] = newValue;
		}

		public override void RemoveValue()
		{
			var obj = GetValue();
			HttpContext.Current.Items.Remove(obj);
		}
	}
}

This ensures that there will be a shared instance of the same object, in this case an instance of DbContext, for each incoming http request.

For background processes we can use the built-in ContainerControlledLifetimeManager to ensure we get the same instance of the DbContext each time an application use-case is executed.

Here is how everything is wired up in the DI container:

namespace UnitOfWork.EF.DependencyResolver
{
    public class UnityConfig
    {
		public static void RegisterComponents(UnityContainer container)
		{			
			if (HttpContext.Current != null)
			{
				container.RegisterType<IDatabaseContext, DatabaseContext>(new PerHttpRequestLifetimeManager());
			}
			else
			{				
				container.RegisterType<IDatabaseContext, DatabaseContext>(new ContainerControlledLifetimeManager());
			}

			container.RegisterType<IUnitOfWork, UnitOfWork.EF.Infrastructure.UnitOfWork>();
			container.RegisterType<IProductRepository, ProductRepository>();
			container.RegisterType<IProductReviewRepository, ProductReviewRepository>();
			container.RegisterType<IProductService, ProductService>();
			container.RegisterType<IDatabaseConfiguration, EntityFrameworkConfiguration>();
		}
    }
}

As you can see the RegisterComponents method above gets a container as a parameter. Why? Because this is a centralized DI configuration extracted in a separate project. We don’t want to tightly couple this configuration with a specific Adapter of the application (e.g. Web API project, a Background Service project, etc) but instead we want to be able to easily reuse it regardless from where the application use-cases are triggered. Therefore, inside each of these Adapters we’re creating an empty Unity container which we then pass as a parameter to the DI centralized configuration project.

This is the local Unity container for the Web API project:

namespace UnitOfWork.EF.API
{
    public static class UnityConfig
    {
		private static UnityContainer _container = new UnityContainer();

        public static void RegisterComponents()
        {
			UnitOfWork.EF.DependencyResolver.UnityConfig.RegisterComponents(_container);
            GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(_container);
        }

		public static UnityContainer Container
		{
			get
			{
				return _container;
			}
		}
    }
}

And, as you probably know, for web applications the RegisterComponents method will be called in the Global.asax Application_Start method:

namespace UnitOfWork.EF.API
{
	public class WebApiApplication : System.Web.HttpApplication
	{
		protected void Application_Start()
		{
			AreaRegistration.RegisterAllAreas();

			//IoC configurations
			UnityConfig.RegisterComponents();

			GlobalConfiguration.Configure(WebApiConfig.Register);
			FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
			RouteConfig.RegisterRoutes(RouteTable.Routes);
			BundleConfig.RegisterBundles(BundleTable.Bundles);

			//creates and seeds the database at first run
			UnityConfig.Container.Resolve<IDatabaseConfiguration>().Initialise();
		}
	}
}

 

In a similar way, the local Unity container for the Background worker project:

namespace UnitOfWork.EF.BackgroundWorker.Startup
{
	public static class UnityConfig
	{
		private static UnityContainer _container = new UnityContainer();

		public static void RegisterComponents()
		{
			UnitOfWork.EF.DependencyResolver.UnityConfig.RegisterComponents(_container);
		}

		public static UnityContainer Container
		{
			get
			{
				return _container;
			}
		}
	}
}

And as we don’t have a Global.asax in a console app, the RegisterComponents method will be called at the beginning of the Main method, which is the entry point for a console app:

namespace UnitOfWork.EF.BackgroundWorker
{
	class Program
	{
		static void Main(string[] args)
		{
			while (true)
			{
				UnityConfig.RegisterComponents();

				var worker = UnityConfig.Container.Resolve<Worker>();
				worker.Run();

				Thread.Sleep(5000);
			}
		}
	}
}

In the scenario above I’m assuming there’s no external timer that triggers the execution of the background process, like there is for Azure Web Jobs for example. Therefore, everything runs inside a while statement and before each execution of a use-case we re-configure the UnityContainer to get a fresh DbContext instance for the Unit of Work and the repositories.

In action

1. Running the UnitOfWork.EF.API project will automatically create a LocalDb database called ProductDatabase with to pre-populated tables:

Products
Products

 

 

 

ProductReviews
ProductReviews

 

 

 

 

 

 

 

2. To delete a product via the Web API we just need to send a HTTP DELETE request containing the product Id. An easy way to do this is using Fiddler’ composer.

Fidder_UnitOfWork

 

 

 

 

 

This will hit the following Delete method in the ProductController which will execute the ‘Delete Product’ use-case:

namespace UnitOfWork.EF.API.Controllers
{
	public class ProductController : ApiController
	{
		private readonly IProductService _productService;

		public ProductController(IProductService productService)
		{
			_productService = productService;
		}

		...

		// DELETE api/values/5
		public void Delete([FromUri]DeleteProductCommand command)
		{
			_productService.Delete(command);			
		}
	}
}

And the result is that the Product and all the associated Reviews are deleted as shown:

UnitOfWork_Results1

 

 

 

 

 

 

 

 

3. To delete a product via the background worker we just set the Id of the product we want to delete and run the console app.

namespace UnitOfWork.EF.BackgroundWorker
{
	public class Worker : IWorker
	{
		private readonly IProductService _productService;

		public Worker(IProductService productService)
		{
			_productService = productService;
		}

		public void Run()
		{
			var productId = Guid.Parse("287278A8-AA52-4B34-A96F-5C94967F3C58");//replace with an actual product Id from the database
			var deleteProductCommand = new DeleteProductCommand() { Id = productId };

			_productService.Delete(deleteProductCommand);
		}
	}
}

And the results:
 

UnitOfWork_Results2

 

 

 

 

 

 

 

 

Conclusion

To summarize, we’ve seen:

  • That the Unit Of Work pattern groups a set of business operations inside a single database transaction.
  • In the context of DDD and the Hexagonal/Onion architectural patterns, the Unit of Work is used inside the Application Services.
  • How it can be implemented using EF and Unity while maintaining loose coupling between the application core and infrastructure concerns.

Full source code available at: https://github.com/florindpreda/UnitOfWork.EF

2 Comments

  • Cephas PAD

    12th June 2017 at 9:39 am Reply

    Thanks for the posting.

    In fact, I have two questions that need to be asked.

    1. Your Infrastructure project implemented Domain project. Does it violate the hierarchy of DDD which should be Infrastructure > Domain > Application > Presentation ?

    2. The idea that not making a ‘ProductReviews’ collection is new to me. Please give more about this. is that really helpful?

    Thanks in advanced

    • Florin Preda

      1st July 2017 at 2:12 pm Reply

      Hi,

      1. In DDD projects the Domain should be at the core of your application and therefore should not depend on any other layer. If you want to represent this as an n-tier architecture it would be
      Presentation>Application>Domain.
      Consider the Infrastructure external and don’t make any of the other layers depend on it. Use a Dependency Resolution project for the DI wiring.

      2. Apart from Domain Driven Design considerations, the simple benefits is performance. If you have 100k Product Reviews that’s a very big collection to carry around inside the Product entity. If you want to update one ProductReview you will have to fetch all of them via the Product entity. Also, this approach doesn’t allow you to do server side paging.

      Thanks

Post a Reply to Cephas PAD Cancel Reply