a little secret (don’t tell anyone ): in Axon, the @EventHandler and the @EventSourcingHandler are technically identical. In fact there isn’t even any mechanism triggering on either of these annotations. It’s just because they are meta-annotated with @MessageHandler(messageType = EventMessage.class) that makes Axon trigger on them. It is, however, recommended to use @EventSourcingHandler, as it best describes the intent of the method.
Implementing a dirty check based on events is possible, but slightly cumbersome. The problem is that, from a GenericJpaRepository perspective, the aggregate is somewhat a black box. That’s different when using an EventSourcingRepository, where at least one can assume the aggregate is event sourced.
The general approach would be to wrap or subclass the GenericJpaRepository and wrap the EventBus that is passed into it. Around the publish() method, set a dirty flag to true (preferably as a resource in the UnitOfWork). In the save method on your wrapping repository, only forward the save call to the GenericJpaRepository if the flag is set to true.