I'm doing a family day care app, and thought I'd try DDD/CQRS/ES for it, but I'm running into issues with designing the aggregates well. The domain can be described pretty simply:
- A child gets enrolled
- A child can arrive
- A child can leave
The goal is to track the times of the visits, generate invoices, put notes (eg. what was had for lunch, injuries etc.) against the visits. These other actions will be, by far, the most common interaction with the system, as a visit starts once a day, but something interesting happens all the time.
The invariant I'm struggling with is:
- A child cannot arrive if they are already here
As far as I can see, I have the following options
1. Single aggregate root Child
Create a single Child
aggregate root, with the events ChildEnrolled
, ChildArrived
and ChildLeft
This seems simple, but since I want each other event to be associated with a visit, it means the visit would be an entity of the Child
aggregate, and every time I want to add a note or anything, I have to source all the visits for that child, ever. Seems inefficient and fairly irrelevant - the child itself, and every other visit, simply isn't relevant to what the child is having for lunch.
2. Aggregate Roots for Child
and Visit
Child
would source just ChildEnrolled
, and Visit
would source ChildArrived
and ChildLeft
. In this case, I don't know how to maintain the invariant, besides having the Visit
take in a service for just this purpose, which I've seen is discouraged.
Is there another way to enforce the invariant with this design?
3. It's a false invariant
I suppose this is possible, and I should protect against multiple people signing in the same child at the same time, or latency meaning the use hits the 'sign in' button a bunch of times. I don't think this is the answer.
4. I'm missing something obvious
This seems most likely - surely this isn't some special snowflake, how is this normally handled? I can barely find examples with multiple ARs, let alone ones with lists.
It sounds like the "here" in your invariant "A child cannot arrive if they are already here" might be an idea for an aggregate. Maybe
Location
orDayCareCenter
. From there, it seems trivial to ensure that theChild
cannot arrive twice, unless they have previously left.Of course, then this aggregate would be pretty long-lived. You may then consider an aggregate for a
BusinessDay
or something similar to limit the raw count of child arrivals and departures.Just an idea. Not necessarily the way to solve this.