What this is about
You do not start a new CDMS project with an empty folder. The hub gives you a project scaffold: a ZIP with a POM, a main class, configuration and the current model. You unpack it, build once, and the application runs.
What exactly is in the scaffold depends on a few settings of the system in the hub: Which database does it use? Does it need login? Does CIAS run in the same process? Each setting is a branch that adds dependencies, configuration and sometimes classes.
How you get the scaffold
-
1Developer→Hubopens the system in the hub and clicks Project scaffold
-
2Hubreads the settings of the system and the model
-
3Hub→Developerdelivers
<id>-project.zipBehind this isGET /api/rest/cms/system/{id}/scaffold. So you can also fetch the scaffold with a script. -
4Developerunpacks it, enters the client secret in
cdms-generator.properties, creates.env -
5Build
mvn clean compile: fetches the metadata, maintains the POM, generates the codeResult: A project that runs
What is in the ZIP
katalog/
├── pom.xml ← parent cdms-app-parent, dependencies per branch
├── README.md ← building, starting, all environment variables
├── cdms-generator.properties ← access to the hub for the build (secret empty)
├── .env.example ← runtime variables: database, tenant mode, CIAS …
├── .gitignore
├── .cache/ ← the model as YAML, so the first build works offline
│ ├── system.yaml
│ └── models.yaml …
└── src/
├── main/java/com/codamai/shop/katalog/
│ ├── Start.java ← Spring Boot main class
│ └── config/ ← only with CIAS EMBEDDED: three wiring classes
├── main/resources/application.yaml
└── test/java/…/TenantContextArchitectureTest.java ← only with login (OIDC)
| File | what for |
|---|---|
pom.xml | The parent is cdms-app-parent. It brings the whole build: fetching metadata, code generator, Spring Boot packaging, native image. Your POM only contains coordinates, a few properties and the dependencies. |
Start.java | @SpringBootApplication for the package com.codamai. Without it there would be no source file, and without a source file no annotation processors run, so no generator either. |
application.yaml | your configuration. Database, tenant mode and identity provider are not here on purpose. They come from the environment. |
.env.example | Template for the environment variables. Always included: CODAMAI_PERSISTENCE_TENANT_MODE=SINGLE and the database access settings CODAMAI_PERSISTENCE_DATABASE_*. |
cdms-generator.properties | System ID, hub address, Keycloak address, realm, client ID and cacheDir=.cache. You enter the clientSecret yourself. The file is listed in .gitignore. |
.cache/ | the model at the time of the download, see Code generation in the build |
The hub derives the Maven coordinates from the names. The project “Shop” and the system “Katalog” become groupId com.codamai.shop, artifactId katalog and the base package com.codamai.shop.katalog.
The branches
The settings are on the system in the hub, the placement of the CIAS module on the project. The build reads them later from .cache/system.yaml and .cache/cias.yaml. Maven or Spring properties do not switch the branches.
flowchart TD
A{"authentication"} -- "NONE" --> N["no login<br/>CIAS is always NONE"]
A -- "OIDC" --> O["+ CIAS filter chain<br/>+ architecture test<br/>+ CIAS_ISSUER, CIAS_CLIENT …"]
O --> C{"runtime of the<br/>CIAS module"}
C -- "another one" --> R["REMOTE<br/>+ CIAS tenant client<br/>CIAS runs elsewhere"]
C -- "the system's" --> E["EMBEDDED<br/>+ tenant, user and<br/>role management, Keycloak adapter<br/>+ block codamai.cias, cias-configuration.yaml<br/>+ 3 classes under config/"]
C -- "none / no module" --> X["build refused (400)"]
D{"database"} -- "MYSQL" --> D1["mysql-connector-j"]
D -- "MARIADB" --> D2["mariadb-java-client"]
S{"storage"} -- "FILESYSTEM" --> S1["+ file storage building block<br/>+ basePath /files/"]
S -- "NONE" --> S0["no files"]
M{"monitoring"} -- "ACTUATOR" --> M1["+ Prometheus, AOP<br/>+ metrics and probes on 8081"]
M -- "NONE" --> M0["basic Actuator setup only<br/>on 8081"]
U{"a model audited?"} -- "yes" --> U1["+ hibernate-envers"]
When: authentication: OIDC or NONE
OIDC: The POM gets cias-authentication (checks every token) and, for tests, cias-test-support. .env.example contains CIAS_ISSUER (the issuer as the token carries it), CIAS_CLIENT and CIAS_CLIENT_SECRET, plus CIAS_BACKCHANNEL_URL commented out. On top of that you get the test TenantContextArchitectureTest. It checks that the tenant only comes from the tenant gate. NONE: none of this. Without login there is no CIAS either.
Result: With OIDC every request is logged in, see Token check
When: authentication: OIDC, the project's CIAS module runs in the system's runtime
CIAS runs in the same process as CDMS. The POM gets cias-tenancy, cias-user, cias-authorization and cias-iam-keycloak. application.yaml gets a block codamai.cias (tenant management on, tenants are looked up locally). Under config/ there are three classes that wire up CIAS: CiasEmbeddedConfiguration, CiasIdentityConfiguration, CiasRepositoryTransactionsPostProcessor. On every build the generator also writes cias-configuration.yaml from the CIAS module. What is switched on there (users, roles, registration …) is on; CIAS_USER and CIAS_AUTHORIZATION override it per environment.
Result: A single unit, see Embedded
When: authentication: OIDC, the project's CIAS module runs in another runtime
CIAS runs as a separate service or embedded in another CDMS module of the project. The POM only gets cias-tenancy-client. It asks CIAS for the tenants. .env.example contains CODAMAI_CIAS_TENANCY_CLIENT_BASE_URL, the address of the CIAS service.
Result: Two services, see Standalone
When: authentication: OIDC, the CIAS module is placed in no runtime or the project has none
No scaffold is produced. The hub answers with 400, and the message names what to model: create a CIAS module, place it in a runtime, or set authentication to NONE.
When: database: MYSQL or MARIADB
The POM gets the matching JDBC driver. .env.example gets CODAMAI_PERSISTENCE_DATABASE_DRIVER and an example URL like jdbc:mysql://localhost:3306/{tenant}. At runtime CDMS replaces {tenant} with the tenant, see Which database?
When: storage: FILESYSTEM or NONE
FILESYSTEM: The POM gets cdms-localfs-storage, and application.yaml gets the storage location codamai.cdms.persistence.file.basePath (default /files/, can be changed with CODAMAI_CDMS_PERSISTENCE_FILE_BASE_PATH). NONE: no file storage.
Result: See Storage backends
When: monitoring: ACTUATOR or NONE
Every application has the Spring Boot Actuator, always on the separate management port 8081, which is not reachable from outside. ACTUATOR adds more: micrometer-registry-prometheus and spring-boot-starter-aop in the POM, and in application.yaml the endpoints health, info, metrics, prometheus and health probes for Kubernetes.
When: at least one model is audited
There is no switch for this. As soon as a model is audited in the hub, hibernate-envers is added to the POM.
Result: See What is audited
Whether the application serves one tenant or many is not a branch of the scaffold. CODAMAI_PERSISTENCE_TENANT_MODE (SINGLE or MULTI) decides this at runtime, see Single- or multi-tenant.
An example
<parent>
<groupId>com.codamai.cdms</groupId>
<artifactId>cdms-app-parent</artifactId>
</parent>
<properties>
<cdms.main.class>com.codamai.shop.katalog.Start</cdms.main.class>
<cdms.generator.fetch.skip>false</cdms.generator.fetch.skip>
</properties>
<dependencies>
<!-- cdms-managed:dependencies:start -->
… core: cdms-rest-api, cdms-system-layer, cdms-database …
hibernate-envers ← auditing
mysql-connector-j ← database: MYSQL
cdms-localfs-storage ← storage: FILESYSTEM
cias-authentication ← authentication: OIDC
micrometer-registry-prometheus ← monitoring: ACTUATOR
cias-tenancy-client ← CIAS module in another runtime
<!-- cdms-managed:dependencies:end -->
</dependencies>spring:
application: { name: katalog }
threads: { virtual: { enabled: true } }
codamai:
cdms:
api: { createReadMode: STRICT }
persistence: ← storage: FILESYSTEM
file:
basePath: ${CODAMAI_CDMS_PERSISTENCE_FILE_BASE_PATH:/files/}
management:
server: { port: 8081 }
endpoints: { web: { exposure: { include: health, info, metrics, prometheus } } } ← monitoring: ACTUATORCreated once, then yours
What happens to the files when you build later or download the scaffold again?
| File | What happens on every build |
|---|---|
pom.xml: parent, managed properties, section between the cdms-managed markers | is rewritten from the hub |
pom.xml: coordinates, your own dependencies, plugins, your own properties | stays as it is |
Start.java, OpenApiConfiguration, NativeHintsConfiguration | the generator creates them if they are missing, otherwise never touched |
application.yaml, .env.example, CIAS classes under config/ | never touched |
generated code in target/generated-sources/ | new on every build |
Two consequences:
- Changing a branch later (for example
storagetoFILESYSTEM) affects the POM on the next build: the dependency is added, and deselected ones are removed. You add the matching configuration inapplication.yamland.envyourself. The README of a fresh scaffold shows you what has to go there. - A new download is a new ZIP. Nothing is merged with your project. Take from it what you need.
You turn off POM maintenance with CODEGEN_POM_GENERATE=false.