Abschlussmigration
Ein Legacy-Order-&-Billing-Slice wird schrittweise strangled, getestet und produktionsreif gemacht.
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.
@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.
// 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);
}
}
@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 ü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.
@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:
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.