Zum Inhalt springen

Transaktionen aus Client-Sicht

Kurzantwort: Ein Request ist eine Transaktion. Geht irgendwo etwas schief — Validierung, Rechte, Datenbank —, wird der gesamte Request zurückgerollt; es bleibt nichts halb Geschriebenes zurück. Über mehrere Requests hinweg gibt es dagegen keine Klammer: Wer zwei Aufrufe braucht, muss selbst aufräumen.


flowchart TB
    A["Ein HTTP-Request"] --> B["alles, was er anfasst:<br/>das Objekt, seine Kinder,<br/>gelöste Beziehungen"]
    B --> C{"irgendwo ein Fehler?"}
    C -->|nein| D["gemeinsamer Commit<br/>am Ende des Requests"]
    C -->|ja| E["vollständiger Rollback<br/>— auch schon geschriebene Teile"]

Das gilt auch für verschachteltes Schreiben: Ein Auftrag mit zwanzig Positionen, bei dem Position 17 die Validierung reißt, hinterlässt keinen Auftrag und keine Position.

Praktische Folge: Sie brauchen keine Kompensationslogik innerhalb eines Requests. Der Fehlerfall ist immer „nichts passiert”.

Was Sie in mehreren Aufrufen tun, ist mehrfach committet:

POST /crm/order/create → committet
POST /crm/invoice/create → schlägt fehl

Der Auftrag bleibt bestehen. Es gibt kein „Rückgängig” über Requests hinweg und keinen Weg, mehrere Aufrufe zu einer Transaktion zu bündeln.

Konsequenz für den Entwurf: Wenn zwei Dinge zusammengehören müssen, bilden Sie sie im Modell als Beziehung ab und schreiben sie in einem Request. Geht das nicht, brauchen Sie einen fachlichen Aufräumpfad — etwa einen Status, der einen unvollständigen Vorgang erkennbar macht.

Nach einem create liest der Server das Objekt zurück, um Ihnen die angeforderten Felder zu liefern. Dieses Zurücklesen kann scheitern, obwohl das Anlegen geklappt hat — etwa weil ein Feld der response für Sie nicht lesbar ist. Dafür gibt es zwei Verhaltensweisen:

ModusVerhalten
STRICT (Standard)Anlegen und Lesen laufen zusammen. Scheitert das Lesen, wird auch das Anlegen zurückgerollt. Sie bekommen den Lesefehler; nichts wurde gespeichert.
LENIENTDas Anlegen wird zuerst festgeschrieben. Scheitert das Lesen danach, bleibt das Objekt bestehen und Sie bekommen HTTP 200 mit der Fehlerkennung CDMS_CREATE_SUCCEEDED_READ_FAILED — und der id des angelegten Objekts.

Den Modus können Sie pro Request wählen:

{
"data": { "name": "Muster GmbH" },
"response": ["+"],
"createReadMode": "LENIENT"
}

Wann lohnt sich LENIENT? Wenn das Anlegen fachlich das Wichtige ist und die Antwortdaten zweitrangig sind — etwa beim Import. Sie müssen dann nur damit rechnen, dass ein „erfolgreicher” 200er einen Teilerfolg meldet:

{
"error": "CreateSucceededReadFailedException",
"messageKey": "CDMS_CREATE_SUCCEEDED_READ_FAILED",
"code": "200",
"id": "5a2b8f1e-…"
}

Prüfen Sie im Client also nicht nur den Statuscode, sondern auch, ob data tatsächlich gefüllt ist.

Zwei Stellen, an denen ein Request nicht alles-oder-nichts ist:

1. Über Datenbankgrenzen hinweg. Berührt eine Operation sowohl mandantenübergreifende als auch mandantenbezogene Daten, liegen diese in verschiedenen Datenbanken. Der Server rollt bei einem Fehler beide zurück, aber es gibt keine verteilte Transaktion — in seltenen Fällen (Ausfall zwischen zwei Commits) kann ein Teilzustand entstehen. In der Praxis trifft das nur Operationen, die beide Bereiche schreiben; die sind bewusst selten.

2. Dateien. Der Dateiinhalt wird ins Dateisystem geschrieben, die Metadaten in die Datenbank — ohne gemeinsame Transaktion:

FallErgebnis
Upload scheitertRollback, weder Datei noch Datensatz. Sauber.
Upload klappt, Request scheitert danachDatensatz fehlt, Datei liegt verwaist im Speicher

Für Sie als Client heißt das: Ein fehlgeschlagener Upload-Request hat nichts Nutzbares hinterlassen — wiederholen Sie ihn einfach.

Es gibt weder ETag noch Versionsnummer. Zwei parallele Änderungen am selben Objekt überschreiben einander lautlos, der letzte Schreibvorgang gewinnt.

Was Sie dagegen tun können:

  • PATCH statt PUT, wenn Sie nur einzelne Felder ändern — dann kollidieren Sie nur mit jemandem, der dasselbe Feld anfasst.
  • Für kritische Abläufe eine fachliche Prüfung vorsehen (Status, Zeitstempel).
  • Bei auditierten Modellen lässt sich im Nachhinein nachvollziehen, wer wann was geändert hat — siehe Auditing.
Antwortwiederholen?
401ja, nach Token-Erneuerung
409, 503ja, mit Wartezeit
400, 422nein — der Request ist inhaltlich falsch
403, 404nein
Netzwerkfehler ohne Antwort bei createvorsichtig — der Server könnte das Objekt angelegt haben

Der letzte Fall ist der unangenehme: Es gibt keine Idempotenzschlüssel. Bei einem Abbruch ohne Antwort prüfen Sie erst per Suche (etwa über eine fachliche Nummer), ob das Objekt existiert, bevor Sie es erneut anlegen.

Wie die Transaktionsklammer im Server funktioniert, steht unter Transaktionen.