Do we really need Unit of work?

133 views
Skip to first unread message

Szymon Pobiega

unread,
Jul 13, 2010, 1:43:41 PM7/13/10
to ncqrs-dev
Hej

I've recently seen Greg Young's session on DDD from NDC 2010. Besides
the session was great, one thing was especially worth remembering.
Greg said that when carefully applying DDD (which means carefully
partitioning domain into aggregates), there shouldn't be need for UoW.
In every transaction only one aggregate is (re)constructed and
persisted so all we need is a repository. There should not be need for
UoW to synchronize changes between separate repositories.

Currently, the workflow is:

using (var uow =
NcqrsEnvironment.Get<IUnitOfWorkFactory>().CreateUnitOfWork())
{
var o = uow.GetById<SomeObject>(id);
o.DoSomething();
}

without UoW, it would be:

var repo = NcqrsEnvironment.Get<IRepository>(); //in reality
repository would be probably injected
var o = repo.GetById<SomeObject>(id);
o.DoSomething();
repo.Save(o);

We must explicitly save aggregates, but this does not hurt us since in
most cases command service is responsible for this. The only problem
is when DoSomething() results in creating new aggregates. I think
ncqrs should not allow to do this since this is an illusion of
maintaining consistency across aggregate boundaries. While it can be
supported by SQL or RavenDB event stores, not every event store would
allow for this. Moreover, the illusion of consistency across
aggregates roots complicates scenarios where some aggregates use event
sourcing for persistence and some do not.

The case when one aggregate creates another one could be supported by
enabling aggregates to issue commands and this brings us to my
'messaging' spike which doesn't differentiate commands coming from
clients from this issued by aggregates (it calls them simply
messages).

What are your thoughts?

Rick van Lieshout

unread,
Jul 13, 2010, 3:14:07 PM7/13/10
to ncqr...@googlegroups.com
Currently I have a aggregate similar to a Batch. This Batch is able to split itself into multiple (smaller) batches.
The new batch should maintain a reference to its parent batch (parent-child relation). After the new child batch is created it should be given a location. I can't really see how I would do this using 'messages' to send commands.

Currently I plan the command-event stream like this:

C: CreateBatch
  E: BatchCreated (no parent)
C: SplitBatchToLocation
  E: BatchCreated (parent)
  E: BatchPlacedOnLocation

I see no problems for any EventStore here. However using messages I'd get:

C: CreateBatch
  E: BatchCreated (no parent)
C: SplitBatchToLocation
  E: BatchCreated (parent)
  C: SetBatchOnLocation    <-- What batch? Where is the EventSourceId? Commands do not return values.
    E: BatchPlacedOnLocation <-- How whould this get thrown?

I'm just starting with Ncqrs so I could miss the big picture here... but I do not see this (yet)...

Rick 

2010/7/13 Szymon Pobiega <szymon....@gmail.com>

Scott Reynolds

unread,
Jul 13, 2010, 3:22:43 PM7/13/10
to ncqr...@googlegroups.com
I'd say you need a unit of work I mean given the example where you want to call more than one event handler (e.g. When we save a order, we want to email the user, then we want to update our other database) this all needs to be transactional. 

