Cascade-Deleting related Aggregates

Dear Axon community,

we are developing an application based on Axon 4.0 that consists of several aggregates (~15 - 20), some of which are related to each other in 1:n relationships which are expressed as the aggregate identifier of the single-sided aggregate stored in the multi-sided aggregate.
We deliberately have chosen to slice our aggregates in that way due to scalability reasons (to avoid large numbers of events stored to a single aggregate for a hierarchical object graph, see example below)

Auto Generated Inline Image 1.png

In our domain model a project can have 10s or max 100s of images, an image can have several 1.000s -10.000s of annotations and an annotation can have again several labels.

This way of slicing aggregates works quite well for almost all of our use-cases except cascade-deleting related aggregates along with a parent entity. Deleting a project would mean that we soon would need to mark several 100.000 aggregates deleted simultaneously. While technically we do not see a big problem in cascading the delete operation (e.g. managing the relations as lists of child identifiers directly in the aggregate or in a dedicated command-model that can be used to determine child-aggregate identifiers), we are not quite sure how the axon framework in combination with axon server will react when several parent aggregates are deleted simultaneously, since we expect this operation to create considerable load (100.000s or 1.000.000 of events to process in a very short time).

We would appreciate any thoughts and insights on
a) how you would (or already did) approach cascade deleting of related aggregates differently
b) if we need to take any additional measures to avoid bursts of events or if the axon framework (axon 4.x with axon server community edition) simply can handle this out of the box (which is what I hope and also tend to think from how I got to know the framework up to now)

Thanks and Best Regards,
Jakob

Hi Jacob,
Could you educate me in how you are creating relationships between Aggregates using Axon?

From a DDD perspective the data should be organized in such a way where you are primarily deleting the Root Aggregate and the rest follows
A worthless 2 cents as I am not addressing the crux of your question from a performance perspective.

Hello Jakob and Michael,

Are the Aggregate you mention also Aggregate Roots from Axon’s perspective?
Thus, do you have an @Aggregate annotated Project, Image, Annotation and Label class?
Whether that’s the case or not highly influences the approach to “deleting” the Aggregates.

a) how you would (or already did) approach cascade deleting of related aggregates differently

Regardless of this little missing bit of info, I can definitely shine some light on the approaches and potential issues you might run into.

Firstly, know that deleting an Aggregate from Axon’s perspective is marking it as deleted in an Event Sourcing Handler.

Thus, there would first be a conscious operation targeted towards your Aggregate(s), a command, describing the desire to remove/close/kill the Project aggregate.

The Project Aggregate would then decide whether this operation is okay to perform of course, publishing an Event if it is.
This event would in turn be handled in an @EventSourcingHandler annotated method that’ll in turn call the AggregateLifecycle#markDeleted() method.
Doing the latter will ensure that future commands being targeted towards that Project instance will throw an AggregateDeletedException.

After this operation it’ll become difficult what’ll happen, depending on the hierarchy of your Aggregates.
If Image, Annotation and Label are Aggregate Members of the Project Aggregate, then you are done; the entirety is removed as the Aggregate Root has been marked deleted.

Unless you want dedicated events for each member’s removal too, then you’d have to handle the project removed event and publish an Image, Annotation and Label removed event too.

Things change if you have decided to make distinct Aggregate Roots for each type, then you are required to perform some delegation logic to these instances.

This would firstly require a query model containing the entire hierarchy based on the Aggregate Identifiers.
With this model you would be able to introduce a component that will issue all separare delete/kill/remove commands to the related Image, Annotation and Label aggregates.
Then, each of these Aggregate instances would also contain a similar command handler to deduce whether removal is ok to perform at that point in time and publish an event marking it’s occurrence as such.

This component would basically be an Event Handler listening to the ProjectRemovedEvent, knowing it needs to cascade this operation towards all related aggregates.
That makes it either a regular Event Handling Component or a Saga; I would personally go for the former, as the lifecycle of such a Saga is quite vague.

b) if we need to take any additional measures to avoid bursts of events or if the axon framework (axon 4.x with axon server community edition) simply can handle this out of the box (which is what I hope and also tend to think from how I got to know the framework up to now)

Axon Framework would not care about big burst of events essentially.
How much throughput can be reached depends on the Event Bus implementation being utilized.
Stating you are using Axon Server makes the discussion simpler and more straightforward.

We have done operations where we saw Axon Server easily ingest 50k events a second.
Know though, that this throughput depends on quite a lot for things, but it is certainly doable.
I would thus argue that these big batches of cascading deletes, which likely introduce small sized events, shouldn’t be overly complicated.

There is a final point which is of importance to note though, moving back to your Aggregate hierarchy.
Axon Server will by default be capable of handling 32k events in a single transaction.
If you exceed this number of events in a single operation (i.e. a command handler and all events published there after), then you will have to tweak that parameter.

Could you educate me in how you are creating relationships between Aggregates using Axon?

A response to this question depends as I said on the chosen hierarchy between the Aggregates.
If there’s a single Aggregate Root with members under it, this Ref.Guide page should explain it sufficiently.
If every Aggregate is however it’s own Aggregate Root, you will have to consciously associate them with one another.
Thus in short, dedicated commands to make these associations.

Hope all this helps you guys out!

Cheers,
Steven

Hi Steven,

thanks for your thorough answer, that makes it pretty clear to me that we will go for the query model and issue the delete commands to the related aggregates.

To finally answer your (and Michaels) question about Aggregate Roots and how they are linked: We have deliberately chosen to use separate Aggregate Roots for each of our entities Project, Image, Annotation - those are all annotated with @Aggregate. The link between the Aggregates is established via storing the opposite aggregate identifier on the n-side of the relation as a simple String property.

You’re doing some really great stuff there at axon, thank you!

Best Regards,
Jakob