Our experience migrating from Axon Framework 4.12 to 5.0

We recently successfully completed migrating our CQRS/Event Sourcing backend from Axon Framework 4.12 to 5.0. The codebase has dozens of aggregates, multiple sagas, and hundreds of event handlers, so it wasn’t trivial.

We wrote up what we learned along the way — the biggest surprises were the scope of API changes (every package moved), replacing sagas with stateful event handlers backed by database persistence, and the underdocumented @EventTag requirement that silently breaks test fixtures.

The full write-up is here: Migrating from Axon Framework 4 to 5: What We Learned — SaaSForge

A few things I’d highlight for anyone planning the same migration:

  • Automate the mechanical work (package renames, annotation swaps) with scripts — it’s the bulk of compilation errors but trivially scriptable
  • The saga-to-stateful-handler refactoring was the biggest change but also the most valuable
  • Watch out for the upcaster gap (not available until 5.2.0)
  • @EventTag on every event + @EventSourcedEntity(tagKey) on every aggregate — without this, test fixtures silently fail to replay events

Happy to answer questions if anyone is going through the same process.

And we have to say: the Axon 5 works REALLY well now when the full reactive stack is embraced!

@petrmac, I must applaud the effort you’ve put into this! We are planning extensive migration support, as we are fully aware of the sheer number of changes. Our first steps for this can be found here. But you did it nonetheless and wrote a blog post about it! I can’t stress enough how awesome I find it to read things like this, so thanks for sharing!

Thanks for the kind words, Steven! We initially implemented the service in version 4, but soon figured out that blocking code inside reactive chain is not a good idea. Now with reactive stack we can achieve excellent throughput with just 1 reasonably sized pod. Our stress tests show that with the reactive stack we can easily accomodate hundreds of concurrent users with just 3 gigs or RAM (roughly 10x)!
Will write more on the performance, once I have time.

Thanks for a great framework again.

Thanks for your write up - I enjoyed reading it and it definitely helps those of us who are familiar with v4 and are looking to adopt v5.

@petrmac How did you guys manage on migrating deadlines within sagas to stateful event handlers in Axon Framework 5?

Yep, fully migrated. We kept old sagas as deprecated for a while. They are deleted now and everything is now as stateful handler.
BTW, greetings from Devoxx Greece. If someone wants to meet, I will be around.

Did you have to use event scheduler instead of a deadline manager for stateful event handling, since the scope for deadlines’ are only sagas and aggregates?

We didn’t use EventScheduler in place of DeadlineManager — we replaced both. Time-based triggers became Spring @Scheduled jobs that query Postgres for what’s “due”, and multi-event coordination became ProcessStateService rows. When Axon 5.2’s saga module ships, we’ll likely keep this split: the deadline-attached-to-saga model is elegant, but for our two main use cases — batch-import coordination and trial lifecycles — DB-as-source-of-truth gives us better introspection and testability.

Hi James!

I made a writeup on what we have actually used. You can read more details here: Replacing Axon Sagas with Stateful Event Handlers: What We Built Instead — SaaSForge