Send back created entity instead of ack in CQRS

1,655 views
Skip to first unread message

Christophe Herreman

unread,
Apr 28, 2015, 3:03:02 AM4/28/15
to ddd...@googlegroups.com
I'm working on a legacy client-server app that basically has most of the BL in the client and uses the server merely as a storage mechanism. We're in the process of refactoring both the client and the server to move the BL to the server. We're trying to implement CQRS, which is going well so far: client sends command messages to the server, server sends back ack/nack, client read model is updated in the background by listening to incoming event messages from the server. I assume this is good implementation.

I've bumped into a case were just sending an ack back to the client makes it hard to implement a view: the interface allows the user to create a new entity and then expects the user to immediately do something with that entity. I could listen to the incoming "entity created" events, but that seems like unneeded complexity if I could just get the entity back instead of the ack.

Are there any suggestions to handle this? Is it OK to break the CQRS rules in this case and send back the entity, or is there another solution?

- Christophe

Wayne Douglas

unread,
Apr 28, 2015, 3:38:30 AM4/28/15
to ddd...@googlegroups.com
This is a good reason why client generated ids are good. Keep the record you just created and do what you need with it. Why do you care that the server sends the exact same chunk of data back? You already have it.

Sent from my iPhone
--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Christophe Herreman

unread,
Apr 28, 2015, 3:45:19 AM4/28/15
to ddd...@googlegroups.com
Generating the ID on the client would certainly help in correlating the event with the new entity. However, the entity is created on the server because certain business rules are applied. The client only sends a command to create an entity of a certain type.

- Christophe

Ben Kloosterman

unread,
Apr 28, 2015, 7:26:16 AM4/28/15
to ddd...@googlegroups.com
The BL is normally wrapped in some sort of facade  . Generate the key or a bare object with key , submit it as a separate command . Then run the BL , then query it and return to the client.  Alternatively use a natural key to query it, 

Returning the object direct will often  place pretty large compromises on the design  and it will often devolve to CRUD, eg you may not have Name  and other read domain information in the BL as there is no logic on it .  Its also affects the infrastructure most return nothing which keeps things simple. 

Ben

João Bragança

unread,
Apr 28, 2015, 11:14:50 AM4/28/15
to ddd...@googlegroups.com
POST REDIRECT GET. If you already have the client generated ID, by the time the user agent receives the 303 and issues a GET your read model should already be updated.

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Greg Young

unread,
Apr 28, 2015, 11:16:55 AM4/28/15
to ddd...@googlegroups.com
If not return a Retry-After
Studying for the Turing test

Christophe Herreman

unread,
Apr 29, 2015, 5:18:40 AM4/29/15
to ddd...@googlegroups.com
I found a StackOverflow post that discusses this. Posting it here as a reference: http://stackoverflow.com/questions/4361889/how-to-get-id-in-create-when-applying-cqrs

- Christophe

Greg Young

unread,
Apr 29, 2015, 5:20:36 AM4/29/15
to ddd...@googlegroups.com
"Cqrs is all about fire and forget"

Sam Holder

unread,
Apr 29, 2015, 5:31:21 AM4/29/15
to ddd...@googlegroups.com
Greg, I can't decipher if that sarcasm, disdain, pity or something else coming though via email :-)

Charles Solar

unread,
Apr 30, 2015, 3:26:17 AM4/30/15
to ddd...@googlegroups.com
I would always always use a client generated ID

But I have seen architecture designs where certain people are updating their read models immediately after processing a command and not waiting for the event store to send the events.  
This is terrible design because you walk into the risk of desyncing your entities so you then have to write a whole system to make sure your event store generated entity matches your "concurrently updated" one.  But its pretty much the only way to do this if you absolutely HAVE to have that generated read model immediately.


Not sure where that diagram image is from but I've seen it a couple times

@yreynhout

unread,
Apr 30, 2015, 4:49:07 AM4/30/15
to ddd...@googlegroups.com
Options:
- What I've done in the past is hand out identifiers in the server facade (to use Bennie's terminology) and echo back the enriched command. So the client would send a command without the entity identifier, the server would echo back the command with entity identifier upon ACK. The disadvantage is that from the client's perspective it's a new command upon each NACK. Instead of the echo-ing you could also send back a 201 CREATED with a location header pointing to the command, where the perception is that the command is itself just another resource. Following that link, you get back the same thing as the echoing would've done. The echo-ing just shaves that extra round trip.
- Assign identifiers from the client (uuid generators in js are a thing these days - but now I'm making assumptions about your stack) and be done with it. It's orthogonal to the entity being created on the server. Unless, the database is handing out identifiers #pesky
- Fetch a "not yet taken" entity identifier from a read model (or a batch of these to optimize), using it on your "creational" commands. Upon nack (oops the number is taken), try another one. If chance of collision is low, this could work - a.k.a. the unique, incrementing invoice number (I believe it was Greg that explained this particular technique on this forum a while back). When combined with a user's (session) identifier and/or device identifier this could be unique enough.
- <what everybody else said/>

Christophe Herreman

unread,
Apr 30, 2015, 8:03:11 AM4/30/15
to ddd...@googlegroups.com
From what I read so far in the replies, generating the id's on the client side seems like the best idea. However, we are constrained in size/memory since the id's end up in an embedded part (home automation modules). Also, introducing a new type of id would require quite some effort to refactor across the system (UI, "backend" and embedded).

To give you an idea of our stack:
- AIR/Flex: UI, highly interactive environment to configure a home automation
- C#: the "backend" where the domain logic lives (or rather, should live as the UI now contains a good part of the logic)
- C: embedded for the electronic modules

The software is distributed as an offline, standalone tool available for Windows and Mac.

We decided for now to add a method that returns the next available id for a given entity type from the backend. The UI will now need to ask the next available id and then send that with the command to create a new entity.

- Christophe

Greg Young

unread,
Apr 30, 2015, 8:09:00 AM4/30/15
to ddd...@googlegroups.com
dpending how you transport there is another common pattern that is
used over http. It is common that a client may not have the ability or
its really to annoying to generate an id (javascript with a uuid as
example). In this case post without an id. Redirect the request to an
idempotent one off uri that includes an id in it.

eg:

>> POST newshitbird -> /data/shitbirds
<< TEMP REDIRECT /data/shitbirds/incoming/43452
>> POST newshitbird -> /data/shitbirds/incoming/43452
//note you can post as many times as you want this uri is unique and idempotent
<< 201 CREATED location:/data/shitbirds/43452

This pattern is used in conjunction with atom in the event store api
as well if you want to check it out in practice.

Cheers,

Greg

Christophe Herreman

unread,
Apr 30, 2015, 8:15:29 AM4/30/15
to ddd...@googlegroups.com
But then the command would return more than just an ACK? 

Greg Young

unread,
Apr 30, 2015, 8:16:54 AM4/30/15
to ddd...@googlegroups.com
there isn't even a command here... this a restful api on top of such
things. e.g. the "client" is the restful api code

On Thu, Apr 30, 2015 at 3:15 PM, Christophe Herreman

Christophe Herreman

unread,
Apr 30, 2015, 8:19:48 AM4/30/15
to ddd...@googlegroups.com
Oh yes of course, I get it. This will not work in our stack but it's good to know this could be an option.

- Christophe

Daniel Lidström

unread,
May 6, 2015, 9:01:08 AM5/6/15
to ddd...@googlegroups.com
Another option is to use the data from the command. You most likely already have a natural key in there. Just query on that key instead of the database id.
Reply all
Reply to author
Forward
0 new messages