Who generates the Aggregate Ids?

1,310 views
Skip to first unread message

Rod Johnson

unread,
Jul 22, 2011, 12:37:16 PM7/22/11
to DDD/CQRS
Is it a best practice to let the Domain object determine it's
aggregate ID in the constructor or to let the client pass in a new
guid? One of the benefits I see to letting the client do it, is that
it knows exactly what object "Should" be in the database after the
command has been sent. Thoughts?

Udi Dahan

unread,
Jul 23, 2011, 5:58:41 AM7/23/11
to DDD/CQRS
There is a big benefit in client-generated IDs in that it allows
clients to send follow-on commands without waiting for a response from
the server:

CreateParent { ParentID = parentGuid, /* other info */ }
CreateChild { ChildID = childGuid, ParentID = parentGuid, /* other
info */ }

Cheers,

Udi

√iktor Ҡlang

unread,
Jul 23, 2011, 9:27:58 AM7/23/11
to ddd...@googlegroups.com

It also scales better

Peter Ritchie

unread,
Jul 23, 2011, 3:48:58 PM7/23/11
to ddd...@googlegroups.com
Drawback of putting it right in the domain class is that it's now taken on the responsibility of id generation.  Guids are one thing; but anything else and you've got potential issues with multi-threaded scenarios.

I'm also not a big fan of just letting the database do it... what if you don't use a database?

Cheers -- Peter

Rinat Abdullin

unread,
Jul 23, 2011, 3:56:36 PM7/23/11
to ddd...@googlegroups.com
Peter, 

Hi-Lo generators or just any atomic storage would do that as well.

BTW, scalability is not an issue even if you are generating thousands of identifiers server-side per second (you are going to start hitting different perf problems here)

Best, 
Rinat

Kallex

unread,
Jul 24, 2011, 3:23:52 AM7/24/11
to ddd...@googlegroups.com
Hi,
 
On some scenarios, client using real IDs can pose a form of a security exposure. Although it might be arguable slim, getting a hold of existing IDs in any form, can open an option for generating commands for "later use" to be ran on properly authorized user's behalf.
 
This can be mitigated by issuing a layer in between, that has 1:1 mapping on IDs that are valid for certain amount of time. The same mapping can be used to have client creating new records with GUIDs even when server side is using some other data type.
 
The layer doesn't have to be applied everywhere (in case of heavy performance requirements), but it allows logical way of controlling the technical information exposure out from the system.
 
Kalle

PhilJ

unread,
Jul 24, 2011, 6:07:09 AM7/24/11
to DDD/CQRS
> Rod:
> Is it a best practice to let the Domain object determine it's
> aggregate ID in the constructor or to let the client pass in a new
> guid? Thoughts?

Client side if you need to reference to whatever you created (e.g. set
a focus in a list, load a corresponding view etc...), which is
practically always the case for me. Or for followup commands as Udi
wrote.


> Kallex:
> On some scenarios, client using real IDs can pose a form of a security
> exposure. Although it might be arguable slim, getting a hold of existing IDs
> in any form, can open an option for generating commands for "later use" to
> be ran on properly authorized user's behalf.

Shouldn't permitting an operation be based on proper authorization
instead of knowledge (or obsucring of such) of an Id? I believe the
authorization tokens should time out, not the Ids...

Kalle Launiala

unread,
Jul 24, 2011, 7:00:05 AM7/24/11
to ddd...@googlegroups.com

On Sunday, July 24, 2011 1:07:09 PM UTC+3, PhilJ wrote:

> Kallex:
> On some scenarios, client using real IDs can pose a form of a security
> exposure. Although it might be arguable slim, getting a hold of existing IDs
> in any form, can open an option for generating commands for "later use" to
> be ran on properly authorized user's behalf.

Shouldn't permitting an operation be based on proper authorization
instead of knowledge (or obsucring of such) of an Id? I believe the
authorization tokens should time out, not the Ids...
 
Well, this is exactly what I wrote in the same paragraph that you quoted ;-). Security through user authenticated authorization, not because of ID obscurity.
 
Unauthorized user may have access to obtain real aggregate IDs through normal queries on objects where he has the read access, but is not authoried to execute commands against. Based on that information malicious commands can be prepared well in advance.
 
Now injecting the well-prepared commands on properly authorized user's context is much simpler task than trying to obtain the right expiring IDs.
 
This is one scenario where having 1:1 ID mapping layer keeps the logic on server-side only. Other scenarios where it removes dependencies are requirements for merging or disconnected clients support for commands and events. Disconnected clients can use GUIDs for their identification while the server is operating with longs for performance for example.
 
Kalle

√iktor Ҡlang

unread,
Jul 24, 2011, 7:20:49 AM7/24/11
to ddd...@googlegroups.com

