After several hours spent on Google and some A4's later I finally give up and have to reach to you guys for help. This is not a made-up example, it's from a project we're in the process of building. Apologies if there's something horribly wrong with the way we do things - it's our first CQRS+ES project, with all the excitement and hair-pulling that it entails.
The underlying theme is that the system is highly configurable, and it's this flexibility that seems to be the root of our problems. I will focus on just one relationship out of many. There is a
Customer in our domain. A
Customer has
State, and can transition to another state, but only if that transition is allowed.
Both States and transitions between them can be changed. We modeled this as:
Customer (aggregate root)Guid Id
State CurrentState
void ChangeState(State nextState)
void OnCustomerStateChanged(CustomerStateChangedEvent @event)
State (aggregate root)Guid Id
Set<StateTransition> AllowedTransitions
bool IsAllowed(State nextState)
StateTransition (entity)State Input
State Output
We thought it would be good to hold a reference to
State in
Customer rather than
StateId because that way
Customer can veryfy if it can transition to some requested
nextState. This encapsulates some of its logic in the entity and prevents us from dealing with anaemic domain entities.
(So when someone asks the
Customer to transition to another state,
Customer asks its
CurrentState if the requested state
IsAllowed. To answer that question,
CurrentState queries its
AllowedTransitions to see if any has
Output of the same
Id as the requested
nextState. If it is the case, then
Customer publishes
CustomerStateChangedEvent, and applies it via
OnCustomerStateChanged where actual assignment to
CurrentState takes place.)
However, holding on to a reference to
State in
Customer puts additional demand on our domain events. It seems that
OnCustomerStateChanged shoud receive
State, rather than
StateId. What's worse, we might end up storing that
State, with its
AllowedTransitions (and other properties not shown), in the event store! This does not feel right, as AFAIK it's good practice to keep domain events thin, ie. just ints and strings. There was
some discussion on this group on fat events, but the "keep events thin" seems to have prevailed.
Is there any recommended way out? Overall, it seems that domain logic in one aggregate root depends on another aggregate root, but it feels correct from the "avoid anaemic domains" point of view. Perhaps this is a good candidate for a domain service?
Regards,
Dawid