Root Aggregate: Change of State or Failure with Exception or ...?

Cumulative roots for state change control - what is currently allowed and what is not. If the state transition is enabled, continue. If not, you are throwing an exception explaining the reason this was not allowed.

But what if the state change does not happen because it is already in the requested state ?

For example, if you have a method Approve

in your aggregated root and by the time it is called the state is already approved?

  • If you chose an exception a la " XYZ is already approved "
  • Or should it be silently ignored ?
  • Or is it necessary to "signal" the state change again (event-sourcing, next paragraph)?

In my case, I am using event sourcing, so an event occurs if a state change has occurred. Having events in my event flow with no real state change does not seem "clean" to me, because I would like to be sure that events were actually created due to state changing changes.

Is there a rule?

In the described case, it doesn't really hurt to approve an approved item. So this is generally the case (thanks to @Eben Roux, @ guillaume31).

But add a little more spice (the actual question behind the question):

Let's assume:

  • bus message
  • asynchronous command / event handling
  • process manager

What if the process manager (aka saga) issues a command (asynchronous) and wants to know if the command succeeded? I think it will reduce the mental burden / sources of errors if the process manager doesn't have to worry about this implementation detail.

I see 3 ways to handle this:

  • command "with error message ABC with success / failure message sent on the
    process-manager bus waits for this message instead of event
  • perform synchronization command execution
    if the process manager does not encounter any exceptions, all right, go to
  • introduce a new eventApprovalDeclined { WasAlreadyApproved = true }

    Also, the process manager is waiting for this event - declination is part of the cumulative history, perhaps an advantage, perhaps never needed ...

I know "it depends"
But can you think of any other (more elegant / lighter / different) solution? What's your favorite "process-compatible manager" solution to this problem?


source to share

3 answers

I don't think there will be a rule of thumb for this.

The message idempotent is a good thing of course, so just ignoring the message / state change is probably the way to go.

I will not signal this again as there will be no effect.



I would say it depends on your domain. If you want to alert the user that they approve of an already approved thing, there must be some feedback from the aggregate. It could be an exception as in Deactivate()

, or a return value passed by the application handler / handler (note that this may not be 100% CQRS).

If the fact that an assertion has already been made is irrelevant to the domain's task, you can simply ignore the action or perform it again independently.

From a UI perspective, you most likely won't be able to refocus anything, so the cases where this happens will be minor: concurrent approvals by 2 users, scripts that assert "brute force", etc.



It seems to me - you are obfuscating application and infrastructure issues.

The application layer depends on the expected behavior of the domain. What will be the business case for being approved twice? Why is this happening at all? Maybe it's just a concurrency UI layer, but then why are multiple users approving the same thing at the same time? Maybe the problem at the business level hasn't been solved yet. Understanding why the former is necessary. The solution is often not in the software itself.

The infrastructure layer deals with things like guaranteed message delivery (for example, at least once or exactly once). When infrastructure fails to deliver on that promise, how does it affect the business (if any)? How does this affect your process? What's your process anyway? Again, it's important to think about these things from a domain perspective. At the end of the day, you are either taking a risk or lowering your operating costs.

In this particular case, ask yourself why and how your process got to the way it was before letting go of the approval command (a bit weird for the approval process, it's usually done by humans). How did it happen that he did not comply with the previous statements (remember that ever the unit was already approved)? You can enter a timeout (as a message to its (process) future self), but in this case it seems strange (I don't know what to say enough).

Don't get this wrong, but I think you need to either delve deeper into the subject area or share most of the specifics.



All Articles