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.
Die Regel
Abschnitt betitelt „Die Regel“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”.
Zwischen zwei Requests gibt es keine Klammer
Abschnitt betitelt „Zwischen zwei Requests gibt es keine Klammer“Was Sie in mehreren Aufrufen tun, ist mehrfach committet:
POST /crm/order/create → committetPOST /crm/invoice/create → schlägt fehlDer 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.
Der Sonderfall: angelegt, aber nicht lesbar
Abschnitt betitelt „Der Sonderfall: angelegt, aber nicht lesbar“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:
| Modus | Verhalten |
|---|---|
STRICT (Standard) | Anlegen und Lesen laufen zusammen. Scheitert das Lesen, wird auch das Anlegen zurückgerollt. Sie bekommen den Lesefehler; nichts wurde gespeichert. |
LENIENT | Das 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.
Wo die Atomarität endet
Abschnitt betitelt „Wo die Atomarität endet“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:
| Fall | Ergebnis |
|---|---|
| Upload scheitert | Rollback, weder Datei noch Datensatz. Sauber. |
| Upload klappt, Request scheitert danach | Datensatz 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.
Kein Optimistic Locking
Abschnitt betitelt „Kein Optimistic Locking“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.
Wiederholen — wann ist das sicher?
Abschnitt betitelt „Wiederholen — wann ist das sicher?“| Antwort | wiederholen? |
|---|---|
| 401 | ja, nach Token-Erneuerung |
| 409, 503 | ja, mit Wartezeit |
| 400, 422 | nein — der Request ist inhaltlich falsch |
| 403, 404 | nein |
| Netzwerkfehler ohne Antwort bei create | vorsichtig — 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.
Details
Abschnitt betitelt „Details“Wie die Transaktionsklammer im Server funktioniert, steht unter Transaktionen.