11

I want to implement my first application using CQRS pattern along with Event Sourcing. I am wondering how creation of aggregate roots should be handled properly. Let's say someone sends CreateItem command. How it should be handled? Where the event ItemCreated should be stored? As first event of a new Item? Or should I have some kind of ItemList entity that aggregates all items and its event list consists only of ItemCreated events?

Udi Dahan suggests not to create aggregate roots and always use instead some kind of fetch method. But how I can fetch something that is new and certainly does not have any ID assigned. I understand the idea behind and it is pretty reasonable to think that a new object is an object that has its state composed of zero events replied on it. But how should I use it? Should I have a distinct method in my Repository like getNewItem() or make my get(id) method accepting Optional<ItemId> instead?

Edit: After some time of digging I found really interesting implementation of the aforementioned patterns using actors. The author instead of creating the aggregate, retrieves it from some kind of repository with newly created UUID. The drawback of this approach is he allows for a temporary inconsistency state. I am also wondering how I can implement delete method with such approach. Simply add Deleted event to the event list of the aggregate?

  • 1
    I suspect Udi's post-title is misleading. IMHO it sounds like his real objective is that freshly-made ARs should always be reachable from some other place, in a way that captures context about why/how/who decided that the new AR needed to be created. Everything else is about how a particular implementation (NHibernate?) could make it easier to manage. – Darien May 6 '14 at 2:34
  • 2
    Note that the Udi Dahan article you're referencing specifically calls out that his advice may not apply to event sourcing: udidahan.com/2009/06/29/dont-create-aggregate-roots/… – E.Z. Hart Dec 5 '14 at 1:11
13

The idea in Udi's post, as I gather, is that no kind of item appears out of thin air. There is (almost) always something, or more specifically, some domain operation, which caused the item to be created. Just like Udi's example of a user actually being born out of a visitor registering to the site. At that point and at that bounded context Visitor is the aggregate root, which is retrieved by his IP address. This Visitor then creates the new "item", a user at this point, through a domain operation called Register. Same goes for the step before, which is another bounded context: Referrer is the AR, which is retrieved by the URL and that has a domain operation called BroughtVisitorWithIp, where the visitor is born.

Udi writes very nicely on deletion as well: http://www.udidahan.com/2009/09/01/dont-delete-just-dont/. Main idea is, that you don't delete anything, ever. There's always a domain operation behind, which we want to capture. Like an order being cancelled, rather than deleted. Read it, it's a very good post.

The main point here on both accounts, doing DDD and especially Event Sourcing, is that you should never do straight CRUD-operations. If you find yourself in a situation where you really need to just insert, update or delete some data, and there truly is no domain operation behind it, then maybe DDD and Event Sourcing is not a good fit for that bounded context. You are free to combine these two as you wish as long as a single bounded context adheres to one principle. This way the CRUD-style bounded context might create some row in the database, that becomes an entity and an Aggregate root in another bounded context, where you now can retrieve the AR and not have to create it.

  • 2
    "maybe DDD and Event Sourcing is not a good fit for that bounded context." You get the point of DDD right. It's should not be implemented in every case just for satan's glory but only when one need to deal with a complex domain full of uncertain rules. Personally I've did it for legal software where requirements is not driven by logic. – Yegor Chumakov Jul 2 '15 at 20:38
  • 2
    +1 for this sentence alone "for that bounded context." :) – Songo Jul 3 '15 at 2:43
  • 2
    +1 the use of the verbs 'Add' and 'Create' is strongly suggestive that you are still thinking about your domain in terms of interaction with a good old tabular database. Without knowing your domain/bounded context I can't say whether this is appropriate or not. Ignore the persistence, focus on the COMMANDS and EVENTS first (aka the INTENTIONS and the OUTCOMES) which are unique to your domain, then worry about how to persist the state, which is a problem that has been solved hundreds of thousands of times before. – Matt Jul 3 '15 at 12:06
  • "the use of the verbs 'Add' and 'Create' is strongly suggestive that you are still thinking about your domain in terms of interaction with a good old tabular database" Hmmm. When you have a UI design that has a big 'Add something' button in it, then sadly, that is the intention; literally to add something new. I generally agree with you, but we're not talking database level here, sometimes Add or Create are actually the right words to use. – designermonkey Dec 6 '17 at 16:46
  • 1
    @designermonkey When you have those buttons in the UI, do you really have a domain operation behind them? Perhaps, but 9 out of 10 times there's really no need for a complex domain operation at that bounded context. And pure CRUD operation is just that, a pure CRUD operation, and should be handled as such. Only when there is a need for the complexity of the domain model, should it be used. Thus the different bounded contexts with different design principles. – Tuukka Haapaniemi Dec 7 '17 at 19:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.