FYI for anyone who is working on porting from Axon 2 to Axon 4: the semantics of apply() changed in a way that may require changes to aggregate code. Happily, my JUnit tests caught this because a bunch of command handlers stopped generating the same events as under Axon 2. Below are the two options I came up with; would love to hear others if I’m missing something better.
Command handlers on event-sourced aggregates aren’t supposed to update state; that has to be done in event handlers. However, in Axon 2, the apply() method was synchronous: when it returned, the aggregate’s state would reflect the applied events, and command handlers could thus use that state to decide what to do next. For example:
`
@CommandHandler
public void handle(MyCommand command) {
FirstEvent event = makeEvent(command);
apply(event);
if (flagThatGetsSetByFirstEventHandler) {
apply(makeSecondEvent());
}
if (flagThatGetsSetByFirstOrSecondEventHandler) {
apply(makeThirdEvent());
}
}
`
In Axon 4, this won’t work, because apply() defers event handling until after the command handler returns. So the second and third events would never be published. There appear to be two ways to port this logic. First is to use callbacks:
@CommandHandler public void handle(MyCommand command) { FirstEvent event = makeEvent(command); apply(event).andThen(() -> { if (flagThatGetsSetByFirstEventHandler) { apply(makeSecondEvent()).andThen(() -> { if (flagThatGetsSetByFirstOrSecondEventHandler) { apply(makeThirdEvent()); } }); } else if (flagThatGetsSetByFirstOrSecondEventHandler) { apply(makeThirdEvent()); } }); }
Second is to move the dependent logic into event handlers, guarded by isLive().
`
@CommandHandler
public void handle(MyCommand command) {
apply(makeEvent(command));
}
@EventSourcingHandler
public void on(FirstEvent event) {
updateState(event);
if (isLive() && flagThatGetsSetByFirstEventHandler) {
apply(makeSecondEvent());
}
if (flagThatGetsSetByFirstOrSecondEventHandler) {
if (isLive()) {
apply(makeThirdEvent());
}
thirdEventAlreadyApplied = true;
}
}
@EventSourcingHandler
public void on(SecondEvent event) {
updateState(event);
if (isLive() && flagThatGetsSetByFirstOrSecondEventHandler && !thirdEventAlreadyApplied) {
apply(makeThirdEvent());
}
}
`
Feedback welcome!
-Steve