Worum es geht
Ein Hook darf mehr als Felder setzen. Er kann über den System-Layer eines Modells selbst schreiben: einen Protokolleintrag anlegen, einen abgeleiteten Datensatz erzeugen, ein anderes Objekt ändern. Das ist ein zweiter Schreibvorgang, der mitten im ersten läuft.
CDMS behandelt jeden Schreibvorgang als eigene Ebene. Der Schreibvorgang des Requests ist Ebene 1, ein Schreibvorgang aus einem seiner Hooks Ebene 2 und so weiter.
Zwei Ebenen im Ablauf
Beispiel: Ein Before-Hook an Order legt zu jeder neuen Bestellung einen Protokolleintrag an.
sequenceDiagram
participant C as Client
participant S1 as System-Layer Order (Ebene 1)
participant H as Before-Hook
participant S2 as System-Layer Log (Ebene 2)
participant DB as Datenbank
C->>S1: POST /order/create
S1->>S1: Graph bauen, Regeln sammeln
S1->>H: Before-Hooks
H->>S2: createObject(Log)
S2->>S2: eigene Regeln, eigene Before-Hooks, eigene Meldung
S2->>DB: Log schreiben (ohne Commit)
S2->>S2: eigene After-Hooks
S2-->>H: Log
H-->>S1: fertig
S1->>S1: eigene Regeln melden (422, falls verletzt)
S1->>DB: Order schreiben
S1->>S1: eigene After-Hooks
S1-->>C: 200
Note over S1,DB: Commit am Ende des Requests, für beide
Was dabei für jede Ebene gilt:
| Ebene 1 (Order) | Ebene 2 (Log) | |
|---|---|---|
| Regelverstöße | nur die eigenen, gemeldet vor dem Schreiben von Order | nur die eigenen, gemeldet vor dem Schreiben von Log |
| Before-Hooks | die von Order und ihren Kindern | die von Log und seinen Kindern |
| After-Hooks | laufen, nachdem Order geschrieben ist | laufen, nachdem Log geschrieben ist |
| Rechte | Klassenrolle von Order, Feldrechte des Abstiegs | Klassenrolle von Log; ein Feldrecht aus Ebene 1 gilt hier nicht |
| Commit | am Ende des Requests | keiner, gehört zu Ebene 1 |
Was am Ende gespeichert ist
| Bestellung | Protokolleintrag | Ergebnis |
|---|---|---|
| gültig | gültig | 200, beide gespeichert |
| verletzt eine Regel | gültig | 422 mit den Verstößen der Bestellung, nichts gespeichert |
| gültig | verletzt eine Regel | 422 mit den Verstößen des Protokolleintrags, nichts gespeichert |
| egal | Hook wirft | Antwort nach der Exception, siehe Wenn ein Hook scheitert, nichts gespeichert |
Auch ein innerer Create, der createReadMode: LENIENT verlangt, committet nicht. LENIENT gilt nur für den Schreibvorgang des Requests, siehe Anlegen und Zurücklesen: STRICT oder LENIENT. Sonst wäre der Protokolleintrag schon festgeschrieben, bevor die Bestellung geprüft ist.
Wie tief es gehen darf
Ein Hook, der auf jedes Schreiben seines eigenen Modells wieder schreibt, würde nie aufhören. CDMS begrenzt deshalb die Tiefe:
-
1Client→CDMSPOST /api/rest/order/create
-
2CDMSEbene 1: Order, Before-Hook legt Order an
-
3CDMSEbene 2, 3, … bis zur Maximaltiefe: jede Order legt die nächste an
-
4CDMSDie nächste Ebene läge über der Maximaltiefe
-
5CDMS
HookExecutionExceptionwrite-nesting-too-deep|10→ 500 -
6CDMS→Databaseganzer Request wird zurückgerollt, keine einzige Order bleibt
Die Maximaltiefe stellst du in der application.yaml ein:
codamai:
cdms:
api:
max-write-nesting-depth: 10 # Ebene 1 ist der Schreibvorgang des Requests
Die Antwort ist 500, nicht 4xx: Der Client hat nichts falsch gemacht, die Ursache ist ein Hook.
Im Monitoring
Hat die Anwendung Micrometer und eine Registry, misst CDMS jeden Schreibvorgang des Requests:
| Metrik | Art | Bedeutung |
|---|---|---|
cdms.write.nesting.depth | Verteilung | die tiefste Ebene, die ein Schreibvorgang erreicht hat; 1 heißt: kein Hook hat geschrieben |
cdms.write.nesting.refused | Zähler | Schreibvorgänge, die wegen zu großer Tiefe abgelehnt wurden |
Beide tragen das Modell des Schreibvorgangs auf Ebene 1 als Tag model. Ein Maximum, das langsam steigt, heißt: Hooks rufen Hooks auf. Jede Ablehnung steht zusätzlich als Warnung im Log.
Fallen
Wie es weitergeht
- Wo Hooks im Ablauf liegen: Die Reihenfolge in einem Schreibvorgang
- Hooks für Kinder desselben Schreibvorgangs: Hooks bei verschachtelten Objekten und Kaskaden
- Was passiert, wenn ein Hook wirft: Wenn ein Hook scheitert