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