CodamAIDocs
Themafertig

CRMS in den Betriebsarten

Wo CRMS neben CDMS und CIAS sitzt: eingebettet in die CDMS-Anwendung oder als eigener Dienst. Die Kombinationen mit eingebettetem und getrenntem CIAS, wer wen wie aufruft, wie der Hub die Platzierung ableitet und warum beide Arten gleich antworten.

Ausprägungen
CRMS eingebettet, CIAS eingebettetCRMS eingebettet, CIAS getrenntCRMS als eigener DienstMethodenaufruf über ReportRenderPortHTTP mit dem Token der PersonPlatzierung aus dem Hub

Worum es geht

CRMS erzeugt Berichte aus Vorlagen und den Daten einer Anwendung. Es kennt dieselben zwei Bauformen wie CIAS:

  • eingebettet (local): CRMS läuft im Prozess einer CDMS-Anwendung. Die Anwendung ruft CRMS über Java-Methoden auf.
  • als eigener Dienst (remote): CRMS läuft in einem anderen Prozess. Die Anwendung ruft es über HTTP auf, mit dem kleinen Baustein crms-client.

CRMS steht dabei immer neben CIAS, nie an seiner Stelle. Es verwaltet keine Personen, keine Mandanten und keine Rollen. Es fragt CIAS, wer gerade anfragt und für welchen Mandanten.

Die Bauform von CRMS ist eine eigene Entscheidung, unabhängig von der Bauform von CIAS. Was die beiden Arten im Einzelnen mitbringen, steht im CRMS-Modul unter Eingebettet oder als eigener Dienst.

Die Prozessbilder

Ein Prozess ist ein laufendes Programm. Die Bilder zeigen, was jeweils in einem Prozess liegt.

CRMS neben CDMS und CIAS

Wann: CRMS und CIAS laufen beide im Prozess der CDMS-Anwendung. So arbeitet das Hub-Backend.

flowchart LR
    F["Frontend mit BFF"] -- "Bearer-Token" --> P
    subgraph P["Ein Prozess"]
        direction TB
        FK["Filterkette"]
        C["CDMS"]
        R["CRMS"]
        CI["CIAS-Module"]
        FK --> C
        C -->|"Methodenaufruf<br/>ReportRenderPort"| R
        R -->|"Methodenaufruf<br/>Aufrufer, Mandanten"| CI
    end
    P --> DB[("System-DB + Mandanten-DBs<br/>CDMS-, CIAS- und CRMS-Tabellen")]
    R --> S3[("S3-Bucket<br/>endgültige Dokumente")]
    classDef client fill:#475569,stroke:#475569,color:#fff
    classDef cdms fill:#1976d2,stroke:#1976d2,color:#fff
    classDef cias fill:#8e24aa,stroke:#8e24aa,color:#fff
    classDef crms fill:#ffc107,stroke:#b38600,color:#1f1a00
    classDef db fill:#4d7c0f,stroke:#4d7c0f,color:#fff
    class F client
    class C cdms
    class FK,CI cias
    class R crms
    class DB,S3 db

Ergebnis: Kein Netz zwischen den dreien. Ist der Prozess oben, sind alle drei oben.

Wann: CRMS liegt in der CDMS-Anwendung, CIAS ist ein eigener Dienst.

flowchart LR
    F["Frontend mit BFF"] -- "Bearer-Token" --> P
    subgraph P["Dienst 1: CDMS-Anwendung"]
        direction TB
        FK["Filterkette"]
        TC["Mandanten-Client"]
        C["CDMS"]
        R["CRMS"]
        FK --> C
        C -->|"Methodenaufruf<br/>ReportRenderPort"| R
        R -->|"Aufrufer"| FK
        R -->|"Mandantenliste"| TC
    end
    TC -- "HTTP + Dienst-Token" --> CI["Dienst 2: CIAS"]
    P --> DB[("System-DB + Mandanten-DBs<br/>CDMS- und CRMS-Tabellen")]
    classDef client fill:#475569,stroke:#475569,color:#fff
    classDef cdms fill:#1976d2,stroke:#1976d2,color:#fff
    classDef cias fill:#8e24aa,stroke:#8e24aa,color:#fff
    classDef crms fill:#ffc107,stroke:#b38600,color:#1f1a00
    classDef db fill:#4d7c0f,stroke:#4d7c0f,color:#fff
    class F client
    class C cdms
    class FK,TC,CI cias
    class R crms
    class DB db

