Schichten und Request-Flow
Kurzantwort: Ein Request läuft durch fünf Stationen: Filterkette (CIAS) → REST-API → System-Layer → Autorisierung/Persistenz/Dateien → Datenbank/Dateisystem. Die REST-Schicht kennt nur Payloads, der System-Layer kennt die Fachlogik, die Persistenz kennt die Technik. Die Transaktion umklammert den gesamten Request, nicht die einzelne Service-Methode.
Die Schichten
Abschnitt betitelt „Die Schichten“| Schicht | Modul | Verantwortung |
|---|---|---|
| Filterkette | cias-authentication | Token prüfen, RequestContext aufbauen, Kontextwechsel autorisieren |
| REST-API | cdms-rest-api | Payloads entgegennehmen, Multipart/Base64-Dateien in Temp-Dateien schreiben, Response Request expandieren, Fehler abbilden |
| System-Layer | cdms-system-layer | Rekursion über den Objektgraphen, Validierung, Hooks, Owner-/Attributfilter, Orchestrierung von Create+Read |
| Autorisierung | cdms-authorization | Rollenprüfung je Modell und Operation |
| Persistenz | cdms-database | Persistenzziel auflösen, EntityManager, Criteria-Abfragen, Feldprojektion, Envers |
| Dateien | cdms-localfs-storage | Pfad bilden, Bytes bewegen, Versionen halten |
Die Dateischicht ist die einzige optionale: Ein System mit storage: NONE
bekommt kein Backend und startet ohne eines. Siehe
Dateien und Storage.
Ablauf eines Schreibvorgangs
Abschnitt betitelt „Ablauf eines Schreibvorgangs“sequenceDiagram
autonumber
participant C as Client
participant F as JwtSessionFilter (CIAS)
participant A as {Model}Api (REST)
participant E as Expander
participant S as {Model}System (System-Layer)
participant H as Hooks
participant Z as Autorisierung
participant P as {Model}Database
participant DB as Datenbank
C->>F: POST /api/rest/<pfad>/create + Bearer-Token
F->>F: Token prüfen, RequestContext füllen,<br/>Tenant-/User-Switch autorisieren
F->>A: Request weiterreichen
A->>A: Multipart/Base64 → Temp-Dateien
A->>E: Response Request expandieren
E-->>A: RequestSingle (fields, models, lists)
A->>S: createObject(DTO, request, files)
S->>Z: createAccessAllowedByClass()
S->>S: recursiveCreate: Felder setzen,<br/>Defaults, Validierung sammeln,<br/>Beziehungen rekursiv
S->>H: Before-Hooks ausführen
S->>S: assertValid() – alle Verstöße auf einmal
S->>P: createObject(entity)
P->>DB: INSERT
S->>H: After-Hooks ausführen
S->>P: flush()
S->>S: readObject(id, request) – Antwort zusammenstellen
S-->>A: DTO
A-->>C: SingleResponse
Wichtig an der Reihenfolge: Hooks laufen vor der Validierung. Ein Hook darf ein Feld setzen, das der Client gar nicht kennt, und damit eine Pflichtregel erfüllen. Anschließend werden alle gesammelten Verstöße erneut geprüft und nur das gemeldet, was der Hook nicht behoben hat.
Ablauf eines Lesevorgangs
Abschnitt betitelt „Ablauf eines Lesevorgangs“sequenceDiagram
autonumber
participant C as Client
participant A as {Model}Api
participant E as Expander
participant S as System-Layer
participant P as Persistenz
participant DB as Datenbank
C->>A: POST /read/{id} mit response-Definition
A->>E: expandResponse(payload, meta)
E->>E: '+' → skalare Felder, '*' → zusätzlich Referenzen (nur id),<br/>verschachtelte ModelResponse rekursiv
E-->>A: RequestSingle
A->>S: readObject(id, request)
S->>S: readAccessAllowedByClass()
S->>S: Sicherheitsfilter ergänzen (_userId, Attributfilter)
S->>P: queryObjects(request) – Tuple-Projektion
P->>DB: SELECT genau der angefragten Spalten
DB-->>P: Tuple
P-->>S: Entity (aus Tuple zusammengesetzt)
S->>S: pro Referenz: recursiveRead / queryObjects
S-->>A: DTO
A-->>C: SingleResponse
Es werden nie ganze Objektgraphen geladen. Was nicht im Response Request steht, wird nicht aus der Datenbank geholt.
Was wo entschieden wird
Abschnitt betitelt „Was wo entschieden wird“| Entscheidung | Ort |
|---|---|
| Darf der Benutzer diese Operation auf diesem Modell? | cdms-authorization über die Modell-Rollen |
| Darf der Benutzer diese Zeile? | System-Layer über Owner-Filter und Attributfilter |
| Welche Datenbank? | Persistenz, anhand der Entity-Basisklasse |
| Welche Felder kommen zurück? | REST (Expander) und Persistenz (Projektion) |
| Wann wird committet? | Request-Ende, siehe Transaktionen |
| Was passiert bei fehlendem Feld im Payload? | System-Layer, je nach Verb – siehe Schreibsemantik |
Der Schreibschutz vor dem Schreiben
Abschnitt betitelt „Der Schreibschutz vor dem Schreiben“Update, Patch, Delete und Rollback laden ihr Ziel über getFullEntity(id) –
ein find, das keinen Filter anwendet. Damit die Kenntnis einer ID nicht
genügt, um fremde Daten zu ändern, prüft der System-Layer vorher
assertVisibleForWrite(id): dieselben Filter wie beim Lesen, per
Count-Abfrage. Ist die Zeile unsichtbar, kommt 404 – nicht 403, denn
„existiert, gehört dir aber nicht” würde die Existenz fremder Daten verraten.