11. Inventory REST: REST-System in SOAP-Prozess integrieren
Nicht jedes Backend ist SOAP. Das Portal kann REST nutzen, ohne den SOAP-Prozess dadurch unklar zu machen.
Inventory ist häufig moderner als das Order-Legacy-System und bietet REST/JSON. Der Prozess muss trotzdem dieselbe fachliche Qualität behalten: Verfügbarkeit prüfen, Reservierung anlegen, Reservierungs-ID speichern und bei späterem Fehler eventuell kompensieren.
Wichtig ist, dass der REST Adapter HTTP-Details nicht bis in den Prozess trägt. Ein 409 Conflict kann fachlich „nicht verfügbar“ bedeuten, während ein 503 ein technischer Retry-Fall ist.
- HTTP 200/201: Reservierung erfolgreich.
- HTTP 409: fachlich nicht verfügbar.
- HTTP 429/503: technische Überlastung, Retry oder Pending.
- Reservierungs-ID im Prozessstatus speichern.
InventoryRestAdapter mit fachlichem Fehler-Mapping
/**
* Design Pattern: Adapter.
* Zweck: REST/JSON und HTTP-Statuscodes in fachliche Port-Antworten übersetzen.
*/
public final class InventoryRestAdapter implements InventorySystemPort {
private final HttpInventoryClient client;
public ReservationResult reserve(ReservationCommand command) {
HttpResponse<InventoryReservationResponse> response = client.reserve(command);
return switch (response.statusCode()) {
case 200, 201 -> ReservationResult.reserved(response.body().reservationId());
case 409 -> throw new BusinessRejectedException("OUT_OF_STOCK", "Artikel nicht verfügbar");
case 429, 503 -> throw new BackendUnavailableException("INVENTORY_BUSY");
default -> throw new BackendUnavailableException("INVENTORY_UNEXPECTED_STATUS");
};
}
}
16. Transaktionen: keine naive Gesamttransaktion über alle Systeme
Eine Portalbestellung ist ein verteilter Prozess. Eine einzige technische Datenbanktransaktion über Portal, SOAP, REST und JMS ist selten sinnvoll.
Früher wurden Enterprise-Systeme oft mit JTA/XA als globale Transaktion gedacht. In modernen Multi-System-Landschaften ist das häufig zu starr. SOAP Legacy, REST Services, Broker und Datenbank haben unterschiedliche Transaktionsmodelle. Der Portalprozess muss deshalb fachlich konsistent sein, nicht nur technisch atomar.
Die richtige Frage lautet: Welche Schritte müssen vor Benutzerantwort abgeschlossen sein? Welche Schritte dürfen später passieren? Welche Schritte brauchen Kompensation? Welche Schritte sind idempotent wiederholbar?
- Lokale Transaktion: Prozessstatus + Outbox speichern.
- Synchrone Calls: nur für direkt entscheidende Schritte.
- Asynchrone Calls: Benachrichtigung, Read-Model, Folgeprozesse.
- Kompensation: Reservierung freigeben, Bestellung stornieren, manuelle Klärung.
- Status statt Illusion: Pending ist oft ehrlicher als Schein-Erfolg.
17. Idempotenz: Doppelklick, Retry und verlorene Antworten beherrschen
Portalbenutzer klicken doppelt, Browser senden erneut, Netzwerke verlieren Antworten und Backends liefern Timeouts. Ohne Idempotenz entstehen doppelte Bestellungen.
Jede Portalbestellung braucht eine fachliche Idempotency-Key-Strategie. Ein PartnerOrderId, kombiniert mit Mandant und Benutzerkontext, kann verhindern, dass derselbe Benutzerwunsch mehrfach verarbeitet wird. Der Prozess prüft vor dem Start, ob bereits ein Prozess für diese fachliche Anfrage existiert.
Auch SOAP Backends sollten eine externe Referenz erhalten. Wenn die Antwort verloren geht, kann das Portal später über Status oder Suchoperation prüfen, ob die Bestellung bereits angelegt wurde.
- Idempotency Key = tenant + partnerOrderId + processType.
- Vor Start prüfen, ob Prozess schon existiert.
- Backend-Referenz speichern, sobald bekannt.
- Retry nur für technische Fehler, nicht für fachliche Ablehnungen.
- Antwortverlust als eigener Fall behandeln.
Idempotenz im Prozessstart
public PortalOrderResult start(SubmitPortalOrderCommand command) {
String idempotencyKey = command.tenant() + ":" + command.partnerOrderId();
return states.findByIdempotencyKey(idempotencyKey)
.map(existing -> PortalOrderResult.fromExisting(existing.trackingId(), existing.status()))
.orElseGet(() -> startNewProcess(command, idempotencyKey));
}
25. Performance: Latenzbudget statt Hoffnung
Ein Portal, das nacheinander fünf Systeme synchron aufruft, wird schnell langsam. Deshalb braucht jeder Call ein Latenzbudget.
Die Benutzererfahrung hängt an der Summe aller synchronen Schritte. Wenn Customer 300 ms, Inventory 500 ms, Order SOAP 1.500 ms und Billing 1.200 ms braucht, ist die Seite schnell bei mehreren Sekunden. Bei Last oder Timeout wird es unbrauchbar.
Deshalb muss man entscheiden: Was braucht der Benutzer wirklich sofort? Was kann asynchron laufen? Welche Daten können gecacht werden? Wo ist ein Pending-Status besser als Warten?
- Jeder Backend-Call bekommt connect timeout und read timeout.
- Keine unendlichen SOAP-Timeouts.
- Parallelisierung nur, wenn fachlich unabhängig.
- Caching für Referenzdaten, nicht für kritische Sperrstatus ohne Konzept.
- Bulk/Batch für Statuslisten statt N+1 SOAP Calls.