Ergebnis: CRMS ruft CDMS weiter als Methode auf. Nur was CRMS von CIAS braucht und nicht in der Filterkette liegt, geht über HTTP.

Wann: Das CRMS-Modul liegt in einer anderen Laufzeit. Die CDMS-Anwendung ist nur Client.

flowchart LR
    F["Frontend mit BFF"] -- "Bearer-Token" --> P
    subgraph P["Dienst 1: CDMS-Anwendung"]
        direction TB
        FK["Filterkette"]
        C["CDMS"]
        CC["crms-client"]
        FK --> C
        C -->|"ReportRenderPort"| CC
    end
    subgraph Q["Dienst 2: CRMS"]
        direction TB
        FK2["Filterkette<br/>Token-Tausch"]
        R["CRMS"]
        FK2 --> R
    end
    CC -- "HTTP + Token der Person" --> FK2
    Q -- "HTTP + Dienst-Token<br/>Mandant? Mandantenliste?" --> CI["CIAS"]
    P --> DB1[("Datenbanken der Anwendung")]
    Q --> DB2[("Datenbank des CRMS-Dienstes")]
    classDef client fill:#475569,stroke:#475569,color:#fff
    classDef cdms fill:#1976d2,stroke:#1976d2,color:#fff
    classDef cias fill:#8e24aa,stroke:#8e24aa,color:#fff
    classDef crms fill:#ffc107,stroke:#b38600,color:#1f1a00
    classDef db fill:#4d7c0f,stroke:#4d7c0f,color:#fff
    class F client
    class C cdms
    class FK,FK2,CI cias
    class CC,R crms
    class DB1,DB2 db

Ergebnis: Die Anwendung trägt von CRMS nur crms-client. Der CRMS-Dienst prüft das Token selbst und fragt CIAS über HTTP.

Im dritten Bild ist es für die CDMS-Anwendung gleich, ob CIAS bei ihr eingebettet ist oder nicht. Sie spricht mit CRMS nur über crms-client. Der eigenständige CRMS-Dienst fragt CIAS immer über HTTP, wie ein CDMS-Dienst mit getrenntem CIAS. „Dienst 2“ kann auch eine andere CDMS-Anwendung des Projekts sein, in die CRMS eingebettet ist. Dann gilt für sie das erste oder zweite Bild.

Die Kombinationen

Aus Sicht einer CDMS-Anwendung gibt es drei Fälle. Jeder Fall entsteht im Hub, nicht durch eine Zeile, die du von Hand schreibst.

CRMS und CIAS, von einer CDMS-Anwendung aus gesehen
Alles eingebettetCRMS eingebettet, CIAS getrenntCRMS als eigener Dienst
CRMS-Platzierunglocallocalremote
CIAS-EinbindungEMBEDDEDREMOTEEMBEDDED oder REMOTE, für CRMS ohne Bedeutung
Von CRMS im ProzessStarter, die Dokumentablage, die Formatmodule und die HTTP-Schnittstelle von CRMSwie linksStarter und crms-client, sonst nichts
Woher CRMS den Aufrufer kenntaus der Filterkette im selben Prozessaus der Filterkette im selben Prozessim CRMS-Dienst, aus dessen eigener Filterkette
Woher CRMS die Mandantenliste kenntvon der Mandantenverwaltung von CIAS im selben Prozess, Methodenaufrufvom CIAS-Dienst über HTTP, mit dem Dienst-Token der Anwendungim CRMS-Dienst, vom CIAS-Dienst über HTTP
Wo die CRMS-Tabellen liegenin den Datenbanken der Anwendungin den Datenbanken der Anwendungin der Datenbank des CRMS-Dienstes

Eine Zeile fehlt absichtlich: CRMS ohne CIAS gibt es nicht. CRMS braucht CIAS, um den Aufrufer zu kennen und den Mandanten zu prüfen. Ein System, das nicht über CIAS anmeldet, bekommt kein eingebettetes CRMS.

