Hello there. The question is about how to handle Axon Events in a saga.
My case is about deleting nested entities with foreign keys.
Imagine I have a menu with menu sections. Each menu section has its menu item and each menu section might have nested menu section with depth level up to 10, and all of the nested menu section might have own menu items.
So, to delete the whole menu I have to:
- Delete all menu items
- Delete all menu sections
- Delete menu
And besides all of that it has to be done with sending commands to delete items, sections to have the events about it.
So, before I delete menu section I have to wait until ALL menu items have been deleted. I do it with CompletableFuture.allOf()
@StartSaga
@SagaEventHandler(associationProperty = "transactionalId")
public void on(MenuTransactionalDeletedEvent event) {
Iterable<MenuItem> menuItemsList = menuItemRepository.findAll() // here are the items
Iterable<MenuItem> menuSectionList = menuSectionRepository.findAll() // here are the sections
List<CompletableFuture<Object>> deleteMenuItemsFutures = new ArrayList<>();
menuItemList.forEach(menuItem -> {
CompletableFuture<Object> send = commandGateway.send(
new DeleteMenuItemTransactionalCommand(
menuItem.getId(),
event.getTransactionalId(),
));
deleteMenuItemsFutures.add(send);
});
CompletableFuture.allOf(deleteMenuItemsFutures.toArray(new CompletableFuture[0]))
.thenApply(deleteMenuSection -> {
List<CompletableFuture<Object>> deleteMenuSectionsFutures = new ArrayList<>();
sections.forEach(menuSection -> {
CompletableFuture<Object> send = commandGateway.send(
new DeleteMenuSectionCommand(
menuSection.getId(),
event.getTransactionalId()
));
deleteMenuSectionsFutures.add(send);
});
return CompletableFuture.allOf(deleteMenuSectionsFutures.toArray(new CompletableFuture[0]))
.thenApply(deleteMenu -> commandGateway.send(new DeleteMenuCommand(
event.getId(),
event.getTransactionalId(),
)));
});
@SagaEventHandler(associationProperty = "transactionalId")
public void on(MenuItemDeletedTransactionalEvent event) throws InterruptedException {
menuItemRepository.deleteById(event.getId());
}
@SagaEventHandler(associationProperty = "transactionalId")
public void on(MenuSectionDeletedByRangeEvent event) {
menuSectionRepository.deleteRange(event.getLeftMargin(), event.getRightMargin());
}
@EndSaga
@SagaEventHandler(associationProperty = "transactionalId")
public void on(MenuDeletedEvent event) {
menuRepository.deleteById(event.getId());
}
Is it the best way to do it ? Or I might somehow configure Axon Saga to handle all events sequentially ?
Wait before some commands are done and only then go further ?
The main problem was when all events have been dispatched - sometimes I handled events to delete menu section before all of the items have been deleted - and there was an error, because that menu section still had menu items in it