The Java EE monolith — EJBs, an aging Spring core, a heavyweight application server — was the enterprise standard for a reason, and it still carries critical work. We move it to a modern JVM stack and clean services one slice at a time, parity proven before any cutover.
A legacy Java EE or J2EE application is usually not failing so much as ossifying. Heavy EJBs, container-managed transactions, and a tangle of XML deployment descriptors bind the code to a specific application server and a release cadence measured in quarters. An old Spring version, long past active support, sits underneath. Everything ships together as one deployable, so a change to one corner means a full regression of the whole, and scaling means standing up another copy of the entire monolith. The JVM itself is as strong as ever — the constraint is the framework generation and the architecture, not the language.
The target is concrete: a modern LTS JVM running Jakarta EE or Spring Boot, with the app-server monolith decomposed into domain services along real business boundaries. We keep the runtime upgrade separate from the decomposition — you can clear the javax-to-jakarta move and get onto a supported JVM first, then carve out services as the seams emerge, in any cloud, including regulated and air-gapped environments.
A slice is a single behavior — one service, one bounded context. Each is analyzed, rebuilt on a modern JVM stack, validated in shadow against the monolith, and promoted only on parity. A strangler facade shifts traffic gradually; the legacy app retires last.