CodamAIDocs
Topicdone

CRMS in the modes of operation

Where CRMS sits next to CDMS and CIAS: embedded in the CDMS application or as a service of its own. The combinations with embedded and standalone CIAS, who calls whom and how, how the hub derives the placement, and why both forms answer alike.

Variants
CRMS embedded, CIAS embeddedCRMS embedded, CIAS standaloneCRMS as a service of its ownmethod call through ReportRenderPortHTTP with the person's tokenplacement from the hub

What this is about

CRMS produces reports from templates and the data of an application. It knows the same two forms as CIAS:

  • embedded (local): CRMS runs in the process of a CDMS application. The application calls CRMS through Java methods.
  • as a service of its own (remote): CRMS runs in another process. The application calls it over HTTP, with the small building block crms-client.

CRMS always stands next to CIAS, never in its place. It manages no persons, no tenants and no roles. It asks CIAS who is calling right now and for which tenant.

The form of CRMS is a decision of its own, independent of the form of CIAS. What each form brings along in detail is in the CRMS module under Embedded or as a service of its own.

The process pictures

A process is a running program. The pictures show what sits in one process in each case.

CRMS next to CDMS and CIAS

When: CRMS and CIAS both run in the process of the CDMS application. This is how the hub backend works.

flowchart LR
    F["Frontend with BFF"] -- "Bearer token" --> P
    subgraph P["One process"]
        direction TB
        FK["Filter chain"]
        C["CDMS"]
        R["CRMS"]
        CI["CIAS modules"]
        FK --> C
        C -->|"method call<br/>ReportRenderPort"| R
        R -->|"method call<br/>caller, tenants"| CI
    end
    P --> DB[("System DB + tenant DBs<br/>CDMS, CIAS and CRMS tables")]
    R --> S3[("S3 bucket<br/>final documents")]
    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

Result: No network between the three. When the process is up, all three are up.

When: CRMS sits in the CDMS application, CIAS is a service of its own.

flowchart LR
    F["Frontend with BFF"] -- "Bearer token" --> P
    subgraph P["Service 1: CDMS application"]
        direction TB
        FK["Filter chain"]
        TC["Tenant client"]
        C["CDMS"]
        R["CRMS"]
        FK --> C
        C -->|"method call<br/>ReportRenderPort"| R
        R -->|"caller"| FK
        R -->|"tenant list"| TC
    end
    TC -- "HTTP + service token" --> CI["Service 2: CIAS"]
    P --> DB[("System DB + tenant DBs<br/>CDMS and CRMS tables")]
    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

Result: CDMS still calls CRMS as a method. Only what CRMS needs from CIAS and does not find in the filter chain goes over HTTP.

When: The CRMS module sits in another runtime. The CDMS application is only a client.

flowchart LR
    F["Frontend with BFF"] -- "Bearer token" --> P
    subgraph P["Service 1: CDMS application"]
        direction TB
        FK["Filter chain"]
        C["CDMS"]
        CC["crms-client"]
        FK --> C
        C -->|"ReportRenderPort"| CC
    end
    subgraph Q["Service 2: CRMS"]
        direction TB
        FK2["Filter chain<br/>token exchange"]
        R["CRMS"]
        FK2 --> R
    end
    CC -- "HTTP + the person's token" --> FK2
    Q -- "HTTP + service token<br/>tenant? tenant list?" --> CI["CIAS"]
    P --> DB1[("Databases of the application")]
    Q --> DB2[("Database of the CRMS service")]
    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

Result: Of CRMS, the application carries only crms-client. The CRMS service checks the token itself and asks CIAS over HTTP.

In the third picture it makes no difference to the CDMS application whether CIAS is embedded in it or not. It talks to CRMS only through crms-client. The standalone CRMS service always asks CIAS over HTTP, like a CDMS service with standalone CIAS. “Service 2” can also be another CDMS application of the project in which CRMS is embedded. Then the first or second picture applies to it.

The combinations

From the point of view of one CDMS application there are three cases. Each case comes from the hub, not from a line you write by hand.

CRMS and CIAS, seen from one CDMS application
Everything embeddedCRMS embedded, CIAS standaloneCRMS as a service of its own
CRMS placementlocallocalremote
CIAS integrationEMBEDDEDREMOTEEMBEDDED or REMOTE, irrelevant for CRMS
Of CRMS in the processstarter, the CRMS document archive, the format modules and the CRMS HTTP interfaceas on the leftstarter and crms-client, nothing else
Where CRMS learns the callerfrom the filter chain in the same processfrom the filter chain in the same processin the CRMS service, from its own filter chain
Where CRMS learns the tenant listfrom CIAS tenant management in the same process, method callfrom the CIAS service over HTTP, with the application's service tokenin the CRMS service, from the CIAS service over HTTP
Where the CRMS tables livein the databases of the applicationin the databases of the applicationin the database of the CRMS service

One row is missing on purpose: there is no CRMS without CIAS. CRMS needs CIAS to know the caller and to check the tenant. A system that does not sign in through CIAS gets no embedded CRMS.

Who calls whom and how

