Replace the framework, not the data tier.
The bespoke Spring, EJB, Struts and JSF applications that have outlived their architects — typically a mix of competent SQL, layered business logic and a custom framework that nobody quite remembers the assumptions behind — refactored so the data model survives, the integration contracts survive, and the framework is replaced by a supervised metadata-driven runtime.

The schema usually survives intact
Most bespoke J2EE applications carry a relational schema that has been designed and refined over years. The schema is imported as-is into the dictionary; the platform recompiles it against the same target engine if appropriate.
SQL stays where it is
The hand-written SQL — and any stored procedures or triggers — moves with the schema. Business logic in the database tier is preserved. The platform reasons about it natively.
The framework is the problem
The Spring / EJB / Struts / JSF framework — and its accumulated configuration, custom annotations and forgotten conventions — is the part that no longer scales with the operator. It is the layer the platform replaces.
Integration contracts survive
The integration surface that the application exposes — REST endpoints, SOAP services, file drops, message queues, scheduled batch — is preserved as a contract. The platform's gRPC microservice mesh and integration layer carry it forward.
The five-layer methodology, applied to custom J2EE
The framework changes; almost everything else survives.
1 · Schema
The relational schema is imported into the dictionary's table-object catalogue. Tables, indexes, sequences, constraints become rows the platform can recompile, against the same target engine or another.
2 · Database business logic
Existing stored procedures, functions and triggers stay. Business logic embedded in Java service classes is extracted and, where it belongs in the data tier (validations, derivations, transactional rules), moved into stored procedures and triggers. Where it belongs in the middle tier (orchestration, integration, AI), it is rewritten as server-side JavaScript on the platform.
3 · Domain model becomes metadata
JPA entities, Hibernate mappings, JSF managed beans and Struts action mappings are read and reified as metadata rows. The platform materialises the application surface — table objects, REST endpoints, screens, validations — from those rows.
4 · Batch and middle tier
Server-side JavaScript on the platform's GraalVM Polyglot runtime handles what was previously Java service code — integration, file handling, document generation, scheduled processing, AI orchestration. The platform's standard library (more than forty namespaces) replaces most of the third-party Java dependencies.
5 · UI
Generated from the metadata, on Vue 3.5 / Vuetify 4. The JSF / Struts / Spring MVC screens become responsive browser UI. Role-aware rendering, accessibility and mobile delivery are properties of the runtime, not features to be added.
What changes for the operator
Custom J2EE estates are usually competent at the database tier and showing their age at every layer above. The Spring or EJB framework was a good choice when the application was built; its custom extensions and forgotten conventions are now the dominant maintenance cost. Talent for the original stack is scarce; the framework itself is several major versions behind; and the integration surface is hard to extend without disturbing the rest of the application.
The migration preserves the data tier and the integration contracts entirely. What changes is the application tier: the Spring / EJB / Struts / JSF code is replaced by metadata that the platform interprets at runtime, and what remains of the middle tier is rewritten as server-side JavaScript on the platform's runtime. The team that delivers this is a database engineer, a Java-fluent analyst who can read the existing application, and a business analyst who knows what the business expects.
A typical custom J2EE migration vs an Airtool migration
| A typical migration | An Airtool migration | |
|---|---|---|
| Approach | Re-platform to a newer Java stack (Spring Boot, Micronaut) | ✓ Replace the framework with a metadata-driven runtime |
| Schema | Re-modelled where ORM conventions changed | ✓ Imported as-is into the dictionary |
| Business logic | Re-written in the new framework's conventions | ✓ Database-tier rules preserved; middle-tier rules rewritten as server-side JavaScript |
| UI | Re-implemented in the new frontend framework | ✓ Generated from metadata at runtime |
| Integration contracts | Re-implemented; risk of breaking consumers | ✓ Preserved as platform-level contracts |
| Customer-side team | Java team experienced in the source and target stacks | ✓ Database engineer + Java-fluent analyst + our team |
| Outcome | A modern Java framework; the same architectural pattern; another migration in 7–10 years | ✓ A supervised runtime; AI- and integration-ready; no further framework migrations on the horizon |
Why custom J2EE estates benefit most from this approach
Bespoke J2EE applications are the modernisation case most often answered with "re-platform to a newer Java framework" — and the case where that answer leads to the same problem again in seven years. Each framework migration carries the same shape of risk: business logic translated, UI re-implemented, integration contracts redrawn. The estate that emerges is modern for a few years, and then it is legacy again.
Replacing the framework with a metadata-driven runtime breaks the cycle. The application stops being a Java codebase that has to be migrated and becomes a set of metadata rows the platform interprets. New requirements become dictionary changes, not Java refactors. AI agents read the same metadata the runtime reads. The migration question, for the first time, has an answer that holds for more than one generation of frameworks.