Question

Practical usage of the Unit Of Work & Repository patterns

I'm building an ORM, and try to find out what the exact responsibilities of each pattern are. Let's say I want to transfer money between two accounts, using the Unit Of Work to manage the updates in a single database transaction.

Is the following approach correct?

  1. Get them from the Repository
  2. Attach them to my Unit Of Work
  3. Do the business transaction & commit?

Example:

from = accountRepository.find(fromAccountId);
to = accountRepository.find(toAccountId);

unitOfWork.attach(from);
unitOfWork.attach(to);    

unitOfWork.begin();
from.withdraw(amount);
to.deposit(amount);
unitOfWork.commit();

Should, as in this example, the Unit Of Work and the Repository be used independently, or:

  • Should the Unit Of Work use internally a Repository and have the ability to load objects?
  • ... or should the Repository use internally a Unit Of Work and automatically attach any loaded entity?
 21  7719  21
1 Jan 1970

Solution

 18

The short answer would be that the Repository would be using the UoW in some way, but I think the relationship between these patterns is less concrete than it would initially seem. The goal of the Unit Of Work is to create a way to essentially lump a group of database related functions together so they can be executed as an atomic unit. There is often a relationship between the boundaries created when using UoW and the boundaries created by transactions, but this relationship is more coincidence.

The Repository pattern, on the other hand, is a way to create an abstraction resembling a collection over an Aggregate Root. More often than not the sorts of things you see in a repository are related to querying or finding instances of the Aggregate Root. A more interesting question (and one which doesn't have a single answer) is whether it makes sense to add methods that deal with something other than querying for Aggregates. On the one hand there could be some valid cases where you have operations that would apply to multiple Aggregates. On the other it could be argued that if you're performing operations on more than one Aggregate you are actually performing a single action on another Aggregate. If you are only querying data I don't know if you really need to create the boundaries implied by the UoW. It all comes down to the domain and how it is modeled.

The two patterns are dealing at very different levels of abstraction, and the involvement of the Unit Of Work is going to be dependent on how the Aggregates are modeled as well. The Aggregates may want to delegate work related to persistence to the Entities its managing, or there could be another layer of abstraction between the Aggregates and the actual ORM. If your Aggregates/Entities are dealing with persistence themselves, then it may be appropriate for the Repositories to also manage that persistence. If not, then it doesn't make sense to include UoW in your Repository.

If you're wanting to create something for general public consumption outside of your organization, then I would suggest creating your Repository interfaces/base implementations in a way that would allow them to interact directly with your ORM or not depending on the needs of the user of your ORM. If this is internal, and you are doing the persistence work in your Aggregates.Entities, then it makes sense for your Repository to make use of your UoW. For a generic Repository it would make sense to provide access to the UoW object from within Repository implementations that can make sure it is initialized and disposed of appropriately. On that note, there will also be times when you would likely want to utilize multiple Repositories within what would be a single UoW boundary, so you would want to be able to pass in an already primed UoW to the Repository in that case.

2011-05-28

Solution

 4

I recommend you to use this approach when the repository uses UoW internally. This approach has some advantages, especially for a web application.

In a web application, the recommended pattern of using UoW is Unit of Work (session) per HTTP request. So if your repositories will share UoW, you will be able to use the first level cache (using identity map) for object that were requested by other repositories (like data dictionaries that are referenced by multiple aggregates). Also, you will have to commit only one transaction instead of multiple, so it will work much better in terms of the performance.

You could take a look at Hibernate/NHibernate source code that is mature ORMs in Java/.NET world.

2011-05-23