4
votes

I'm been reading, and have now seen two different implementations of the Unit of Work pattern. The first pattern has the Repository talking to the Unit of Work to a domain object.

Unit of Work Repository called

The other implementation has the Service layer registering the domain object as modified:

Unit of Work Service Layer called

I guess my question is, what are the benefits/drawbacks to each? I know that is lot to answer without providing some implementation code for the repositories/mappers/etc... but in general, who/what should really be responsible for "newing up" the UoW and then working with it?

My thought is if you let the Repository handle it, it should be provided as a injectable interface to the repository(s) so the same UoW can span multiple repositories (aka, multiple domain objects)...

If the service layer handles it, then you're kind of "stuck" with only one UoW implementation per Service Layer call (example, ServiceLayer.AddCustomer(customer)).

In the web world, I can't see multiple service layers being called for different domain objects... but maybe in the non-web world I can.

I imagine something eventually has to call "commit()", so it makes the most sense to have that tied into the Service Layer.

Thanks, Mike

2

2 Answers

1
votes

Even in the first instance, your UnitOfWork will most likely be a singleton (per thread), so that even if a CustomerRepository and OtherBoilerplateExampleRepository both access the UnitOfWork before a commit is called, a commit will still use all changes made by all repositories.

That being said, I think the first pattern is the cleaner way of doing it. With the second way you're saying that the CustomerService has to both manipulate business objects and register the changes with the UnitOfWork. That violates separation of concerns a bit. Also, not shown in these examples, but the Repository pattern provides a place to cache data objects, as well.

As far as injecting dependencies, I think rather than making the UoW an injectable interface, making the Mapper pattern (that you see in the second example but is presumably implied in the first) injectable is the more appropriate choice. No matter if you're working with an SQL DB, XML file, etc., the UoW transaction management functions should stay the same.

1
votes

Exposing the UoW to clients is typically a stronger abstraction over the storage implementation than using a Repository since the semantics are state-based instead of explicit "load/save/delete" actions. On the other hand, explicitness allows clients greater control over what really happens over-the-wire through use of specifications etc.

IMO there is no "correct" approach, choosing the most appropriate pattern really depends on the application's data flows and the persistance layer constraints.

A few thoughts-

  1. A Repository typically manages a single aggreggate root, while a UoW can cover multiple.

  2. Ideally, the UoW should be persistant-agnostic (since it deals mostly with object state such as change tracking). Your mapper will obviously be tailored to your storage implementation. But where are the other storage concerns/responsibilities addressed? The Repository is usually the answer to that.

  3. Repository pattern makes it easier to extend the storage solution with specialized semantics (by adding new methods), or tweaking internal implementations (for example calling a sproc in certain scenarios instead on dynamic code)

For an application with straighforward and homogenous storage requirements, directly exposing the UoW is a probably a simpler model. For more complex environments (such as mixed usage of dynamic SQL and sprocs, legacy schemas, et) then Repository is more maintainable and extensible, at the cost of being a bit more restrictive to use.

This is off the top of my head, hope it helps...