Modernisierung / Migration

Diese Seite bleibt auch ohne JavaScript lesbar. Suche und Buttons sind Zusatzkomfort.

Legacy-Migration zu Spring Enterprise

Von EJB, SOAP, JSP, JTA und EAR zu Spring Boot, REST, Events, Observability und Cloud-Betrieb.

Legacy zu Spring: Strangler statt Big Bang Legacy EAREJB • SOAP • JSP Anti-CorruptionAdapter/FassadeDaten & Verträge Spring ServiceREST • Events • Cloud Jeder Schnitt: Messbarkeit, Vertragstest, Rollback, Betriebsmodell.
1. Nicht Big Bang, sondern kontrollierter Schnitt

Alte Java-Enterprise-Systeme enthalten oft EJB-Services, SOAP-Schnittstellen, JTA-Transaktionen, JMS, JSP/JSF, EAR-Deployment und Application-Server-spezifische Konfiguration. Eine direkte Komplettmigration ist riskant, weil Fachlogik, technische Infrastruktur und Betriebswissen vermischt sind.

Ein Strangler-Ansatz schneidet fachliche Fähigkeiten nach und nach heraus. Neue Spring-Services übernehmen klar begrenzte Funktionen, während Adapter die alte Welt schützen. Jeder Schritt braucht Vertrag, Messung, Rollback und Datenstrategie.

Anti-Corruption Adapter für SOAP Legacy
@Component
class LegacyCustomerAdapter implements CustomerPort {
    private final LegacyCustomerSoapClient soap;

    LegacyCustomerAdapter(LegacyCustomerSoapClient soap) {
        this.soap = soap;
    }

    @Override
    public CustomerSnapshot load(CustomerId id) {
        LegacyCustomerResponse response = soap.getCustomer(id.value());
        return new CustomerSnapshot(
            CustomerId.of(response.customerNumber()),
            response.fullName(),
            CustomerStatus.fromLegacyCode(response.statusCode())
        );
    }
}
2. Migration von EJB Service zu Spring Use Case

Ein alter EJB-Service mischt häufig Remote-Schnittstelle, Transaktion, Fachlogik und technische Lookups. Bei der Migration sollte zuerst die Fachoperation isoliert werden. Danach wird sie als Spring Service mit expliziten Ports, Transaktionsgrenze und Tests aufgebaut.

Legacy EJB Ausgangspunkt
@Stateless
public class OrderFacadeBean implements OrderFacadeRemote {
    @Resource private SessionContext ctx;
    @EJB private PricingBean pricing;
    @PersistenceContext private EntityManager em;

    public String submit(OrderDto dto) {
        // Validierung, Preisberechnung, Persistenz, Audit und JMS in einer Methode
        // schwer testbar und stark an Container gekoppelt
        return "OK";
    }
}
Spring Zielbild mit Ports
@Service
class SubmitOrderUseCase {
    private final PricingPort pricing;
    private final OrderRepository orders;
    private final EventPublisher events;

    @Transactional
    public OrderId submit(SubmitOrder command) {
        Price price = pricing.calculate(command.lines());
        Order order = Order.submit(command.customerId(), command.lines(), price);
        orders.save(order);
        events.publishAfterCommit(new OrderSubmitted(order.id()));
        return order.id();
    }
}
3. Datenstrategie

Die schwierigste Frage ist oft nicht REST oder Boot, sondern Daten. Gemeinsame Datenbank, geteilte Tabellen, Stored Procedures und implizite Nebenwirkungen müssen sichtbar gemacht werden. Optionen sind Read-only-Zugriff, separate Read Models, Outbox Pattern, CDC oder schrittweise Tabellenhoheit.

Outbox-Grundidee
CREATE TABLE outbox_event (
  id UUID PRIMARY KEY,
  aggregate_id UUID NOT NULL,
  event_type VARCHAR(120) NOT NULL,
  payload JSONB NOT NULL,
  created_at TIMESTAMP NOT NULL,
  published_at TIMESTAMP NULL
);

Enterprise-Prüffragen

  • Fachliche Fähigkeit klar geschnitten?
  • Adapter schützt neue Domäne vor Legacy-Modell?
  • Datenhoheit und Synchronisation geklärt?
  • Rollback und Monitoring pro Migrationsschritt vorhanden?