Worum es geht
Ben arbeitet für den Kunden nordbau. Er hat ein Audit abgeschlossen und klickt auf „Prüfbericht erzeugen“. Ein paar Sekunden später hat er ein PDF. Dazwischen arbeiten alle drei Module zusammen, und jedes tut genau eine Sache:
- CDMS hält die Daten des Audits.
- CIAS sagt, wer anfragt, für welchen Mandanten und mit welchen Rollen.
- CRMS macht aus einer Vorlage und Daten ein Dokument und bewahrt es auf, wenn es ein Nachweis ist.
Diese Seite ist eine Abzweigung von Vom Login bis zu den Daten. Anmeldung, BFF und Filterkette laufen genau so wie dort. Hier geht es um das, was danach kommt.
Der ganze Weg
So sieht der Weg aus, wenn CRMS eingebettet in der Anwendung läuft und Ben einen endgültigen Bericht verlangt:
sequenceDiagram
autonumber
participant U as Benutzer
participant F as BFF
participant FK as Filterkette (CIAS)
participant A as eigener Code der Anwendung
participant C as CDMS
participant R as CRMS
participant DB as Mandanten-DB
participant S as Dateispeicher
U->>F: klickt auf „Prüfbericht erzeugen“
F->>FK: POST /api/rest/audit/report mit Bearer-Token
FK->>FK: Token prüfen, Mandant nordbau, Tor, Rollen
FK->>A: RequestContext gefüllt
A->>C: liest Audit, Fragen, Antworten
C->>DB: SELECT (Datenbank von nordbau)
DB-->>C: Zeilen
C-->>A: Objekte
A->>A: baut die Payload, höchstens zwei Ebenen
A->>R: ReportRenderPort.render(FINAL, audit-report, audit-4711, Payload)
R->>FK: wer ruft? (aus dem RequestContext)
FK-->>R: Ben, nordbau, Rollen
R->>DB: sucht die Vorlage (Ben, nordbau, System)
R->>R: prüft die Payload
R->>R: rendert im Pool, siegelt das PDF
R->>S: legt die verschlüsselten Bytes gesperrt ab
R->>DB: hängt die Revision an die Kette, Journal
R-->>A: RenderedDocument mit Revision
A-->>F: PDF
F-->>U: Download
Die folgenden Etappen gehen den Weg langsam durch.
Etappe 1: Bis zur Anwendung
Der Browser ruft wie immer eine Route seines BFF auf. Der BFF schickt die Anfrage mit Bens Access-Token an das Backend. Dort läuft sie zuerst durch die Filterkette von CIAS: Token prüfen, Mandant bestimmen, Mandanten-Tor fragen, effektive Rollen bilden. Am Ende steht der RequestContext, der Merkzettel für genau diese Anfrage.
Das ist derselbe Weg wie bei jeder anderen Anfrage, Station für Station unter Vom Login bis zu den Daten. Wird nordbau nicht bedient, endet der Bericht hier mit 403, bevor eine Zeile Code der Anwendung läuft.
Etappe 2: Die Anwendung sammelt die Daten
Eine Route für „Prüfbericht erzeugen“ erzeugt der Generator nicht. Das ist eigener Code der Anwendung: ein eigener Controller, wie die Berichte des Hubs ihn haben, oder ein Hook. Beides sind Spring-Beans, und beide lassen sich den Port von CRMS geben.
-
1Anwendung→CDMSliest das Audit mit allem, was der Bericht zeigt: Listen, Gruppen, Fragen, AntwortenDas läuft im RequestContext von Bens Anfrage. CDMS wählt darum die Datenbank von
nordbau. -
2Anwendungbaut daraus die Payload: höchstens zwei Ebenen, an den Enden nur Text, Zahl, Wahrheitswert oder DatumSummen, Prozente und Bewertungen rechnet die Anwendung vorher aus. CRMS rechnet nicht und lädt nichts nach.
-
3Anwendung→CRMSruft
ReportRenderPort.render(…)auf, mit Vorlagenschlüssel, Format, Modus und beiFINALeiner ReferenzErgebnis: Ab hier ist CRMS dran. Die Anwendung wartet im selben Aufruf auf das Dokument.
So sieht der Aufruf aus:
RenderedDocument document = renderer.render(RenderRequest.finalDocument(
new TemplateKey("audit-report"), // template key
OutputFormat.PDF,
new DocumentReference("audit-4711"), // the chain: the audit's ID
payload)); // at most two levels
Die Referenz ist die Kennung des Fachobjekts in deiner Anwendung, hier die ID des Audits. CRMS liest nicht hinein, es legt alle endgültigen Fassungen desselben Berichts darunter ab. Wie die Payload aussehen darf, steht unter Die Payload und ihre Prüfung, alle Felder der Anfrage unter Die Render-Anfrage.
Etappe 3: CRMS fragt CIAS, wer anfragt
CRMS liest kein Token. Es fragt CIAS nach dem Aufrufer, und CIAS antwortet aus dem RequestContext, den die Filterkette schon gefüllt hat:
| Was CRMS bekommt | Woher | Wofür CRMS es braucht |
|---|---|---|
| angemeldet ja oder nein | RequestContext | ohne Person kein Bericht: crms-authentication-required |
| die Person | die Benutzer-ID aus dem Token | steht an jedem endgültigen Dokument, findet Bens persönliche Vorlagen |
| der Mandant | der Mandant der Anfrage, hier nordbau | welche Mandantenvorlagen gelten, wem das Dokument gehört |
| die Rollen | Realm-Rollen und Rollen des Clients | Vorlagen schreiben, freigeben, Dokumente lesen |
Rendern braucht keine Rolle. Jede angemeldete Person darf rendern, mit jeder Vorlage, die sie sieht. Die Rollen spielen erst beim Bearbeiten von Vorlagen und beim Lesen gespeicherter Dokumente eine Rolle, siehe Die Rollen.
Etappe 4: Die Vorlage finden
Die Anfrage nennt nur den Schlüssel audit-report. Welche Ausgabe und welche Revision rendert, entscheidet CRMS. Eine Ausgabe ist die Fassung einer Vorlage für einen Scope, also für das System, einen Mandanten oder eine Person. Die genaueste gewinnt:
-
1CRMS→DatenbankHat Ben eine eigene Ausgabe (Scope
USER)? Gesucht wird in der Datenbank vonnordbau -
2CRMS→DatenbankHat
nordbaueine Ausgabe (ScopeTENANT)? Ebenfalls in der Datenbank vonnordbau -
3CRMS→DatenbankGibt es eine Systemvorlage (Scope
SYSTEM)? Gesucht wird in der System-Datenbank -
4CRMSnimmt die erste Ausgabe, die in einer passenden Sprache eine veröffentlichte Revision hatErgebnis: Gibt es keine:
template-not-found. Eine Vorlage eines anderen Mandanten findet Ben nie.
Eingebettet liegen die Tabellen von CRMS neben denen der Anwendung, in denselben Datenbanken. Welche Sprache gilt und wie CRMS zurückfällt, steht unter Wie CRMS die Vorlage findet und Sprache und Zeitzone.
Etappe 5: Prüfen, rendern, antworten
-
CRMSFormatBietet die Installation
PDFan, und ist das Formatmodul da?↳ neinformat-not-offered|pdf -
CRMSModusSpeichert die Installation endgültige Dokumente?↳ nein
document-revisions-disabled -
CIASAufruferGibt es eine angemeldete Person?↳ nein
crms-authentication-required -
CRMSVorlageFindet Ben eine veröffentlichte Revision von
audit-report?↳ neintemplate-not-found -
CRMSAusgabetypPasst
PDFzum Ausgabetyp der Vorlage?↳ neinformat-not-for-template|pdf|<ausgabetyp> -
CRMSPayloadHält die Payload die Strukturregeln und die Felder der Vorlage ein?↳ nein
payload-rejectedmit allen Verstößen -
CRMSRendernLäuft die Vorlage im Pool durch, innerhalb von Zeit- und Größengrenze?↳ nein
render-timeout,render-output-too-large,render-capacity-exhausted… - Das Dokument ist fertig. Bei FINAL wird es jetzt abgelegt
Bis zur Payload läuft alles auf dem Thread der Anfrage, denn nur dort gibt es den RequestContext und damit die richtige Datenbank. Das eigentliche Rendern läuft in einem begrenzten Pool, siehe Ausführung und Grenzen.
Bei FINAL kommt danach der Schritt, der den Bericht zum Nachweis macht:
-
1CRMSsiegelt das PDF, wenn die Installation siegelt
-
2CRMSerzeugt einen Schlüssel nur für dieses Dokument und verschlüsselt die Bytes damit
-
3CRMS→Dateispeicherlegt die Bytes auf WORM-Speicher ab, gesperrt bis zum Ende der AufbewahrungWORM heißt „write once, read many“: Ein Objekt lässt sich bis zu seinem Datum weder ändern noch löschen.
-
4CRMS→Datenbankhängt die Revision an die Kette von
audit-report+audit-4711, mit Bens Kennung, Zeit, Hashes undretainUntil, und schreibtdocument-storedins JournalDas Dokument gehört dem Mandanten der Anfrage und liegt in dessen Datenbank. Ohne Mandant liegt es in der System-Datenbank. -
5CRMS→Anwendunggibt die Bytes zurück, zusammen mit der neuen RevisionErgebnis: Die Bytes kommen erst, wenn sie abgelegt sind. Ein endgültiges Dokument, das Ben hat und CRMS nicht, gibt es nicht.
Was eine Revision genau festhält, steht unter Was ein endgültiges Dokument ist, wie die Kette Änderungen aufdeckt unter Die zwei Hash-Ketten.
Etappe 6: Zurück zu Ben
Die Anwendung gibt das Dokument an den BFF, der BFF an den Browser. Geht etwas schief, kommt aus CRMS eine CrmsException mit einem Schlüssel, nie ein Satz. Die Anwendung reicht den Schlüssel unverändert weiter und übersetzt ihn nicht. Das Frontend zeigt ihn in Bens Sprache an. Der Hub macht es so: Er wandelt jede CrmsException in seine eigene Fehlerform um, mit demselben Schlüssel und dem Status, den CRMS für diesen Schlüssel nennt. Die Schlüssel stehen unter Die Fehlerantwort.
Eingebettet oder als eigener Dienst
Wo CRMS läuft, leitet der Hub aus der Laufzeit des CRMS-Moduls ab, siehe Eingebettet oder als eigener Dienst. Dein Code ruft in beiden Fällen denselben Port auf und bekommt dieselben Objekte und Schlüssel zurück. Unterwegs passiert aber Verschiedenes.
Wann: Das CRMS-Modul liegt in der Laufzeit der Anwendung (codamai.crms.placement=local).
sequenceDiagram
autonumber
participant A as Anwendung
participant FK as Filterkette (CIAS)
participant R as CRMS (im Prozess)
participant DB as Mandanten-DB
A->>R: render(…) als Methodenaufruf
R->>FK: wer ruft?
FK-->>R: Ben, nordbau, Rollen (aus dem RequestContext)
R->>DB: Vorlage suchen, bei FINAL Kette schreiben
R-->>A: RenderedDocument
Ergebnis: Kein Netzwerk, kein zweites Token. Vorlagen und Dokumente liegen in den Datenbanken der Anwendung, ein FINAL wird in der Transaktion der Anfrage geschrieben.
Wann: Das CRMS-Modul liegt in einer anderen Laufzeit (codamai.crms.placement=remote).
sequenceDiagram
autonumber
participant A as Anwendung
participant CL as crms-client
participant FK as Filterkette im CRMS-Dienst
participant K as Keycloak
participant R as CRMS-Dienst
participant DB as DB des CRMS-Dienstes
A->>CL: render(…)
CL->>CL: Bens Token aus der laufenden Anfrage
CL->>FK: POST /crms/render, Authorization: Bearer (Bens Token)
FK->>K: Token tauschen für den Client des Dienstes
K-->>FK: Token mit den CRMS-Rollen
FK->>FK: Mandant aus dem Token, Tor, Rollen
FK->>R: RequestContext gefüllt
R->>DB: Vorlage suchen, bei FINAL Kette schreiben
R-->>CL: Bytes und Crms-*-Header
CL-->>A: dasselbe RenderedDocument
Ergebnis: Die Person reist mit: Der Aufruf trägt Bens Token, nie ein Dienst-Token. Der Dienst hat eine eigene Filterkette, ein eigenes Mandanten-Tor und eine eigene Datenbank.
Drei Folgen des eigenen Dienstes:
- Die Rollen kommen vom Client des Dienstes. Der Dienst tauscht Bens Token gegen eines für seinen eigenen Client. Eine Dokumentrolle muss Ben also an diesem Client haben, nicht am Client der Anwendung. Siehe Token-Tausch.
- Der Mandant kommt nur aus dem Token.
crms-clientreicht allein die KopfzeileAuthorizationweiter, keine Kopfzeilentenantoderuser. Der Dienst bestimmt den Mandanten so, als käme die Anfrage ohne diese Kopfzeilen. - Ohne laufende Anfrage gibt es kein Token. Ein zeitgesteuerter Job in der Anwendung bekommt
crms-authentication-required, und nichts wird gesendet.
Wie der Client Antworten in Exceptions übersetzt, steht unter Der Client für den eigenen Dienst.
Vorschau, endgültig, flüchtig
Der Weg bis zum Rendern ist für alle drei Modi derselbe. Was danach passiert, unterscheidet sich:
| PREVIEW | FINAL | TRANSIENT | |
|---|---|---|---|
| Wofür | Ben sieht sich den Bericht an, bevor er ihn abschließt | der Nachweis, etwa der abgeschlossene Prüfbericht | eine Arbeitskopie zum Weitergeben, etwa die Projektdokumente des Hubs |
| Referenz | nein | ja, Pflicht | nein |
| Vorlage aus | jedem Scope, den Ben sieht | jedem Scope, den Ben sieht | nur dem Scope SYSTEM |
| Gespeichert | nein | ja: verschlüsselt auf WORM-Speicher, Revision in der Kette mit Bens Kennung | nein |
| Im Dokument | sichtbares Wasserzeichen PREVIEW, Markierung crms-preview | nichts Sichtbares, PDF mit Siegel, wenn eingerichtet | kein Wasserzeichen, Markierung crms-transient in den Metadaten |
| Eingeschaltet | immer | solange die Installation Dokumentrevisionen speichert | nur wenn die Installation es einschaltet |
Ein typischer Ablauf nutzt zwei Modi: Ben klickt erst auf „Vorschau“ (PREVIEW), prüft das Ergebnis und schließt dann ab (FINAL). Beide Aufrufe gehen genau denselben Weg und rendern mit derselben Vorlage, nur der zweite legt etwas ab. Alle Regeln der Modi stehen unter Vorschau, endgültig, flüchtig.
Was in welchem Aufruf steckt
| Abschnitt | Was geht mit | Wer prüft es |
|---|---|---|
| Browser → BFF | Sitzungs-Cookie | der BFF |
| BFF → Anwendung | Bens Access-Token, dazu die ID des Audits | die Filterkette von CIAS |
| Anwendung → CDMS | nichts Neues: derselbe RequestContext | CDMS mit Rollen und Filtern |
| Anwendung → CRMS, eingebettet | RenderRequest als Java-Objekt | CRMS, mit dem Aufrufer aus CIAS |
| Anwendung → CRMS-Dienst | POST /crms/render mit Bens Token und der Anfrage als JSON | die Filterkette im CRMS-Dienst, dann CRMS |
| CRMS → Datenbank und Dateispeicher | keine Anmeldedaten der Person, sondern die Verbindungen der Installation | Datenbank und Speicher selbst |
Wo der Weg endet, wenn etwas fehlt
| Filterkette lässt durch | Vorlage gefunden | Payload gültig | Rendern gelingt | Was Ben bekommt |
|---|---|---|---|---|
| nein | – | – | – | 401 oder 403 aus der Filterkette, mit cias.authentication.…. CRMS wurde nie gefragt |
| ja | nein | – | – | template-not-found, als 404 |
| ja | ja | nein | – | payload-rejected mit violations, als 422. Die Anwendung hat falsche Daten gebaut |
| ja | ja | ja | nein | ein render-…-Schlüssel, etwa render-timeout als 504 |
| ja | ja | ja | ja | das Dokument, bei FINAL zusätzlich abgelegt |
Fallen
Wie es weitergeht
- Ein Prüfbericht von Anfang bis Ende: derselbe Bericht über Jahre, mit Prüfen, Legal Hold und Kündigung
- Vom Login bis zu den Daten: der Weg bis zur Anwendung
- Wenn CIAS nicht erreichbar ist: auch für Berichte
- Was CRMS ist und Die Render-Anfrage
- Beispiel: die Berichte des Hubs: ein eingebetteter Aufruf mit
TRANSIENT