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 build reads them later from .cache/system.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-authentication<br/>+ architecture test<br/>+ CIAS_ISSUER, CIAS_CLIENT …"]
O --> C{"cias"}
C -- "REMOTE" --> R["+ cias-tenancy-client<br/>CIAS runs as a separate service"]
C -- "EMBEDDED" --> E["+ cias-tenancy, cias-user,<br/>cias-authorization, cias-iam-keycloak<br/>+ block codamai.cias<br/>+ 3 classes under config/"]
C -- "NONE" --> X["no CIAS tenant service"]
D{"database"} -- "MYSQL" --> D1["mysql-connector-j"]
D -- "MARIADB" --> D2["mariadb-java-client"]
S{"storage"} -- "FILESYSTEM" --> S1["+ cdms-localfs-storage<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: cias: EMBEDDED (only with OIDC)
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. User and permission management are prepared, and you turn them on with CIAS_USER and CIAS_AUTHORIZATION.
Result: A single unit, see Embedded
When: cias: REMOTE (only with OIDC, default)
CIAS runs as a separate service. 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: cias: NONE
No CIAS module in the scaffold. The application checks tokens but does not ask a tenant service.
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: REMOTE
<!-- 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.