Loading multiple aggregates in same command handler

1,267 views
Skip to first unread message

andho

unread,
Jan 18, 2012, 4:09:46 AM1/18/12
to ddd...@googlegroups.com
I have gone through the Fohjin sample and found out that in one of the Handlers, an AggregateRoot is loaded, and using this AR another is created and then saved to the repository. Find below the excerpt:

        public void Execute(OpenNewAccountForClientCommand compensatingCommand)
        {
            var client = _repository.GetById<Client>(compensatingCommand.Id);
            var activeAccount = client.CreateNewAccount(compensatingCommand.AccountName);

            _repository.Add(activeAccount);
        }

The two aggregates are sent to the same repository, and from what I can gather, the repository saves the Aggregates in the same EventStore.

But I have frequently come across posts where it is recommended to save Events for each aggregate type in a different Event Stream, and I also prefer this way, as it avoids UUID collisions.

Or am I mistaken?

mynkow

unread,
Jan 18, 2012, 4:33:38 AM1/18/12
to ddd...@googlegroups.com
Update only 1 AR in a transaction. The code you pasted updates only 1 AR. You can load as many ARs as you wish but in the end update only 1. Of course you can break that rule sometimes.

mynkow

unread,
Jan 18, 2012, 4:38:04 AM1/18/12
to ddd...@googlegroups.com
PS: It was not clear... Save events when you commit the transaction. Saving events means that you are doing Event Sourcing. Is that what you are talking about?

Philip Jander

unread,
Jan 18, 2012, 5:37:47 AM1/18/12
to ddd...@googlegroups.com
Am 18.01.2012 10:09, schrieb andho:
>
> The two aggregates are sent to the same repository, and from what I
> can gather, the repository saves the Aggregates in the same EventStore.
>
> But I have frequently come across posts where it is recommended to
> save Events for each aggregate type in a different Event Stream, and I
> also prefer this way, as it avoids UUID collisions.
>

Generally, I would suggest a single store. What would be your reason for
separating the events?
The only reason I can think of to separate stores is to be able to
multi-thread or multi-process the domain and avoid having to coordinate
calls to the event storage.
Lateron, it is kind of trivial to split one store into many based on the
event source ids, so you can always separate if the need should arise.
If you have multiple BCs/ABCs which employ event sourcing, I would
consider to use separate stores for them, to avoid any accidental or
convenient coupling.

Cheers
Phil

@yreynhout

unread,
Jan 18, 2012, 4:33:01 PM1/18/12
to DDD/CQRS
Client has a factory method to create new accounts. I doubt
CreateNewAccount mutates the Client's state. As such, where's the
harm, where are two aggregates "sent to the same repository"/
persisted? I only see one being persisted.

Disclaimer: I didn't look this up in the Fohjin sample.

andho

unread,
Jan 18, 2012, 11:38:35 PM1/18/12
to ddd...@googlegroups.com
Guess I was not clear. What I mean is both Client and the Account AR is stored in the same Event Store. I guess this is common place/defacto for events in the same BC?

andho

unread,
Jan 18, 2012, 11:41:25 PM1/18/12
to ddd...@googlegroups.com
I haven't thought out as far as multithreading. I was just thinking of having lower UUID collisions. I guess that's not that big of a concern.

Sebastian Burgstaller

unread,
Jan 19, 2012, 3:11:09 AM1/19/12
to DDD/CQRS
3,4028236692093846346337460743177e+38 - this is the number of possible
combinations in the .net implementation of UUID :-)

Philip Jander

unread,
Jan 19, 2012, 4:37:54 AM1/19/12
to ddd...@googlegroups.com
Am 19.01.2012 05:41, schrieb andho:
> I haven't thought out as far as multithreading. I was just thinking of
> having lower UUID collisions. I guess that's not that big of a concern.
I'd say the probability of a huge asteroid colliding with the planet is
*way* higher, so I wouldn't worry too much about the guids.
(However, this only holds for faultless guid implementations. Bad guids
may collide before that asteroid hits ;))

