Version 4 · Migration

Runtime-Modernisierung

EJB-, JNDI- und WebSphere-spezifische Kopplung wird über Use Cases, Ports, Adapter und klare Transaktionsgrenzen geschnitten.

Migration-ÜbersichtStartseite
Runtime-Modernisierung
EJB-, JNDI- und WebSphere-spezifische Kopplung wird über Use Cases, Ports, Adapter und klare Transaktionsgrenzen geschnitten.
Großer Schnitt: Fachlogik vor Laufzeitlogik

Bei der Runtime-Modernisierung wird nicht zuerst das Framework gewechselt. Zuerst wird die fachliche Fähigkeit freigelegt: Befehl, Validierung, Regeln, Persistenz, Ereignis, Nachweis. Danach kann entschieden werden, ob ein Teil in Liberty, JBoss EAP, Spring Boot, Quarkus oder Jakarta EE weiterlebt.

Vorher: WebSphere-EJB mischt alles
Vorher: EJB mit JNDI, JDBC, JMS und Fachlogik
@Stateless
public class InvoiceBean {
    @Resource SessionContext ctx;
    @EJB CustomerBean customerBean;
    @Resource(mappedName = "jdbc/LegacyDS") DataSource ds;
    @Resource(mappedName = "jms/InvoiceQueue") Queue queue;

    public InvoiceResult createInvoice(InvoiceRequest request) {
        // Problem: UI-Validierung, DB-Zugriff, SOAP-Aufruf und MQ-Senden sind gemischt.
        // Refactoring-Ziel: fachlichen Use Case herauslösen und WebSphere-spezifische Adapter kapseln.
        try (Connection c = ds.getConnection()) {
            Customer customer = customerBean.loadCustomer(request.customerNo());
            BigDecimal amount = calculateLegacyAmount(request, customer);
            insertInvoice(c, request, amount);
            sendJmsMessage(queue, request.invoiceNo());
            return InvoiceResult.ok(request.invoiceNo(), amount);
        } catch (Exception ex) {
            ctx.setRollbackOnly();
            return InvoiceResult.failed(ex.getMessage());
        }
    }
}
Nachher: Use Case mit Ports und Adaptern
Nachher: fachlicher Use Case mit markierten Patterns
public final class CreateInvoiceUseCase {
    private final CustomerPort customerPort;          // Pattern: Ports and Adapters
    private final InvoiceRepository repository;      // Pattern: Repository
    private final InvoicePolicy policy;              // Pattern: Policy / Specification
    private final DomainEventPublisher events;       // Pattern: Outbox Publisher

    public CreateInvoiceUseCase(CustomerPort customerPort,
                                InvoiceRepository repository,
                                InvoicePolicy policy,
                                DomainEventPublisher events) {
        this.customerPort = customerPort;
        this.repository = repository;
        this.policy = policy;
        this.events = events;
    }

    public InvoiceResult handle(CreateInvoiceCommand command) {
        CustomerSnapshot customer = customerPort.load(command.customerNo());
        Invoice invoice = policy.createDraft(command, customer);
        repository.save(invoice);
        events.publish(invoice.createdEvent());
        return InvoiceResult.ok(invoice.number(), invoice.total());
    }
}
Entscheidungsmatrix Runtime
OptionWann sinnvoll
WebSphere Liberty / Open LibertyWenn viel Jakarta/Jakarta-ähnliche Struktur bleibt und Serververhalten kontrolliert migriert werden soll.
JBoss EAP auf OpenShiftWenn Java-Enterprise-Serverbetrieb mit Hersteller-Support und Application-Server-Konzept gewünscht ist.
Spring BootWenn fachliche Slices als selbstständige Services mit starkem Ökosystem entstehen sollen.
Quarkus / MicroProfileWenn Cloud-Native Java, schnelle Starts und MicroProfile-Konventionen wichtig sind.
⌂ Cockpit