The calls around CRMS
Who asksWhomWhatHow, and with which token
CDMS codeembedded CRMSrender a report, read a documentmethod call to ReportRenderPort or DocumentArchivePort, no token
CDMS codeCRMS servicerender a report, read a documentthe same ports; crms-client sends HTTP with the person's token of the running request
CRMSCIASWho is calling right now?method call to the filter chain in its own process, in every form
CRMSCIASWhich tenants are there? (timers)embedded a method call, standalone HTTP with a service token
FrontendCRMSedit templates, read documentsHTTP to /crms/**, with the person's token: at the application when CRMS is embedded, otherwise at the CRMS service
CIASCRMSWhich roles do you register?embedded a method call to the bean crmsIdentityRegistry. A CRMS service of its own registers nothing

A few words on this:

  • A port is a Java interface through which your application calls CRMS. Which ones exist is under The client for a separate service.
  • The filter chain is the part of CIAS that checks the token on every request and settles who the caller is. See The parties in a request.
  • The roles of CRMS are names in its configuration (templateRoleUser, templateRoleTenant, documentRoleRead …). See The roles and CRMS at start-up.

How a CDMS application calls CRMS

You let Spring hand you the port, for example in a controller of your own or in a hook, and call it with the data. The hub backend does this for its project documents:

@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)
}

The same code runs in both forms. Which implementation sits behind ReportRenderPort is decided by the classpath: embedded, CRMS itself, otherwise crms-client. What a render request looks like is under The render request.

A report from front to back

“Create report” in both forms

When: CRMS runs in the CDMS application.

sequenceDiagram
    participant B as BFF
    participant F as Filter chain (CIAS)
    participant C as CDMS (controller or hook)
    participant R as CRMS
    participant DB as Tenant DB
    B->>F: POST /api/rest/… + token (person from nordbau)
    F->>F: check token, exchange, admit tenant nordbau
    F->>C: RequestContext filled
    C->>R: render(request)
    Note over C,R: method call, no network
    R->>F: Who is calling? person, nordbau, roles
    R->>DB: resolve template, store if FINAL
    R-->>C: RenderedDocument
    C-->>B: document

Result: One token check, one process.

When: The CDMS application calls a CRMS service through crms-client.

sequenceDiagram
    participant B as BFF
    participant F as Filter chain (in CDMS)
    participant C as CDMS (controller or hook)
    participant K as crms-client
    participant S as CRMS service
    participant I as CIAS
    B->>F: POST /api/rest/… + token (person from nordbau)
    F->>F: check token, exchange, admit tenant nordbau
    F->>C: RequestContext filled
    C->>K: render(request)
    K->>S: POST /crms/render + the same token
    S->>S: check token, exchange it for its own client
    S->>I: is nordbau served? (service token)
    I-->>S: yes
    S->>S: render as embedded
    S-->>K: document
    K-->>C: the same RenderedDocument
    C-->>B: document

Result: The token is checked twice: once by the application, once by the CRMS service. Every revision and every document still names the person, not a service.

Why does the person’s token travel here, while the CIAS lookups use a service token? Because CRMS acts on behalf of the person: it stores documents in their tenant, checks their roles and writes their name into the chain. A service token would blur that. The CIAS lookups, on the other hand, ask about a fact the service itself wants to know. See CIAS as a separate service and Token exchange.

The hub settles the placement

A project has at most one CRMS module. Where you put it in the hub, that is, in which runtime (the program that runs later), settles the placement of every CDMS application of the project. There is no field for local or remote.

What the build derives for a CDMS application
CRMS module in the project?CIAS integration of the applicationWhere is the CRMS module?Result
no––no CRMS
yesNONEin another runtimeno CRMS in this application
yesNONEin its own runtimebuild rejected: CRMS needs CIAS
yesEMBEDDED or REMOTEin no runtimebuild rejected: the CRMS module has to be in a runtime
yesEMBEDDED or REMOTEin its own runtimelocal: CRMS embedded
yesEMBEDDED or REMOTEin another runtimeremote: client of the CRMS there

The derivation works just like the one for CIAS, see Configuration decides what runs. The two decisions are made separately. That is why all combinations above are possible, and two CDMS applications of the same project can use CRMS differently: one embeds it, the other calls it there. If the CRMS module sits in a runtime without a CDMS application, it becomes a CRMS service of its own.

The build writes codamai.crms.placement into the application.yaml and puts the matching jars into the POM. At start, CRMS checks the two against each other, see CRMS at start-up.

Same and not the same

For CRMS the same rule applies as for CIAS: the form is a decision about operation, not about what comes out. An integration test sends the same requests to an embedded and to a remote CRMS and demands the same answers.

Embedded and remote CRMS
Same
what is decided
  • the same ports and the same objects, such as RenderedDocument
  • the same document from the same template revision
  • the same rejections with the same message keys
  • the same role checks, the same person on every revision
Not the same
operation
  • Outage: only remote is there crms-unavailable
  • Timers: run where CRMS is embedded, so in the application or in the CRMS service
  • Database: the application's or one of its own
  • Client roles: those on the application's client or those on the CRMS service's client

More on the remote path is under The client for a separate service.

Watch out

Next

Search