Avoid Unit of Work pattern in domain driven design
Asked Answered
S

3

26

I have read this and it makes me think twice...:

"Avoid unit of work pattern. Aggregate roots should define transaction boundaries."

Why should someone avoid the UOW pattern applying domain driven design?

Sluggish answered 4/2, 2013 at 22:7 Comment(0)
C
34

(Before my post I recommend to read this chapter of "Implementing Domain-Driven Design" book by V. Vernon. It can help to get close with aggregates and contain long answer on your question.)

In a properly designed system one command changes one aggregate at a time, every aggregate has boundaries which defined by invariants in aggregate root. So when you do any changes on aggregate, invariants are checked and changes are applied (or not) in one transaction. It's transaction consistency. Do you need to use Unit of Work here? Don't think so.

But quite often we are in situation when more then one aggregate need to be changed at one time. Transactions become larger, they touch more then one part of a system and we talk about eventual consistency. UoW is a good helper in this case.

As it has been mentioned with no context it's hard to guess what author was thinking, but I suppose he told about transaction consistency case. In distributed system you will need to use something like UoW to provide eventual consistency to a system.

Cetane answered 5/2, 2013 at 8:33 Comment(7)
+1 I agree. My supposition is that the author was probably trying to steer the user away from using the Unit of Work pattern for maintaining consistency with everything which is the type of trap programmers tend to fall into when we get excited about a tool. I assume he is just trying to direct the user towards using the aggregates to maintain consistency whenever possible.Consumable
In his paper on aggregate design, Vernon mentions UoW as one of the ways to manage transactional consistency (dddcommunity.org/library/vernon_2011 - see footnote on page 4 part 1). "Avoid unit of work pattern" seems like a strange recommendation to me - a misinterpretation of Vernon's approach ?Sienese
Old answer, but a very valid use of transactions within the aggregate is for publishing events within a CQRS+ES implementation. To reach the state of eventual consistency, for example, not only should the command complete, but the events should be persisted through whichever publishing mechanism is used. If the event cannot be published/dispatched, consistency will not be achieved. Even without ES, it does not explicitly hurt anything - as long as it is not used to cross the aggregate boundaries, such as having a single command change the transitional state of multiple aggregates.Equilibrist
Aggregates are database-centric and define scope of a transaction, however with modern distributed systems a Unit Of Work can represent an ACID operation on: Saving Aggregate, Sending Commands, Emitting Events. I.e. it's essential to have a UoF.Tamathatamaulipas
@SergeSemenov How are Aggregates database-centric and not domain-centric?Parenthesis
@Gabriel, yes, aggregates are a domain concept. Just wanted to highlight that they always align with a DB transaction scope. However, we need to keep thinking about command and events, which should be also a part of transaction to solve consistency problem of a distributed system. Just focusing on aggregates and ignoring communication primitives will be a DB-centric approach. Thus UoW is highly recommended. One of the solutions is described in "Life beyond distributed transactions" by Pat Halland and shown by Jimmy Bogard.Tamathatamaulipas
Hi, I stumbled upon this decision whether to use UoW or not in my system. I can see that one pitfall of introducing developers to UoW if they are not familiar with DDD. As someone who is very new to DDD, it had been unclear to me which entities of my domain are aggregate roots. When using UoW, it's tempting to do transactions on multiple entitties (that are not aggregates) - because it seems that they guarantee consistency across the related entities. However, if these entities are within one aggregate, this should not have been necessary in the first place, as you mentioned..Taiwan
T
7

Basically, according to M. Fowler, the UoW is "just" a smart persistence tool (however complex this task may be). So IMHO there is no intrinsic incompatibility with the DDD approach, which gives guidelines more about the "spirit" of your doman modeling than about technical tools.

With no context, it's hard to tell what the author of the citation was thinking; but maybe he wrote this because when using UoW, it is often difficult to enable your entities to manage their own lifecycle (as well as others'), typically with persistence and transactional behaviour.

As a matter of fact, it is possible to use the UoW pattern in a DDD-style applications with AOP. With this kind of tools, it becomes possible to keep the DDD spirit, with entity-centric, business-capable domain model(s), while leveraging complex yet business-orthogonal mechanisms to achieve proper transactional persistence.

Typically, in the Java world, you may use in your DDD app:

These give DDD-ready (and heavily-@nnotated ;]) entities.

Tongue answered 4/2, 2013 at 23:30 Comment(0)
W
3

First up, an aggregate is an object for holding a set of loading entities, for a command to be applied to.

  • Entities can belong to many different aggregates.
  • Each command can only talk to one aggregate, which should have all the entities the command needs on it.
  • Aggregates can be reused between commands.

A "Unit of Work" is basically an on-the-fly aggregate, allowing a single transactional update to a set of entities, without having to explicitly define in code an aggregate to do this.

This comes at the cost of not having an explicit Aggregate class on which to put mutation methods which protect your invariants.

The top voted answer contains:

In a properly designed system one command changes one aggregate at a time, every aggregate has boundaries which defined by invariants in aggregate root. ...

But quite often we are in situation when more then one aggregate need to be changed at one time. Transactions become larger, they touch more then one part of a system and we talk about eventual consistency. UoW is a good helper in this case.

This is nonsensical, because, by definition, an aggregate is the set of stuff needed for a transaction. If you find that you "need entities from two different aggregates", then actually what you need is to "define am additional aggregate which holds references to the entities required".

I think what the answer is trying to say is, "quite often we are in situation when more then one aggregate data on two different stores, which can't be updated in a single transaction, and therefore can't be added to a single aggregate need to be changed at one time. Transactions become larger, they touch more then one part of a system and we talk about eventual consistency. UoW is a good helper in this case."

Wagner answered 11/2, 2021 at 13:21 Comment(8)
you are mixing aggregate and aggregate roots.Broca
I don't think I amWagner
@Wagner Interesting answer. Suppose I take a classic problem of transferring money between accounts. AccountAggregate is an aggregate for the command CreateAccountCommand. If I want to transfer money, does it mean that I need to create another aggregate TranferMoneyAggregate which will contain two accounts and holds transactional consistency? Or fetching two separate AccountAggregate aggregates and putting transfer money logic into domain service is a better idea?Einsteinium
Yes, you create TransferMoneyAggregate (or even TwoAccountEntities aggregate, yuck) with sufficient (optimistically) locked data for the command. Interestingly this is what an ORM with Unit Of Work is actually doing on the fly when you lazy load two entities.Wagner
A downside of an ORM with UoW is that it removes hand written Save(some aggregate)methods on Repositories , thus restricting you to working only with data in the ORM mapped database when persisting. I.e. a custom Save could write a blob to S3, then write a database entry with the blobs id.Wagner
@mcintyre321, so if TransferMoneyAggregate contains two entities for both accounts what entity will be an aggregate root? Or aggregate root is TransferMoneyAggregate itself? And another question, if I need to change some property on Account, does it mean that Account becomes an aggregate too in this case?Einsteinium
@Wagner Usually, existing examples of DDD Aggregates are named like Order, Car, Backlog (nouns), where TransferMoneyAggregate sounds like action. It confuses me with use-cases. What is the point of use-cases then? Or am I missing something?Einsteinium
Heh, TransferMoneyAggregate was your name, not mine! EntitiesRequiredForAMoneyTransferAggregate, TwoAccountsAggregate or any other name you like would do.Wagner

© 2022 - 2024 — McMap. All rights reserved.