Bidirectional relations between aggregates

Hi,
We have a problem with modeling a scenario where one of the aggregates contains a reference to a different one and we would like to react to some events that are triggered by the second one.

Let’s imagine a simple virtual scenario where we could have:

  1. A list of products - where Product is an aggregate that someone can manage, add some description etc and delete it. It’s defined globally, for the whole company. Dictionary like thing.
  2. A shop that also is an aggregate and contains a list of products it can offer (only IDs of products).

So in this scenario, we can define a couple of products, then create a shop and add some products so a shop can offer it.
The list of products is more or less constant, some products may be added, sometimes updated but
from time to time we would like to delete a product and make sure that its also removed from all shops (because it’s no longer supported by our company).

The first thing that comes to my mind is a saga that reacts to DeleteProductEvent and asks read model for all shops that offered that product and invoke RemoveProductCommand for all of them in a loop.
However, the problem is that when my saga asks read model about all shops connected to this product, the product itself is already gone because DeleteProductEvent was already consumed by read model handlers.

I found one topic on Axon mailing group where someone shared the link below:
https://medium.com/@Hronom/axon-gods-496525cfd2e
But I’m wondering if long-living saga that is 1:1 connected with a lifecycle of an Aggregate is something appropriate in axon framework and won’t cause e.g. performance issues.

Or maybe Is there any other approach that allows handling such bi-directional connections?

I don’t see why the lifetime of a saga should have an effect on performance of axon. Besides that i dont get your first comment

However, the problem is that when my saga asks read model about all shops connected to this product, the product itself is already gone because DeleteProductEvent was already consumed by read model handlers.

From a Business perspective i don’t think you can wipeout a product from your catalog before you removed them from your stores. So i would command the stores to remove the products an when they tell me that they removed them i will delete it from my catalog. In my opinion a good fit for a saga. I think its similar to the transaction case of Axon-trader: https://github.com/AxonFramework/Axon-trader/blob/master/orders/src/main/java/org/axonframework/samples/trader/orders/command/SellTradeManagerSaga.java

Thanks Nils,
I agree. I should start with clean up before removing the product, but the problem is that I need a command handled by a Product to actually start the saga.
Does it mean that I need to handle two separate commands in my Product aggregate?
One like “InitiateProductRemovalCommand” (this one just starts the saga after emitting an event)
and the second one “RemoveProcuctCommand” (real deletion - invoked by saga at the end)?

That would be my understanding of the business process at least. As i think that would be exactly what would happen if that would be done in the real world.

Another remark but as you say its a virtual scenario it might not be relevant:

I dont think the product is an aggregate in this case but the catalog and a product should be a value object but this totaly depends on your context.