I’m trying to figure out how to prevent (or deal with) concurrent modification issues.
Lert’s say I have a the following Aggregate:
// Kotlin
@Aggregate
class WidgetAggregate {
@AggregateIdentifier
var id: UUID
@AggregateMember
var name: String
@AggregateMember
var displayName: String
}
I want to deal with the following use case.
- The aggregate is at version 5 (i.e.
AggregateLifecycle.getVersion())returns 5).name= “Name Five”displayName= “Display Name Five”
- Simultaneously (or at least nearly simultaneously) User A changes the
nameto “Name Six A” and User B changes thedisplayNameto “Display Name Six B” - Both result in an
UpdateWidgetCommand
data class UpdateWidgetCommand(val id: UUID, val name: String, val displayName: String)
Let’s assume the Command for User A hits first. When the Command for User B hits, we (may) want to reject the Command since it is operating on outdated data compared to when the user input the change. For the rejection, in some use cases we only want to reject if the two commands are changing the same property. And in other use case we want to reject if the update is modifying any property if the Aggregate is not identical to the one the user “saw” at the time of the change.
The documentation seems to indicate that a ConflictResolver can be used in the @CommandHanlder to deal with this situation. But that documentation is, I am sorry to say, rather lacking. There is a Note that states:
Expected Aggregate Version
The
ConflictResolverwill only contain any potentially conflicting events if the Aggregate was loaded with an expected version. Use@TargetAggregateVersionon a field of a command to indicate the expected version of the Aggregate.
But @Steven_van_Beelen stated in this comment in another post:
What the version returns depends on whether you’re Event Sourcing your Aggregate. … In short, as you’re event sourcing the Aggregate, you don’t have to use the
@AggregateVersionannotation.
Question 1
Does that also mean the @TargetAggregateVersion also does not apply if we are using Event Sourcing (which we are)?
Question 2
If we are using Event Sourcing, does that mean that the ConflictResolver doesn’t apply and can’t be used? If it does still apply, how do we use it? I’ve tried, but can’t get it to work. The conflictResolver.detectConflicts() lambda Predicate code never runs. If the ConflictResolver does apply to Event Sourcing, can you provide a detailed example of a @CommandHandler that uses a ConflictResolver to detect the conflict in the version, and how to determine what properties have changed.
Question 3
If the ConflictResolver doesn’t apply, how do we deal with this situation?
Question 4
If the Command from User A hits first, and apply an Event, and then the Command hits for user B and apply an Event, is the Event from User A guaranteed to be processed by the @EventSooureHandler and the @EventHandler in a Projection before the Event from User B?
Question 5
Based on the above answers, what is the best way to Unit Test our Aggregate’s Command Handler for this concurrent modification use case using the Axon Test Framework?
Thanks.