Greg Young

unread,
Jan 19, 2012, 8:08:28 AM1/19/12
to ddd...@googlegroups.com
This is only saving a single aggregate back to the repository... (activeAccount)

Also from wikipedia (who is luckily up today)...

Random UUID probability of duplicates

Randomly generated UUIDs like those generated by the java.util.UUID
class have 122 random bits. There are 128 bits altogether with 4 bits
being used for the version ('Randomly generated UUID'), and 2 bits for
the variant ('Leach-Salz'). With random UUIDs, the chance of two
having the same value can be calculated using probability theory
(Birthday paradox). Using the approximation

these are the probabilities of an accidental clash after calculating n
UUIDs, with x=2122:
n probability
68,719,476,736 = 236 0.0000000000000004 (4 × 10-16)
2,199,023,255,552 = 241 0.0000000000004 (4 × 10-13)
70,368,744,177,664 = 246 0.0000000004 (4 × 10-10)
To put these numbers into perspective, one's annual risk of being hit
by a meteorite is estimated to be one chance in 17 billion,[31] that
means the probability is about 0.00000000006 (6 × 10-11), equivalent
to the odds of creating a few tens of trillions of UUIDs in a year and
having one duplicate. In other words, only after generating 1 billion
UUIDs every second for the next 100 years, the probability of creating
just one duplicate would be about 50%. The probability of one
duplicate would be about 50% if every person on earth owns 600 million
UUIDs.
However, these probabilities only hold when the UUIDs are generated
using sufficient entropy. Otherwise the probability of duplicates may
be significantly higher, since the statistical dispersion may be
lower.

--
Le doute n'est pas une condition agréable, mais la certitude est absurde.

andho

unread,
Jan 20, 2012, 6:11:25 AM1/20/12
to ddd...@googlegroups.com
Yes in this command only one AR is saved into the repo. But the same repo is used in all the Command Handlers right?

I have combed through the code, but haven't found anything indicating different. But I am not very familiar with .NET code.

Sop Killen

unread,
Nov 12, 2012, 10:26:24 AM11/12/12
to ddd...@googlegroups.com
Very sorry for necromancing this thread, but to me it seems like both client and account generate events:

public ActiveAccount CreateNewAccount(string accountName)
{
IsClientCreated();
var activeAccount = ActiveAccount.CreateNew(Id, accountName); // Calls constructor below
Apply(new AccountToClientAssignedEvent(activeAccount.Id)); // used for keeping a list of accounts in client
return activeAccount;
}

private ActiveAccount(Guid clientId, string accountName) : this()        
{            
var accountNumber = SystemDateTime.Now().Ticks.ToString();            
Apply(new AccountOpenedEvent(Guid.NewGuid(), clientId, accountName, accountNumber));        
}

Is this a problem, to update two ARs in one command?
I'm thinking it is easier to follow than the saga solution and the fail scenario is simpler when having everything in one transaction.
What are the cons?

Regards Sop Killen

@yreynhout

unread,
Nov 13, 2012, 3:55:09 AM11/13/12
to ddd...@googlegroups.com
A lot depends on what kind of scaling you want to achieve, the assumptions you can make about data being co-located, the "transaction" support of your underlying store, the need for things like "optimistic concurrency".
I've violated the "1 command == 1 AR" rule a couple of times, especially in cases where it's just easier to affect multiple collaborators. Given I'm using a relational store underneath, have a modest number of messages to process and a modest number of aggregates to store, I can get away with it. That doesn't mean I deliberately design for affecting multiple ARs, on the contrary. If I wanted to tease it apart, and turn it into some workflow, I could perfectly do so. It would be more explicit as well. Why design something for internet scale when it's never going to be used that way? And if it ever comes to that, I have a way to make it happen.

Sop Killen

unread,
Nov 23, 2012, 3:34:40 AM11/23/12
to ddd...@googlegroups.com
Thanks for the detailed answer!
Reply all
Reply to author
Forward
0 new messages