Checking invariants across aggregates

Axon seems to make DDD implementations fairly easy. I’ve followed along and implemented many simple examples. If you can help me work through the following scenario, it will help bring together modeling and implementation a bit more.

Let’s take a simple library example. I have a Holds flow mapped out with a team (event storming). An invariant is that patrons can not hold more than 3 books at a time. Another invariant is the book exists and is not restricted or hidden from users. I’ve seen library management systems DDD modeled several different ways (mostly simple models), often with a Hold, Patron, and Book aggregate. What are some strategies to enforce invariants across potentially multiple aggregates that are part of the same user flow?

My experience with Axon sagas is still very limited. I’d lean towards a HoldSage (vs Hold aggregate) to track the hold status from pending to ready. It would act as an orchestrator, sending a Hold command to a Patron aggregate and listening for a ‘success’ event if # of holds is < 3. Maybe a simple query to a book projection is all that’s needed for the other invariant validation, although this seems iffy. We see similar patterns for checkouts as well. Trying to understand how to use Sages properly without creating a flood of commands and events to check rules in aggregates in our POC.


Hi Jerry,

to be honest with you I’m not quite sure what exactly you are asking for here. So my apologies if I got it wrong and I’m stating the obvious.

Generally speaking invariants should belong the the aggregate. I normally start to think about behaviour first (what operations the system needs to execute), then try to identify the operation related actors (value objects, entities) and the operation related invariants. Only then I’d group them in aggregates. Often times by doing this, one ends up having aggregates that are far from the obvious ones that immediately comes to mind but better suited to ensure boundaries and invariant checks. If that is still not enough then there probably is a need for domain services.

Allow me to share with you some links that I find useful.

I hope this help.

There is on more thing I would like to add here is that you can create an Aggregate from within another Aggregate (in your case the Hold aggregate) using the static method AggregateLifecycle.createNew();

Best Yvonne