What this is about
When you create a model in the hub, the first thing you choose is its scope, the level its data lives on. There are exactly three:
- applies to the whole installation
- always stored in the system database
- every tenant sees the same data
- Example: country list, the platform's product catalog
- belongs to one customer
- stored in that tenant's database
- every person in the tenant sees the same data (as far as their roles allow)
- Example: a company's customers, orders, audits
- belongs to one person
- also stored in the tenant database
- each person sees only their own rows
- Example: personal settings, notes, favorites
Where the data lives
In operating mode MULTI, every tenant has a database of its own. The user level is not a separate database but a refinement inside the tenant database:
flowchart LR
subgraph SYS["System database"]
S1["System models<br/>e.g. country list"]
end
subgraph TA["Database of tenant A"]
A1["Tenant models<br/>e.g. customers of A"]
A2["User models<br/>Anna's rows | Ben's rows"]
end
subgraph TB2["Database of tenant B"]
B1["Tenant models<br/>e.g. customers of B"]
B2["User models<br/>Clara's rows"]
end
SYS ~~~ TA ~~~ TB2
User models have an additional field _userId. On create, CDMS writes the ID of the signed-in person into it, and on read it filters by it.
Which level do I choose?
Two questions are enough:
flowchart LR
Q1{"Do all customers need<br/>the same data?"}
Q1 -->|yes| SYS["System"]
Q1 -->|no| Q2{"Does the data belong to<br/>a single person who is the<br/>only one allowed to see it?"}
Q2 -->|yes| USR["User"]
Q2 -->|no| TEN["Tenant"]
What happens on an access
The level takes effect on every read and write, in two places: choosing the database and filtering the rows.
When: The model has the scope “system-wide”.
-
1Client→CDMSreads or writes, with or without a tenant in the token
-
2CDMSsees from the model: system level. The tenant is not even read; a
tenantheader changes nothing either -
3CDMS→System DBaccesses the system database, without a row filter from the level
Result: Everyone sees the same data, as far as their roles allow.
When: The model has the scope “tenant” (or none).
-
1Client→CDMSreads or writes; the tenant is in the token
-
2CDMSIs there a tenant in the context? May this request access it?No tenant in
MULTI→ 400CDMS_TENANT_REQUIRED. Tenant not allowed → 403. -
3CDMS→Tenant DBaccesses the database of exactly this tenant
Result: Every person in the tenant sees the same data, as far as their roles allow. Other tenants see none of it.
When: The model has the scope “user”.
-
1Client→CDMSreads or writes; tenant and person are in the token
-
2CDMSchooses the tenant database, exactly as on the tenant level
-
3CDMSon create: sets
_userIdto the ID of the signed-in person. A value sent by the client is ignored -
4CDMSon read, search, update, delete: adds the mandatory filter
_userId = signed-in person -
5CDMS→Tenant DBreturns or changes only this person's rows
Result: Each person sees only their own rows. From their point of view, other people's rows do not exist: 404, not 403.
Which database? The complete decision
This is how the persistence layer decides on every single access. The order matters: the question “system model?” comes before everything else.
| System model? | Operating mode | Tenant in the context? | Tenant listed in allowedTenants? | Target |
|---|---|---|---|---|
| yes | – | – | – | System database |
| no | MULTI | yes | yes | Database of this tenant |
| no | MULTI | yes | no | 403 CDMS_TENANT_SWITCH_NOT_AUTHORIZED |
| no | MULTI | no | – | 400 CDMS_TENANT_REQUIRED |
| no | SINGLE | – | – | The single shared database |
“Allowed” means: the tenant is in the allowedTenants list from the token. If the list is missing, nothing is allowed.
The table shows the view of the persistence layer. In MULTI, a request without a tenant usually never gets that far: the CIAS filter chain already rejects it with 403 tenant-required. The 400 is the second safeguard behind it.
There is no fallback: if the tenant is missing, a tenant object never silently ends up in the system database. Whether a tenant is served at all (active, not suspended) has already been decided before, by the tenant gate in CIAS.
SINGLE and MULTI
The levels exist in both operating modes. Only the way data is stored changes:
| MULTI | SINGLE | |
|---|---|---|
| Databases | one system DB + one DB per tenant | exactly one |
| System models | system DB | the one DB |
| Tenant models | the tenant's DB | the one DB, without separation by customer |
| User models | the tenant's DB, filtered by _userId | the one DB, filtered by _userId |
| Tenant required in the token? | yes, otherwise 400 | no |
Relations between levels
A relation connects two models, and both must be reachable in the same database. That leads to the rule the hub enforces while you model:
| Level of A | Level of B | Relation |
|---|---|---|
| System | System | allowed – both in the system DB |
| Tenant | Tenant | allowed – both in the tenant DB |
| Tenant | User | allowed – both in the tenant DB |
| User | Tenant | allowed – both in the tenant DB |
| Tenant or user | System | not allowed – the target lives in another database |
| System | Tenant or user | not allowed – the target lives in another database |
In the hub, invalid targets are grayed out in the selection (“scope not allowed”) and rejected on save.
How the level is set
-
1Adminchooses the Scope field on the model in the hub: “system-wide”, “tenant” or “user”
-
2CDMSstores it as
modelType=SYSTEM,TENANTorUSER. If the value is missing,TENANTapplies -
3CDMSThe generator derives the entity from the matching base class:
AbstractSystemModel,AbstractTenantModelorAbstractUserModelA subtype of an abstract model inherits the base class of its parent model, and with it the parent's level. -
4CDMSOn startup, CDMS checks that every entity is in the right group. If something does not match, the application does not start
Two safeguards against mix-ups
Two independent mechanisms prevent an object from ending up in the wrong database:
-
CDMSRoutingChooses the database by base class. System models ignore the tenant completely.↳ no 400 / 403, never a fallback to the system DB
-
CDMSType setsEach database connection only knows the models of its level. The system DB does not know tenant models, and vice versa.↳ no a misrouted object fails in Hibernate instead of being stored in the wrong place
-
CDMSStartup checkDo routing and type sets match?↳ no the application does not start
- The object lives in the database of its level
What else depends on the level
| Topic | System | Tenant | User |
|---|---|---|---|
| Singleton | one object for the whole installation | one object per tenant | one object per person |
| Files | storage path without tenant | path with tenant | path with tenant |
| History | revision log of the system DB | revision log of the tenant DB | revision log of the tenant DB |
| Row filter | none from the level | none from the level; the tenant takes effect through the database | _userId = signed-in person |
Roles, attribute filters and custom filters apply in addition on every level.