Ah, you're assuming that any authorization is global for all aggregate ids?
If you use eventsourcing (or store the identity of the creator otherwise) you can have authorization per aggregate id
 
 
Kalle



--
Viktor Klang

Akka Tech Lead
Typesafe - Enterprise-Grade Scala from the Experts

Twitter: @viktorklang

Rod Johnson

unread,
Jul 24, 2011, 3:03:25 PM7/24/11
to ddd...@googlegroups.com
I thing guid is perfect to pass to command no need for db at all.

Sent from my iPad

Rod Johnson

unread,
Jul 24, 2011, 3:06:09 PM7/24/11
to ddd...@googlegroups.com
In any real environment, the user would pass in some token to the
command that the handlers would use to authenticate the command. This
would close that hole

Sent from my iPad

Bryan Hunter

unread,
Jul 25, 2011, 2:29:13 PM7/25/11
to ddd...@googlegroups.com
Yep, and client generation can also help with idempotence. If the command CreateCustomer{Id={guidhere},Name="Sue"} is sent (or delivered) multiple times, it is pretty simple and safe to interpret the intent as "Not sure if I told you already, but please create a single customer named Sue". Without a client-generated id, the intent would be ambiguous.

-Bryan
--

Firefly Logic, Inc. // Partner & Senior Software Engineer // www.fireflylogic.com

@yreynhout

unread,
Jul 25, 2011, 3:47:46 PM7/25/11
to DDD/CQRS
I'd be very careful with id-based idempotency. Lot's of Sue's out
there (ask Johnny Cash). Both natural and technical idempotency have
their place. Choose wisely, or pick both.

On 25 jul, 20:29, Bryan Hunter <bryan.hun...@fireflylogic.com> wrote:
> Yep, and client generation can also help with idempotence. If the command
> CreateCustomer{Id={guidhere},Name="Sue"} is sent (or delivered) multiple
> times, it is pretty simple and safe to interpret the intent as "*Not sure if
> I told you already, but please create a single customer named Sue*". Without
> a client-generated id, the intent would be ambiguous.
>
> -Bryan
>
> On Sat, Jul 23, 2011 at 4:58 AM, Udi Dahan <thesoftwaresimpl...@gmail.com>wrote:
>
>
>
>
>
> > There is a big benefit in client-generated IDs in that it allows
> > clients to send follow-on commands without waiting for a response from
> > the server:
>
> > CreateParent { ParentID = parentGuid, /* other info */ }
> > CreateChild { ChildID = childGuid, ParentID = parentGuid, /* other
> > info */ }
>
> > Cheers,
>
> > Udi
>
> > On Jul 22, 7:37 pm, Rod Johnson <rod.john...@wcpro.com> wrote:
> > > Is it a best practice to let the Domain object determine it's
> > > aggregate ID in the constructor or to let the client pass in a new
> > > guid?  One of the benefits I see to letting the client do it, is that
> > > it knows exactly what object "Should" be in the database after the
> > > command has been sent.  Thoughts?
>
> --
>
> <http://www.fireflylogic.com>Firefly Logic, Inc. // Partner & Senior
> Software Engineer //www.fireflylogic.comhttp://codeswamp.com// Twitter:
> @bryan_hunter<http://twitter.com/#!/bryan_hunter>

Bryan Hunter

unread,
Jul 27, 2011, 12:57:07 PM7/27/11
to ddd...@googlegroups.com
Haha! Johnny Cash is indeed why I picked "Sue". Well spotted. I'm not sure you noticed the "Id={guidhere}" part of my example command though? 

Here is a scenario of using a client-generated id in commands and their results: 
CreateCustomer{Id="{guid-guid-1234-guid}', Name = "Sue"} -> Create customer named "Sue" with id ''...1234...'
CreateCustomer{Id="{guid-guid-1234-guid}', Name = "Sue"} -> Hmm... already created. Must be delivery hiccup. Discard.
CreateCustomer{Id="{guid-guid-5555-guid}', Name = "Sue"} -> Create a second customer named "Sue" with id ''...5555...'
CreateCustomer{Id="{guid-guid-1234-guid}', Name = "Sue"} -> Hmm... already created. Must be delivery hiccup. Discard.
CreateCustomer{Id="{guid-guid-1234-guid}', Name = "Pat"} -> This is just weird. Log anomaly and discard.

Result: we create the two distinct customers named Sue that our client wanted and no extras.

It's a good point you raise about natural idempotency. It's the free-standing things like "CreateXyz" where a client-generated id can really help-- they clarify client intent for commands that aren't naturally idempotent. With a naturally idempotent command like "BeatHorseToDeath" there's no need to include a client-generated id or a mechanism to screen for duplicates. Once the horse is dead, no amount of beating is going to kill it again. There's no point (nor harm) in processing a BeatHorseToDeath command twice.

Cheers,
Bryan
Reply all
Reply to author
Forward
0 new messages