Lab 12

Abschlussmigration

Ein Legacy-Order-&-Billing-Slice wird schrittweise strangled, getestet und produktionsreif gemacht.

CapstoneStrangler FigADRMigration
SchwierigkeitFortgeschritten+
Dauer120–180 Min
LernstufeStufe 6 – Praxislabor und Capstone
Arbeitsweise: Erst den Vorher-Code öffnen, dann Analyse lesen, anschließend die Nachher-Lösung und den Test vergleichen. Alle Abschnitte sind standardmäßig geschlossen.

Workshop-Slice

Dieses Lab zeigt einen konkreten Enterprise-Fehler mit Vorher/Nachher-Code. Die Beispiele sind bewusst fachlich benannt, damit du Architekturentscheidungen und nicht nur Syntax übst.

Lernziel

Du kombinierst Architektur, Refactoring, Integration, Tests, Security, Observability und Deployment in einem Abschluss-Slice.

Ausgangslage

Ein WebSphere/EJB-ähnlicher Legacy-Baustein erzeugt Bestellung, Rechnung und Partnerbenachrichtigung synchron in einer großen Transaktion. Jede Änderung ist riskant.

Vorher: problematischer Code

Der alte Service enthält Fachlogik, Transaktion, SOAP, Datenbank und Benachrichtigung in einer Methode.

LegacyOrderBillingBean.javaJAVA
@Stateless
public class LegacyOrderBillingBean {
    @Resource SessionContext ctx;
    @EJB LegacyBillingClient billing;
    @Resource DataSource ds;

    public String placeOrder(LegacyOrderRequest request) {
        try (Connection c = ds.getConnection()) {
            c.setAutoCommit(false);
            String orderNo = insertOrder(c, request);
            String invoiceNo = billing.createInvoice(request.customerNo(), request.amount());
            insertAudit(c, orderNo, invoiceNo);
            sendPartnerNotification(orderNo, invoiceNo);
            c.commit();
            return orderNo;
        } catch (Exception ex) {
            ctx.setRollbackOnly();
            throw new EJBException(ex);
        }
    }
}
Analyse: Was ist daran schlecht?
  • Eine Methode koordiniert mehrere Systeme und technische Details.
  • Partnerbenachrichtigung kann Transaktion blockieren.
  • SOAP-Fehler werden zu EJBException ohne fachlichen Vertrag.
  • Keine schrittweise Migration möglich, weil alles gekoppelt ist.
Nachher: bessere Lösung

Die Zielversion migriert einen Slice mit Strangler-Fassade, Use Case, Ports, Outbox und ADR.

OrderMigrationFacade.javaJAVA
// Pattern: Strangler Fig Facade
// Zweck: Neue Orders laufen über den modernen Slice, Altpfade bleiben kontrolliert erhalten.
public final class OrderMigrationFacade {
    private final PlaceOrderUseCase newUseCase;
    private final LegacyOrderBillingPort legacy;
    private final MigrationToggle toggle;

    public OrderNumber placeOrder(PlaceOrderCommand command) {
        if (toggle.useNewOrderSliceFor(command.customerId())) {
            return newUseCase.place(command);
        }
        return legacy.placeOrder(command);
    }
}
PlaceOrderUseCase.javaJAVA
@Transactional
public OrderNumber place(PlaceOrderCommand command) {
    CustomerSnapshot customer = customerPort.loadCustomer(command.customerId());
    Order order = Order.place(command.orderId(), customer, command.lines());
    orderRepository.save(order);

    outbox.store(OutboxEvent.fromDomainEvent(order.pullDomainEvents().getFirst()));
    audit.write(AuditEntry.orderPlaced(order.orderNumber(), command.requestedBy()));
    return order.orderNumber();
}
ADR-014-order-slice-migration.mdMARKDOWN
# ADR-014: Order-Slice über Strangler-Fassade migrieren

## Entscheidung
Neue Order-Anlagen werden kundensegmentweise über `OrderMigrationFacade` in den modernen Slice geleitet.

## Begründung
- geringer Cutover-Risiko
- Legacy bleibt reversibel erreichbar
- Tests und Observability können pro Segment aufgebaut werden
- Outbox entkoppelt Partnerbenachrichtigung vom Transaktionspfad

## Konsequenzen
- Feature Toggle muss auditiert werden
- Datenvergleich zwischen Legacy und neuem Slice ist Pflicht
- Rückfallpfad bleibt bis zum Abschluss aktiv
Test / Prüfnachweis

Dieser Abschnitt zeigt, wie du die Verbesserung nachweist. Es ist bewusst kein reiner Happy-Path-Test, sondern prüft ein Risiko aus dem Vorher-Teil.

CapstoneSliceIT.javaJAVA
@Test
void newCustomerSegmentUsesModernSliceAndCreatesOutboxEvent() {
    toggle.enableModernSliceFor("B2B-AT");

    OrderNumber number = facade.placeOrder(commandForCustomerSegment("B2B-AT"));

    assertThat(orderRepository.findByOrderNumber(number)).isPresent();
    assertThat(outboxRepository.findUnpublished())
        .extracting(OutboxEvent::eventType)
        .contains("OrderPlaced");
    assertThat(legacySpy.wasCalled()).isFalse();
}
cutover-checklist.yamlYAML
cutover:
  prechecks:
    - modern_slice_health_green
    - outbox_lag_below_30s
    - reconciliation_diff_zero_for_last_24h
  rollout:
    - enable_segment: B2B-AT-5PERCENT
    - watch: order_error_rate
    - watch: billing_invoice_latency
  rollback:
    - disable_feature_toggle
    - replay_unpublished_outbox_events
Typische Fehler
  • Big-Bang-Ablösung ohne Rückfallpfad.
  • Legacy-Datenmodell 1:1 in neue Domain übernehmen.
  • Outbox/Replay erst nach dem ersten Incident planen.
  • ADR und Cutover-Checkliste weglassen.
Deep-Learning-Bezug

Die Links führen zum ausführlichen Inhalt; die Deep-Learning-Seite bleibt nur die Lernlandkarte und kopiert den Inhalt nicht doppelt.

Prüfcheckliste
  • Strangler-Fassade entscheidet nachvollziehbar.
  • Legacy-Rückfallpfad bleibt bis Cutover vorhanden.
  • Outbox entkoppelt externe Benachrichtigung.
  • ADR und Cutover-Checkliste sind vorhanden.
  • Integrationstest beweist neuen Slice.
⌂ Cockpit