AxonFramework
                                
                                
                                
                                    AxonFramework copied to clipboard
                            
                            
                            
                        Improved API for Aggregate State Transitions
Feature Request: Improved API for state transitions.
I came across a wonderful project by @idugalic on github (https://github.com/idugalic/axon-statemachine-demo) which highlights the new polymorphic aggregate feature of axon as well as a finite state machine for an illustrative Order aggregate.
The github README describes the transitions which are valid/invalid for the aggregate and there is also a good image which I am copying here for easier reference:

So, we can transition from an order created to an order paid or order cancelled state. Below is an example of how state transitions are currently expressed in code:
From OrderCreated -> OrderPaid:
@Override @CommandHandler
void on(MarkOrderAsPaidCommand command) {
    apply(new OrderPayingInitiatedEvent(command.getAggregateIdentifier())).andThen(() -> {
        try { createNew(OrderPaid.class, () -> new OrderPaid(orderId, OrderStatus.NEW, items)); }
        catch (Exception e) {
            log.error("Can't create OrderPaid aggregate", e);
            throw new CommandExecutionException("Can't transition to PAID state", e);
        }
    });
}
From OrderCreated -> OrderCancelled:
@Override @CommandHandler
void on(MarkOrderAsCancelledCommand command) {
    apply(new OrderCancellationInitiatedEvent(command.getAggregateIdentifier())).andThen(() -> {
        try { createNew(OrderCanceled.class, () -> new OrderCanceled(orderId, OrderStatus.NEW, items)); } 
       catch (Exception e) {
            log.error("Can not create OrderCanceled aggregate", e);
            throw new CommandExecutionException("Can't transition to CANCELED state", e);
        }
    });
}
My feature request is to improve the API so that the code can be read directly to express the fact that these are transitions. Currently the usage of createNew kind of throws me because it means we are creating a new aggregate (when in my head a transition would still mean it is the same aggregate). Perhaps instead of createNew there could be a stateTransition method, or perhaps there could be an annotated method @StateTransition.
Perhaps my reading of the code is wrong - please chime in if I have misread something.
I don't have a concrete proposal for API improvement just yet but I'm creating this issue for discussions around how an "improved" API for state transitions would look within Axon. I may post back with further discussion points once I have some time.
Regards, vab2048
Thanks for filing this with us @vab2048. As it stands, we were already having discussion between the team members to introduce such a feature, with some thoughts on the API as well.
We just missed to actually draft up the issue; thus, thanks for making it so. :-) Once we start working on this feature, we will update this issue accordingly. So stay tuned!
As shown above, I've mentioned this issue in #2417. The mentioned issue is in essence a duplicate of this issue. However, it contains quite some information and thought on the subject too. As such, I recommend we read both issues once development on aggregate state transitions starts.
I gave this issue some thought and came up with some ideas (all annotation names and class/interface names are made up and can be renamed to something appropriate in the future - please comment on the main idea not the names!):
- We have a generic "state validator" interface which takes the aggregate type as the type parameter.
- Let's call it 
FiniteStateValidator<T>. - It defines a single method 
boolean validate(T aggregate) 
 - Let's call it 
 - We create an implementation for each possible state in the state machine.
- e.g. for aggregate of type 
Orderwe would have:class OrderCreated implements FiniteStateValidator<Order> { /* ... */}class OrderPaid implements FiniteStateValidator<Order> { /* ... */}- etc.
 
 - The 
validatemethod would take the latest aggregate as an argument then perform logic on it to determine whether it was indeed in the given state. Obviously you would need to either make your implementation class a static inner class if the fields/methods you want access to are private, or you can put it in the same package if they are package private. 
 - e.g. for aggregate of type 
 - We can then place some annotation on top of command handlers within the aggregate.
- Let's call it 
@StateMachineCheck(<some-class>)where<some-class>must be a class implementingFiniteStateValidator<Order>. - The framework having seen this annotation would do the leg work to wire up an interceptor that is invoked before the command is invoked to verify that the aggregate is indeed in that state of the finite state machine.
- This would involve calling the 
validatemethod on the specific class for that state with the loaded aggregate passed as an argument. 
 - This would involve calling the 
 - Example annotation values on a 
@CommandHandler:@StateMachineCheck(OrderCreated.class)- A 
@CommandHandlerannotated with this will only run if the validate method of the OrderCreated class returned true. - If it does not return true the framework will throw an exception.
 
- A 
 @StateMachineCheck(values = {OrderCreated.class, OrderPaid.class })- A 
@CommandHandlerannotated with this will run if the aggregate is in theOrderCreatedOROrderPaidstate.- So only one of the boolean methods needs to be true.
 
 - If neither method call to 
validate()returns true the framework will throw an exception. 
- A 
 
 
 - Let's call it 
 
At this point you may be asking yourself, what is the point? Just have the validate methods for each finite state in the aggregate class itself and then make sure you call the relevant methods in the body of your @CommandHandler. Yes that would work but the above annotations would signal intent and be easier to read. Plus it is only part of the picture.
In addition, now suppose we introduce a class level annotation which goes on the aggregate: @FiniteStateMachine. With this on the aggregate itself, the behaviour changes so that every single @CommandHandler must be annotated with a @StateMachineCheck (maybe all except the creational command handler).
By opting in to a @FiniteStateMachine you implicitly inherit an END state. If the aggregate is in the END state it automatically rejects all commands (there probably would need to be a special @CommandHandler that could be used to resurrect it as an extreme exception). This would solve the boiler plate issue mentioned here.
Now with every @CommandHandler being defined such that they know the state of the state machine they are in before being run we are still left to deal with how to define the state transitions:
- That is the job of another annotation: 
@StateMachineTransitionsTo(<some-class>)where<some-class>must be a class implementingFiniteStateValidator<Order>. - This annotation goes on the 
@EventSourcingHandler. - The 
validatemethod could be called after running the@EventSourcingHandlerto validate it has indeed transitioned to that state in the state machine. - You could even make it part of the transaction so that the event is only persisted if the state transition is successful.
 
Now with introspection you have the full finite state machine and the allowed operations (commands) for each finite state, as well as every event which transitions the states from one to another.
I just thought of this and wanted to share in case it is useful. I know this type of feature would be low priority anyway 👍