Practical usage of the Unit Of Work & Repository patterns
Asked Answered
P

3

21

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?
Pegeen answered 19/5, 2011 at 9:40 Comment(2)
Just curious, why building ORM when there is several good ones out there?Housewarming
Just trying to apply ORM best practises to PHP. There are a few ones already, but not exactly matching my expectations.Pegeen
O
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.

Omniumgatherum answered 28/5, 2011 at 4:30 Comment(1)
Thanks for this complete answer. As a conclusion, there is no "best" way to do this, it's a matter of choice in the global context of the ORM.Pegeen
C
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.

Cameo answered 23/5, 2011 at 8:45 Comment(0)
C
2

It depends on what your work boundaries are going to be. If they are going to span multiple repositories then you might have to create another abstraction to ensure that multiple repositories are covered. It would be like a small "service" layer that is defined in domain-driven design (DDD).

If your unit of work is going to be pretty much per Repository then I would go with the second option.

My question, however, to you would be, how can you worry about repository when writing an ORM? They are going to be defined and used by the consumers of your Unit of Work right? If so, you have no option but to just provide a Unit of Work and your consumers will have to enlist the repositories with your unit of work and will also be responsible for controlling the boundaries of unit of work. Isn't it?

Crawfish answered 19/5, 2011 at 13:58 Comment(2)
Thanks Nilesh. Actually my plan is to define a clean interface for all of these patterns, along with a base implementation which aims to be very generic and adapt most of the situations for our everyday work. I've already got a working implementation of this, with models & data mappers along with a mapper registry, which provides access to all mappers in one single class; but I'm trying to push the boundaries to provide support for a Repository holding an Identity Map, with support for Criteria searches, and for a Unit Of Work, to manage updates to the database & to handle transactions.Pegeen
Sorry to be so verbose, I need more space :) So I'd like to find a generic idea of how to put it all together! Your help is much appreciated.Pegeen

© 2022 - 2024 — McMap. All rights reserved.