However, I do really think the the framework needs to be more simple and less prescriptive as well (but that's another discussion). 

Pieter Joost van de Sande

unread,
Jul 13, 2010, 5:14:58 PM7/13/10
to ncqr...@googlegroups.com
Good question Szymon. 

I did not watched the presentation since I've terrible connectivity at my current hotel. I discussed this topic also with Greg two days ago. To be honest, I find this a difficult topic.

First let me try to explain why I choose for the unit of work in the first place.

One of the talks or that inspired me for the unit of work concept was from Dan Ingalls (back in the Smalltalk ages) where he talk about the Chaos Theory and software growth. Is short his statement was that when you call a method on an object, you have an intent, you want something to happen and that something is a concept. When you send a Register message to a Visitor object, you want a registration to happen as a concept. The implementation of this concept will change over time. In the future you maybe want to copy all visit history of the visitor to the new registered user object or create a separate company based on that information. The idea is that you should be able to add, remove or change the chain of operations that is triggered by the Register method call. To accomplish this, you need a context where in you do something and all the operation inside that context are tracked. He visioned that this is the only way to handle complexity over time.

Since we do not only have method calls (which are messages in Smalltalk) but also have commands, I question myself whether this applies on object or commands.


To get back to your question in a more concrete form. In your code example, where you don't even explicitly save your object I assume that a agg root can only raise one event? That feels a bit narrowed to me if this is true, but could be supported easily. If the event store does not support transactions, you are only allowed to accept a unit of work when it only contains one event; otherwise, more events.

Pieter Joost van de Sande

unread,
Jul 13, 2010, 5:32:02 PM7/13/10
to ncqr...@googlegroups.com
Scott,

On Tue, Jul 13, 2010 at 9:22 PM, Scott Reynolds <sc...@reynoldsphotography.com> wrote:
I'd say you need a unit of work I mean given the example where you want to call more than one event handler (e.g. When we save a order, we want to email the user, then we want to update our other database) this all needs to be transactional. 

You could accomplish this without a full transaction. When the CreateNewOrder commands gets executed, it created an new Order which causes the OrderCreatedEvent to happen. This event is saved into the store and you are good to go. That is the transaction scope you need. After the event is stored in the event store, you can now respond back to your user with a message "Thanks for placing the order, we will send you an confirmation shortly". The rest of this can now be processed asynchronously. The e-mail could be send a few seconds later or when the SMTP server is down, the confirmation mail will be send whenever this one is up again. The most important thing is that customers can still place orders, even when the SMTP server is not available.
 
However, I do really think the the framework needs to be more simple and less prescriptive as well (but that's another discussion). 

I would love to hear your feedback on that point!

Chris Chilvers

unread,
Jul 14, 2010, 2:32:42 PM7/14/10
to ncqrs-dev
I think we do, as a command can have multiple events, saving these
events should really be a single transaction. Also publishing to the
bus needs to be in the same transaction. If the message is handled
asynchronously then the scope of the transaction for the bus would be
to ensure the event is wrote to a local durable queue to then be sent
asynchronously.

On Jul 13, 10:32 pm, Pieter Joost van de Sande <p...@born2code.net>
wrote:

Szymon Pobiega

unread,
Jul 14, 2010, 3:06:47 PM7/14/10
to ncqr...@googlegroups.com
I thought about a solution where AggregateRoot maintains a collection of pending (not saved) events. This collection would be persisted by its repository when saving. 

My point was also that why bother having cqrs and event sourcing when we don't enforce asynchronous communication between aggregates? All we get in this case is audit log (event stream). Thre true power of cqrs and event sourcing (performance) comes from asynchronous communication between aggregates and if we do it, unit of work is not necessary.

Yes, saving multiple events coming from single command should be transactional. As for publishing to bus -- it depends. 'bus' can be as simple as Atom feed built on top of event store. In that case publishing to bus is trivial and does not need transactions.

Szymon

2010/7/14 Chris Chilvers <chil...@googlemail.com>

Chris Chilvers

unread,
Jul 15, 2010, 3:46:22 PM7/15/10
to ncqrs-dev
Actually, I like Scott Reynolds suggestion that aggregate roots don't
need to keep a list of any events, only the current state and the unit
of work should track un-committed events (see
http://groups.google.com/group/ncqrs-dev/msg/8581b6ee26180299 ). I
think this is also similar to Mark Nijhof's example where the bus
stores the events and the bus has a Commit method.

With NCQRS we already have an event that fires when an event is
applied, the unit of work could hook this event to maintain a list of
un-committed events.

On Jul 14, 8:06 pm, Szymon Pobiega <szymon.pobi...@gmail.com> wrote:
> I thought about a solution where AggregateRoot maintains a collection of
> pending (not saved) events. This collection would be persisted by its
> repository when saving.
>
> My point was also that why bother having cqrs and event sourcing when we
> don't enforce asynchronous communication between aggregates? All we get in
> this case is audit log (event stream). Thre true power of cqrs and event
> sourcing (performance) comes from asynchronous communication between
> aggregates and if we do it, unit of work is not necessary.
>
> Yes, saving multiple events coming from single command should be
> transactional. As for publishing to bus -- it depends. 'bus' can be as
> simple as Atom feed built on top of event store. In that case publishing to
> bus is trivial and does not need transactions.
>
> Szymon
>
> 2010/7/14 Chris Chilvers <chilve...@googlemail.com>
Reply all
Reply to author
Forward
0 new messages