12

I'm building an application with a domain model using CQRS and domain events concepts (but no event sourcing, just plain old SQL). There was no problem with events of SomethingChanged kind. Then I got stuck in implementing SomethingCreated events.

When I create some entity which is mapped to a table with identity primary key then I don't know the Id until the entity is persisted. Entity is persistence ignorant so when publishing an event from inside the entity, Id is just not known - it's magically set after calling context.SaveChanges() only. So how/where/when can I put the Id in the event data?

I was thinking of:

  • Including the reference to the entity in the event. That would work inside the domain but not necesarily in a distributed environment with multiple autonomous system communicating by events/messages.
  • Overriding SaveChanges() to somehow update events enqueued for publishing. But events are meant to be immutable, so this seems very dirty.
  • Getting rid of identity fields and using GUIDs generated in the entity constructor. This might be the easiest but could hit performance and make other things harder, like debugging or querying (where id = 'B85E62C3-DC56-40C0-852A-49F759AC68FB', no MIN, MAX etc.). That's what I see in many sample applications.
  • Hybrid approach - leave alone the identity and use it mainly for foreign keys and faster joins but use GUID as the unique identifier by which i pull the entities from the repository in the application.
Pein
  • 1,216
  • 9
  • 16
  • 3
    Don't include a reference to the entity in the event. It's fine to tack the id on after the save. It should be considered immutable upon publishing. However this might end up being troublesome if you're going to allow for retries inside the event producing context. There's value in assigning ids from the outside, so much that I think this should be a default strategy. So both using GUIDs and the hybrid approach could work. Another approach might be some HILO mechanism or something like snowflake. – Yves Reynhout Jul 01 '12 at 09:20
  • Could you elaborate on HILO and snowflake? – Pein Jul 01 '12 at 13:24
  • http://nhforge.org/blogs/nhibernate/archive/2009/03/20/nhibernate-poid-generators-revealed.aspx and http://engineering.twitter.com/2010/06/announcing-snowflake.html – Yves Reynhout Jul 01 '12 at 14:19

1 Answers1

11

Personally I like GUIDs for unique identifiers, especially in multi-user, distributed environments where numeric ids cause problems. As such, I never use database generated identity columns/properties and this problem goes away.

Short of that, since you are following CQRS, you undoubtedly have a CreateSomethingCommand and corresponding CreateSomethingCommandHandler that actually carries out the steps required to create the new instance and persist the new object using the repository (via context.SaveChanges). I will raise the SomethingCreated event here rather than in the domain object itself.

For one, this solves your problem because the command handler can wait for the database operation to complete, pull out the identity value, update the object then pass the identity in the event. But, more importantly, it also addresses the tricky question of exactly when is the object 'created'?

Raising a domain event in the constructor is bad practice as constructors should be lean and simply perform initialization. Plus, in your model, the object isn't really created until it has an ID assigned. This means there are additional initialization steps required after the constructor has executed. If you have more than one step, do you enforce the order of execution (another anti-pattern) or put a check in each to recognize when they are all done (ooh, smelly)? Hopefully you can see how this can quickly spiral out of hand.

So, my recommendation is to raise the event from the command handler. (NOTE: Even if you switch to GUID identifiers, I'd follow this approach because you should never raise events from constructors.)

SonOfPirate
  • 5,642
  • 3
  • 41
  • 97
  • 5
    "So, my recommendation is to raise the event from the command handler" => Events should take part of the domain model. Instead of a poor constructor, to increase the meaning, I'd create a static factory: `create` raising the event in case of success. – Mik378 Jul 08 '14 at 09:49
  • "For one, this solves your problem because the command handler can wait for the database operation to complete, pull out the identity value" - what if timeout arise somewere: the object are created, but command handler throws an exception? For the sake of simplicity, i would like to implement command handler as UoW. In such case you have to use Guid as entity id instead of entity id as identity database field. – Sergey Popov Jan 12 '18 at 10:13