Exactly Andy, there are no stupid questions!
You’ve just hit a subject not that many people use, to be honest.
The Aggregate Versioning logic has existed since Axon Framework 2, about nine years ago.
We’ve had limited requests on how to use this within that time frame.
This, combined with the fact that nine years ago, there was just one primary maintainer (AxonIQ’s CTO nowadays), documentation is definitely lacking.
For this, I’d like to extend my apologies.
If you want to file an issue for it with our Reference Guide, that would be amazing.
Of course, if you have some spare time and interest in doing so.
Now, let me go to your questions regarding the versioning support Axon Framework provides:
In terms of using a version for optimistic locking, why bother with an
@AggregateVersionfield at all when the aggregate instance can simpy get it from theAggregateLifecycle?
What the version returns depends on whether you’re Event Sourcing your Aggregate.
If you are not, the user is expected to define the version through the @AggregateVersion annotation, which they can put on a field or method.
If you are using Event Sourcing (the default in Axon Framework), the sequence number is indeed used, with no direct option to change this.
In short, as you’re event sourcing the Aggregate, you don’t have to use the @AggregateVersion annotation.
Is there an easier, more reliable way of setting it, as opposed to having to do it manually in
@EventSourcingHandlermethods?
As the annotation isn’t necessary for your application, this question might not be overly significant.
I will give a response regardless, though.
There is no easy way to invoke some task on every event, apart from having an Event Sourcing Handler for all Aggregate events.
There is a way to make a generic event handler, which is only invoked when there is no more specific event handler for the event in question.
To make it more concrete:
class MyAggregate {
// Some state, the aggregate id, and command handlers left out
@EventSourcingHandler
public void on(BazEvent event) {
// update specific state
}
@EventSourcingHandler
public void on(Object event) {
// update generic state like version
}
}
class FooEvent { }
class BarEvent { }
class BazEvent { }
If the MyAggregate published foo, bar, and baz, and the Aggregate is event-sourced:
FooEventis only handled by the least specific handleron(Object)BarEventis only handled by the least specific handleron(Object)BazEventis only handled by the more specific handleron(BazEvent)
With this, you can thus have one catch-remaining-event-handler.
That way, you wouldn’t have to write an EventSourcingHandler for every event if you’re not interested in the rest of the event.
Exactly how and when does the framework use the annotated field, if at all?
Axon Framework will thus only use the annotated field if you have a State-Stored Aggregate.
However, the aggregate version may be used regardless of the @AggregateVersion annotated field.
More specifically, Axon Framework will use it to compare with the @TargetAggregateVersion field within a command.
Hence, you can make a command like so:
class SomeCommand {
@TargetAggregateIdentifier
private final UUID aggregateId;
@TargetAggregateVersion
private final long aggregateVersion;
// left out rest of state, getters, constructor, equals, and hashcode
}
By dispatching a command like SomeCommand, the field annotated with @TargetAggregateVersion is used to validate if the Aggregate the command is targeted towards is still on that version.
If it is not, a ConflictingAggregateVersionException will be thrown.
Well, quite a lengthy response here.
I do hope everything makes sense here, @andye2004!
Be sure to fire additional questions if necessary. ![]()