Wer ruft wen wie

Die Aufrufe rund um CRMS
Wer fragtWenWasWie, und mit welchem Token
CDMS-CodeCRMS eingebettetBericht rendern, Dokument lesenMethodenaufruf an ReportRenderPort oder DocumentArchivePort, kein Token
CDMS-CodeCRMS-DienstBericht rendern, Dokument lesendieselben Ports, crms-client schickt HTTP mit dem Token der Person der laufenden Anfrage
CRMSCIASWer ruft gerade auf?Methodenaufruf an die Filterkette im eigenen Prozess, in jeder Bauform
CRMSCIASWelche Mandanten gibt es? (Zeitgeber)eingebettet ein Methodenaufruf, getrennt HTTP mit einem Dienst-Token
FrontendCRMSVorlagen bearbeiten, Dokumente lesenHTTP an /crms/**, mit dem Token der Person: bei der Anwendung, wenn CRMS eingebettet ist, sonst beim CRMS-Dienst
CIASCRMSWelche Rollen meldest du an?eingebettet ein Methodenaufruf an die Bean crmsIdentityRegistry. Ein eigener CRMS-Dienst meldet nichts an

Ein paar Wörter dazu:

  • Ein Port ist eine Java-Schnittstelle, über die deine Anwendung CRMS aufruft. Welche es gibt, steht unter Der Client für den eigenen Dienst.
  • Die Filterkette ist der Teil von CIAS, der bei jeder Anfrage das Token prüft und den Aufrufer festlegt. Siehe Die Beteiligten einer Anfrage.
  • Die Rollen von CRMS sind Namen in seiner Konfiguration (templateRoleUser, templateRoleTenant, documentRoleRead …). Siehe Die Rollen und CRMS beim Start.

So ruft eine CDMS-Anwendung CRMS auf

Du lässt dir den Port von Spring geben, zum Beispiel in einem eigenen Controller oder in einem Hook, und rufst ihn mit den Daten auf. Das Hub-Backend macht es so für seine Projektdokumente:

@RestController
@RequestMapping(path = "api/rest/cms/report")
public class ReportApi {

  private final ReportRenderPort renderer;   // embedded CRMS or crms-client

  public ReportApi(ReportRenderPort renderer) {
    this.renderer = renderer;
  }
  // ... builds a RenderRequest from the payload and calls renderer.render(request)
}

Derselbe Code läuft in beiden Bauformen. Welche Implementierung hinter ReportRenderPort steckt, entscheidet der Klassenpfad: eingebettet das CRMS selbst, sonst crms-client. Wie eine Render-Anfrage aussieht, steht unter Die Render-Anfrage.

Ein Bericht von vorn bis hinten

„Bericht erzeugen“ in beiden Bauformen

Wann: CRMS läuft in der CDMS-Anwendung.

sequenceDiagram
    participant B as BFF
    participant F as Filterkette (CIAS)
    participant C as CDMS (Controller oder Hook)
    participant R as CRMS
    participant DB as Mandanten-DB
    B->>F: POST /api/rest/… + Token (Person aus nordbau)
    F->>F: Token prüfen, tauschen, Mandant nordbau zulassen
    F->>C: RequestContext gefüllt
    C->>R: render(request)
    Note over C,R: Methodenaufruf, kein Netz
    R->>F: Wer ruft auf? Person, nordbau, Rollen
    R->>DB: Vorlage auflösen, bei FINAL ablegen
    R-->>C: RenderedDocument
    C-->>B: Dokument

Ergebnis: Eine Prüfung des Tokens, ein Prozess.

Wann: Die CDMS-Anwendung ruft einen CRMS-Dienst über crms-client.

sequenceDiagram
    participant B as BFF
    participant F as Filterkette (in CDMS)
    participant C as CDMS (Controller oder Hook)
    participant K as crms-client
    participant S as CRMS-Dienst
    participant I as CIAS
    B->>F: POST /api/rest/… + Token (Person aus nordbau)
    F->>F: Token prüfen, tauschen, Mandant nordbau zulassen
    F->>C: RequestContext gefüllt
    C->>K: render(request)
    K->>S: POST /crms/render + dasselbe Token
    S->>S: Token prüfen, gegen eigenen Client tauschen
    S->>I: wird nordbau bedient? (Dienst-Token)
    I-->>S: ja
    S->>S: rendern wie eingebettet
    S-->>K: Dokument
    K-->>C: dasselbe RenderedDocument
    C-->>B: Dokument

Ergebnis: Das Token wird zweimal geprüft: einmal von der Anwendung, einmal vom CRMS-Dienst. Jede Revision und jedes Dokument nennt trotzdem die Person, nicht einen Dienst.

Warum hier das Token der Person reist und bei den Lookups von CIAS ein Dienst-Token? Weil CRMS im Namen der Person handelt: Es legt Dokumente in ihrem Mandanten ab, prüft ihre Rollen und schreibt ihren Namen in die Kette. Ein Dienst-Token würde das verwischen. Die Lookups von CIAS fragen dagegen nach einem Sachverhalt, den der Dienst selbst wissen will. Siehe CIAS als eigener Service und Token-Tausch.

Der Hub legt die Platzierung fest

Ein Projekt hat höchstens ein CRMS-Modul. Wo du es im Hub hinlegst, also in welche Laufzeit (das Programm, das später läuft), bestimmt die Platzierung jeder CDMS-Anwendung des Projekts. Ein Feld für local oder remote gibt es nicht.

Was der Build für eine CDMS-Anwendung ableitet
CRMS-Modul im Projekt?CIAS-Einbindung der AnwendungWo liegt das CRMS-Modul?Ergebnis
nein––kein CRMS
jaNONEin einer anderen Laufzeitkein CRMS in dieser Anwendung
jaNONEin ihrer eigenen LaufzeitBuild abgelehnt: CRMS braucht CIAS
jaEMBEDDED oder REMOTEin keiner LaufzeitBuild abgelehnt: das CRMS-Modul muss in eine Laufzeit
jaEMBEDDED oder REMOTEin ihrer eigenen Laufzeitlocal: CRMS eingebettet
jaEMBEDDED oder REMOTEin einer anderen Laufzeitremote: Client des CRMS dort

Die Ableitung läuft genauso wie bei CIAS, siehe Was läuft, bestimmt die Konfiguration. Beide Entscheidungen fallen getrennt. Darum sind alle Kombinationen oben möglich, und zwei CDMS-Anwendungen desselben Projekts können CRMS verschieden nutzen: Die eine bettet es ein, die andere ruft es dort auf. Liegt das CRMS-Modul in einer Laufzeit ohne CDMS-Anwendung, wird daraus ein eigener CRMS-Dienst.

Der Build schreibt codamai.crms.placement in die application.yaml und setzt die passenden Jars in die POM. Beim Start prüft CRMS beides gegeneinander, siehe CRMS beim Start.

Gleich und nicht gleich

Für CRMS gilt dieselbe Regel wie für CIAS: Die Bauform ist eine Entscheidung über Betrieb, nicht darüber, was herauskommt. Ein Integrationstest schickt dieselben Anfragen an ein eingebettetes und an ein entferntes CRMS und verlangt dieselben Antworten.

Eingebettetes und entferntes CRMS
Gleich
was entschieden wird
  • dieselben Ports und dieselben Objekte, etwa RenderedDocument
  • dasselbe Dokument aus derselben Vorlagenrevision
  • dieselben Ablehnungen mit denselben Message Keys
  • dieselben Rollenprüfungen, dieselbe Person an jeder Revision
Nicht gleich
der Betrieb
  • Ausfall: nur entfernt gibt es crms-unavailable
  • Zeitgeber: laufen dort, wo CRMS eingebettet ist, also in der Anwendung oder im CRMS-Dienst
  • Datenbank: die der Anwendung oder eine eigene
  • Client-Rollen: die am Client der Anwendung oder die am Client des CRMS-Dienstes

Mehr zum entfernten Weg steht unter Der Client für den eigenen Dienst.

Fallen

Weiter

Suchen