Design – Domain Driven Design – designing Aggregate Roots

cqrsdesigndomain-driven-design

Imagine you're designing an application for organizing Workshops.
Workshop itself is quite complicated – it acts as a state machine with multiple possible states and transitions between them.

We're using DDD, CQRS and Event Sourcing.

And now, the use cases:

1) one should be able to assign TodoItems to a Workshop and perform CRUD operations on them

2) one should be able to submit additional Resources for a given Workshop (slides, photos, zip/tar.gz archives etc.) and also perform CRUD operations on them

3) one should be able to add a PossibleTerm (with a Lecturer, Date and Room specified) to a Workshop. Also, there should be a way to modify/delete them.

Later on there is a Voting on those terms, system tries to book room for few terms (let's say, for two terms) that won Voting and Users should be split between those Terms.

Here I have following solutions:

1) Model TodoItems, Resources and PossibleTerms as a part of a Workshop.

Pros:

  • everything is where it conceptually belongs to
  • no eventual consistency (not a big problem probably…)

Cons:

  • Workshop aggregate is cluttered with all those CRUD methods for all stuff.
  • According to Implementing Domain Driven Design by Vaughn Vernon, creating huge aggregate roots is an antipattern

2) Create aggregate roots for holding all those "items" (e.g. TodoItemList, PossibleTermList, Resources or whatever you're gonna call them).

Pros:

  • Workshop aggregate is smaller, it's not related with those Lists at all (the only connection would be that for example PossibleTermList holds a reference to WorkshopId)

Cons:

  • when CreateWorkshopCommand arrives, you're forced to create not only a Workshop, but also additional aggregates as they need to exist after Workshop creation. To avoid this, there could be some kind of CreationalSaga, listening for WorkshopCreatedEvent and producing proper commands to create relatives, but I don't think this is a good idea.

3) Model PossibleTerms, Resource, TodoItem as aggregate roots itself

Pros:

  • no need for creating 'aggregate wrappers' (as in 2))

Cons:

  • again, you're cluttering Workshop with at least factory methods
  • it is hard for me to implement term-choosing feature – for counting votes and later on assigning User to a particular Term you need to query your repository for all Terms matching your Workshop ID and so on – this leads to operations on multiple aggregates within one transaction.

Question: which solution sound best for you? Maybe you have differents ideas?

(Bonus question: I need to perform CRUD operations on individual Resources/TodoItem. I'd rather model then as value objects, but how are they then identified?)

Best Answer

I think you're missing the transactional analysis. Which entities will be modified by each of your use cases ? Will some of them impact more than one entity ?

What are your invariants ? Do some of them span multiple entities (e.g., two Terms can't use the same Room at the same date/time) ? Are they true invariants or could eventual consistency be sufficient ?

Vaughn Vernon has a good step by step approach to aggregate modelling here : https://vaughnvernon.co/?p=926

Related Topic