6. Adapter pro Zielsystem: SOAP, REST, JMS und DB sauber trennen
Jedes Zielsystem bekommt einen Adapter, der Protokoll, Datenformat, Timeout, Fehler und Mapping kapselt.
Ein SOAP Adapter verarbeitet WSDL-Port, SOAP Headers, Faults und JAXB Mapping. Ein REST Adapter verarbeitet HTTP-Statuscodes, JSON-Strukturen und API-Versionen. Ein JMS Adapter verarbeitet Queues, Topics, Message Properties und Wiederholbarkeit. Diese Unterschiede dürfen nicht in Controller oder Prozesslogik zerstreut werden.
Die Prozesslogik ruft eine fachliche Schnittstelle auf: reserveInventory, createOrder, createInvoice, publishNotification. Wie das konkret technisch geschieht, ist Adapter-Sache.
- Adapter übersetzen Richtung Backend und zurück.
- Adapter mappen Backend-Fehler in stabile Prozessfehler.
- Adapter enthalten technische Policies wie Timeout, Retry und Header.
- Adapter sind sehr gut testbar, weil sie klare Grenzen haben.
OrderSoapAdapter mit Adapter Pattern und Fault Mapping
package com.example.portal.integration.adapter;
import com.example.portal.domain.OrderCommand;
import com.example.portal.integration.fault.BackendUnavailableException;
import com.example.portal.integration.fault.BusinessRejectedException;
import java.time.Duration;
/**
* Design Pattern: Adapter + Anti-Corruption Layer.
* Zweck: Der Prozess sieht eine fachliche createOrder-Methode.
* SOAP-Port, JAXB-Klassen und SOAP-Faults bleiben hier gekapselt.
*/
public final class OrderSoapAdapter implements OrderSystemPort {
private final GeneratedOrderSoapPort soapPort;
private final OrderSoapMapper mapper;
private final Duration timeout;
public OrderSoapAdapter(GeneratedOrderSoapPort soapPort,
OrderSoapMapper mapper,
Duration timeout) {
this.soapPort = soapPort;
this.mapper = mapper;
this.timeout = timeout;
}
@Override
public OrderSystemResult createOrder(OrderCommand command) {
try {
SubmitOrderRequest request = mapper.toSoapRequest(command);
SubmitOrderResponse response = soapPort.submitOrder(request, timeout);
return mapper.toDomainResult(response);
} catch (BusinessFaultException fault) {
throw new BusinessRejectedException(fault.code(), fault.message());
} catch (SoapTimeoutException timeoutException) {
throw new BackendUnavailableException("ORDER_SOAP_TIMEOUT", timeoutException);
}
}
}
8. Bind im Portal-Kontext: Formular, Command, SOAP Binding und Runtime Binding
Bind bedeutet in Version 3 nicht nur WSDL Binding. Es gibt mehrere Bind-Ebenen zwischen Benutzer, Portal und Backend.
Auf der Oberfläche werden Eingabefelder an ein Formularmodell gebunden. Dieses Formularmodell wird an ein fachliches Command gebunden. Das Command wird im Adapter an JAXB/SOAP-Strukturen gebunden. Zusätzlich wird zur Laufzeit die Zieladresse, Security Policy und Timeout-Konfiguration an den SOAP Client gebunden.
Wenn diese Bind-Ebenen vermischt werden, entstehen starre Portale. Wenn sie sauber getrennt sind, können UI, Prozess und Backend-Vertrag unabhängig weiterentwickelt werden.
- UI Binding: HTML-Formular zu PortalOrderForm.
- Command Binding: PortalOrderForm zu SubmitPortalOrderCommand.
- SOAP Binding: WSDL binding, operation, SOAPAction, document/literal.
- JAXB Binding: XML-Elemente zu Java-Klassen.
- Runtime Binding: Endpoint URL, Timeout, mTLS, Header, Mandant.
WSDL Binding Ausschnitt fuer Portal Order SOAP
<wsdl:binding name="OrderServiceSoap12Binding" type="tns:OrderServicePortType">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<wsdl:operation name="SubmitOrder">
<soap12:operation soapAction="urn:SubmitOrder"/>
<wsdl:input>
<soap12:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap12:body use="literal"/>
</wsdl:output>
<wsdl:fault name="BusinessFault">
<soap12:fault name="BusinessFault" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
Runtime Binding fuer SOAP Client
/**
* Design Pattern: Factory Method.
* Zweck: SOAP Client konsistent mit URL, Timeout und Header-Policy erzeugen.
*/
public final class OrderSoapClientFactory {
public GeneratedOrderSoapPort create(OrderSoapClientProperties properties) {
GeneratedOrderService service = new GeneratedOrderService(properties.wsdlLocation());
GeneratedOrderSoapPort port = service.getOrderServiceSoap12Port();
Map<String, Object> context = ((BindingProvider) port).getRequestContext();
context.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, properties.endpointUrl());
context.put("com.sun.xml.ws.request.timeout", properties.requestTimeoutMillis());
context.put("com.sun.xml.ws.connect.timeout", properties.connectTimeoutMillis());
return port;
}
}
9. SOAP Message vom Portal zum Legacy Order System
Das Portal erzeugt nicht selbst XML-Strings. Der Adapter mappt fachliche Daten in typisierte JAXB-Objekte, die Runtime serialisiert daraus einen SOAP Envelope.
Die SOAP-Nachricht enthält nicht nur fachliche Bestelldaten. Sie enthält auch technische Header wie Correlation-ID, Mandant, Benutzerkontext oder Security-Informationen. Diese Header gehören in eine kontrollierte Handler- oder Interceptor-Schicht, damit sie überall konsistent sind.
- Body enthält die vertraglich definierte Operation.
- Header enthält Kontext, Security und technische Korrelation.
- Fault Detail enthält stabile fachliche Fehlercodes.
Beispiel SOAP Envelope aus Portal-Kontext
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
xmlns:ord="http://example.com/enterprise/order/v3"
xmlns:ctx="http://example.com/enterprise/context/v1">
<soap:Header>
<ctx:CorrelationId>7f4d4b1e-8c31-4c7a-a1b8-449f1a9c1130</ctx:CorrelationId>
<ctx:Tenant>AT-B2B</ctx:Tenant>
<ctx:PortalUser>apolat</ctx:PortalUser>
</soap:Header>
<soap:Body>
<ord:SubmitOrderRequest>
<ord:PartnerOrderId>PORTAL-2026-000881</ord:PartnerOrderId>
<ord:CustomerNumber>C-4711</ord:CustomerNumber>
<ord:OrderLine>
<ord:Sku>NOTEBOOK-PRO-14</ord:Sku>
<ord:Quantity>2</ord:Quantity>
</ord:OrderLine>
</ord:SubmitOrderRequest>
</soap:Body>
</soap:Envelope>
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));
}
21. Contract Versioning: Portal-Version und SOAP-Version trennen
Ein Portal kann Version 3 sein, während ein SOAP Backend weiter Version 1 oder 2 spricht. Diese Trennung ist normal und gewollt.
Versionen beschreiben Grenzen. Die Portal-UI kann neue Felder bekommen, ohne dass sofort das SOAP Backend geändert wird. Umgekehrt kann ein SOAP-Vertrag erweitert werden, ohne die Oberfläche sofort umzubauen. Die Mapping-Schicht entscheidet, welche Informationen übertragen, defaultiert oder ignoriert werden.
Wichtig ist, optionale Erweiterungen rückwärtskompatibel zu gestalten und harte Breaking Changes zu vermeiden. Für SOAP bedeutet das oft neue Namespaces oder neue Operationen statt stiller Bedeutungsänderung bestehender Elemente.
- Portal DTOs versionieren nach UI/API-Bedarf.
- SOAP Verträge versionieren nach Backend- und Partnerkompatibilität.
- Mapping Layer dokumentiert Defaultwerte und nicht übertragbare Felder.
- Neue Pflichtfelder im SOAP-Vertrag sind kritisch.
- Alte Operationen nicht vorschnell löschen.