What is usually done when an aggregate is asked to apply an "impossible" event to its state?

For example, if a ShoppingCart aggregate has already applied a CustomerAckRec (The customer has acknowledged receiving the shipped item) event, and is now asked to apply an AddedItem event?

This question confuses me, because on one hand, it makes no sense to apply that event. Adding the item would create an impossible state, but so would just swallowing the event and leaving the state as is.

Seems like an error should be thrown. But this means that a simple application of an event is now a failable action, so we need to handle that failure.

But on the other hand, events are supposed to be the source of truth, so we shouldn't validate them against the current state of the aggregate?

I guess this is philosophically entirely different from the business rule validation of whether a command is successful or not, more of a validation of the data coming from another machine. But in practice, this essentially always duplicates a part of the validation of the command that would have caused the creation of the event! Which is precisely why we assume that this event would never exist in the first place.

So, should I really pretend there isn't any chance that this event will come? Should I just pretend/cast that I know that if this event arrived, the state is such that this event can be meaningfully applied to?

Neither blindly jamming events into the aggregate without any thought about the possible hard to track undefined behavior that would result from corrupted event storage, nor the duplication in validation sits well with me.

  • you have to be very assertive as to whether your events are asserting requests that can later fail vs. accomplishments that did for sure succeed. – Erik Eidt Aug 30 '18 at 0:01
  • @ErikEidt they are truly events in the event store, not commands. But I am talking about the technical implementation of the aggregate replay, not the domain design philosophy. – Mihail Malostanidis Aug 30 '18 at 13:12
  • What do your users want to happen in thet case? – Stop harming Monica Aug 30 '18 at 14:12
  • @Goyo the user should not have any way of getting into this situation. What they actually want is unknown, since we could be replaying the aggregate in order to handle any out of myriads of potential commands. But in all cases, they probably want their command considered fairly instead of getting a temporary error all the way until human intervenes and fixes the state for them. – Mihail Malostanidis Aug 30 '18 at 19:09
  • In a well-architected system, an event of the kind you described can never happen. The UI will have already disabled the button that triggers the event, so the event will never occur under the conditions you stated. – Robert Harvey Aug 30 '18 at 19:15

What is usually done when an aggregate is asked to apply an "impossible" event to its state?

Short answer? exception report.

Longer answer: assuming that the domain model is the authority for the aggregate, there really is no such think as an "impossible state". There are unreachable states -- states that have no inbound transitions -- but there aren't really impossible states.

In most mature domains, there are already protocols in place for correcting data entry mistakes; in accounting, for instance, you'll normally add new events into the stream that first reverse the effects of the error and then correct the mistake.

In less mature domains, you may be inventing those protocols as you go.

Greg Young offered some interesting advice: stop over engineering. Automate happy paths that are well understood, and kick the harder problems back to the human beings.

Ergo: exception reports: when the going gets weird, let the human beings sort it.

So, throw, fail all commands that rely on this aggregate, and page people like crazy?

Probably. In many cases, the important thing to realize is that states that are off the happy path may still be reachable states in the domain. It may be that full or limited capabilities are still available while the entity is on the recovery protocol path.

For example, consider a seat map for an airplane. The same seat should not be assigned to two different passengers. But the gate agents have protocols they can use to resolve the situation, and there's no particular reason to prevent assigning other seats on the flight while the conflict exists.

| improve this answer | |
  • So, throw, fail all commands that rely on this aggregate, and page people like crazy? Don't even think of bothering trying to see if this event would be inconsequential to permitting the command, since that would probably be complex? – Mihail Malostanidis Aug 30 '18 at 13:17
  • In other words, the more unlikely it is, the less impact this total lockdown would have, so who cares? – Mihail Malostanidis Aug 30 '18 at 13:18
  • 1
    Not "so who cares?", but "so ask the domain experts". – VoiceOfUnreason Aug 30 '18 at 13:57
  • This domain has no experts! dabs profusely – Mihail Malostanidis Aug 30 '18 at 18:33
  • 2
    Then do what you think best, and when someone complains, make them give you clear requirements? – VoiceOfUnreason Aug 30 '18 at 18:35

The problem is that you're calling it an event. Events are reports of what has happened. They can't fail because they're over and done with. Since this hasn't happened yet, and it can still fail, it's a command.

If something is nice enough to tell you about an event you can deal with it however you like but nothing else cares what you do. If something issues you a command you'd better do it or explain why not.

Validation is needed in either case if there is a chance of getting stuff you don't know how to handle. The difference is: when an events validation fails, nothing else cares. It's your problem.

| improve this answer | |
  • I can't call it anything other than an event. I don't expect it to ever appear, I just don't feel comfortable relying on having such detailed assumptions/tight coupling to another system so much as to act like I myself already validated it. In a somewhat unrelated question, how do I recover from this hypothetical situation? I'd assume I would need to append an "EventRecalled(priorEventID)" event? – Mihail Malostanidis Aug 30 '18 at 13:10
  • Sorry, you lost me at, "I can't call it anything other than an event." – candied_orange Aug 31 '18 at 0:44
  • It's an event tho 😂👌 – Mihail Malostanidis Sep 1 '18 at 22:31
  • @MihailMalostanidis by what definition? – candied_orange Sep 1 '18 at 23:15
  • Including the definition you linked fully, but most importantly in the context of event-sourcing (as tagged) where events aren't ephemeral messages but usually persisted forever. – Mihail Malostanidis Sep 2 '18 at 10:59

So, should I really pretend there isn't any chance that this event will come? Should I just pretend/cast that I know that if this event arrived, the state is such that this event can be meaningfully applied to?

On thinking a little more about this with some additional caffeine, a lot of this depends on what happens in the role of this aggregate. If the point of the aggregate is to simply record the events as they occurred and play them back as a transaction record, you should probably accept anything that is received. Whether you should also send out events immediately or if some other part of the system is responsible for validating is up for debate.

However, if these 'impossible' events will cause this subsystem to enter into an invalid state, this is the worst thing you can do. I've run into this thinking many times and it's always a mistake. Usually I come upon this type of error when troubleshooting nasty problems such as data corruption. If you think something will never happen, you should always add some sort of panic button that will fire if it does. There are two basic reasons for this:

  1. When you have a (reliable) alarm for something you don't believe can occur, the lack of those alarms is evidence that your assertion is correct. If you just ignore any such occurrence, you have no evidence of your assumption either way.

  2. As a wise man once said: "shit happens." Just because there's no reason that the system should allow anyone to add an item after payment, that doesn't mean it can't or won't happen. That's some other part of the system that you can't control in this part. The likelihood that the system that generates these events is defect free and will always remain so is exactly 0%.

With regard to 2, there's a really good chance that some malevolent actor would attempt to cause such a series of events. For example, there's a pretty clear financial incentive as to why someone might purchase a thumb drive and then attempt to add a laptop to the order after payment. If someone were successful at that, think about what your explanation will be for ignoring it. Do you think "I thought it could/would never happen" will sound good to you?

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.