You could use an index. This is generally not something that you will find in an eventstore, so you will most likely need to use an external indexing engine for this.
This index would then have to be updated everytime that someone changes a carts contents, and this index update would most likely be async. This would mean that you cannot rely on your index to be 100% correct. So what you could do then in your case is:
- Product gets cancelled
- Do an index lookup for carts containing the product
- Issue product removals for these carts
Now, you may still have carts containing the cancelled product, since you may have cart changes that haven't been indexed yet. This is ok, it's inevitable, don't fight it - embrace it.
With the index lookup you will most likely have handled the majority of the problem. But you may still have some stragglers left.
What you will need to do now is handle the case when a customer checks out a cart which contains a cancelled product. At this point, you can silently remove the cancelled products or show a message that this product is unavailable etc. This should take care of even more of the problem.
Of course, as is the problem with most eventually consistent systems, you may still end up with a few customers actually succeeding to buy a cancelled product. In this case, you will most likely notice that in a later step (like when you're packing the order). In that case, issue a correction event which in turn should refund the customer and send an email telling the customer that you are very sorry that the product is no longer in stock.
This may feel shaky and annoying, but sit back, have a cup of coffee and think about it for a moment. You're going to have to handle this case anyway even if you would have had a fully consistent database.
In real life when a product gets cancelled you probably get a notice from the vendor. This notice may come such that the product was actually cancelled 2 days ago when you got the notice in the first place. So when you enter the data into the system that the product no longer exists it might have actually been unable to buy for several days before that. But there is no way that your system could handle that! So you're going to have to go through the checked out orders and issue refunds, just as the process above states.
Or maybe the product is cancelled, but you have 1 left in your warehouse. Awesome! So the customer can buy it anyway! Except while you are packing the thing, your packers drop it on the floor and it's destroyed, or when your packers go get it they see that it's water damaged...or anything else really. So what do you do? "I'm sorry Mr Customer, here is your refund." While this is a pretty rare occurrence, it will happen sooner or later.
As you may have noticed when shopping online, this will happen to you at one point or another. You'll buy something on amazon, then one day later you'll get a "sorry we can't actually get you this thing - here's your money back"-email. And when you get that mail you of course become disappointed, but you generally understand that it can happen and it's not really anyones fault.
It will happen because all information on the computer will always be old and out of date. :) You cannot design that away even if you're not doing eventsourcing, any design that promises to do so will eventually fail because of real-life concerns (like the clumsy packers example above).
Instead embrace it, plan for it, include it in your use cases/flows, write tests for it and you'll have a system that fails gracefully.
(Also, if you are smart, when you send the "sorry you can't buy this, here's your refund"-email, include a 10% voucher or something like that for your next shopping cart in the email! That way the customer might feel inclined to return to your store and buy something else instead.)