What this is about
You model something in the hub, for example customer. In the code, however, there is not one class for it but several. Each has a job of its own:
- what the client sends
- only writable fields
- references as an ID or as a whole object, depending on what is allowed
- what the client gets back
- all fields including system fields
- references as the DTO of the target model
- what is stored
- table, columns, relations
- the client never sees it
- describes the model
- fields, types, relations, rules, roles
- drives reading, writing, checking
Where each shape lives
flowchart LR
C(["Client"])
subgraph REST["REST layer"]
P["Payload"]
end
subgraph SYS["System layer"]
D["DTO"]
end
subgraph DB["Persistence"]
E["Entity"]
end
M[["Meta"]]
C -- "JSON in" --> P
P -- "payload → DTO" --> D
D -- "field by field" --> E
E -- "entity → DTO" --> D
D -- "JSON out" --> C
M -.-> P
M -.-> D
M -.-> E
The client only knows JSON. Which class its JSON becomes on the server depends on the operation.
The path through the shapes
When: POST /create
-
1Client→CDMSsends JSON with
dataandresponse -
2CDMSreads
dataas a CreatePayload -
3CDMSconverts the payload into a DTO (payload → DTO)
-
4CDMScreates an empty entity and copies field by field, guided by the metadata: default values, validation, relations, hooksFields starting with
_andidare skipped. The server sets them itself. -
5CDMS→Databasestores the entity
-
6CDMS→Databasereads the new object back with exactly the fields from
response -
7CDMS→Clientconverts the entity into a DTO and sends it as JSON
Result: data contains the created object as a DTO.
When: PUT /update/{id}
-
1Client→CDMSsends JSON with the complete
dataincludingid -
2CDMSreads
dataas an UpdatePayload and converts it into a DTO -
3CDMS→Databaseloads the existing entity
-
4CDMScopies every field from the DTO to the entity. What is missing is cleared
-
5CDMS→Clientstores, reads back, responds with a DTO
Result: Afterwards the entity matches exactly the state that was sent.
When: PATCH /update/{id}
-
1Client→CDMSsends JSON with the fields to change and
id -
2CDMSreads
dataas a free map, not as a payload classOnly a map can tell whether a field is missing (leave it unchanged) ornull(clear it). A class would havenullfor every missing field. -
3CDMS→Databaseloads the existing entity
-
4CDMScopies only the keys that are in the map and known to the metadata
-
5CDMS→Clientstores, reads back, responds with a DTO
Result: Only the named fields have changed.
When: POST /read/{id}, GET /read/{id}, POST /query
-
1Client→CDMSsends the field selection
response -
2CDMSuses the metadata to work out which fields and references are meant
-
3CDMS→Databasereads only these columns as a row (tuple), not the whole table
-
4CDMSbuilds an entity from the row and converts it into a DTO
-
5CDMS→Clientsends the DTO as JSON
Result: The DTO contains the requested fields. Fields that were not requested are null.
The payloads in detail
| Payload | Endpoint | Special feature |
|---|---|---|
CreatePayload | POST /create | no id, the server assigns it |
UpdatePayload | PUT /update/{id} | id is required |
| free map | PATCH /update/{id} | only the named fields, id is required |
IdWrapperPayload | inside the others | only { "id": …, "@type": … }, for references |
Every payload is wrapped in an envelope that says what should come back:
{
"data": { ← the CreatePayload
"name": "Muster GmbH",
"address": { "id": "a1…" } ← reference as IdWrapper
},
"response": ["id", "name"], ← what should come back
"createReadMode": "STRICT" ← optional
}{
"data": { ← the DTO
"id": "5a2b…",
"name": "Muster GmbH",
"_createdOn": "2026-09-21 10:12:00", …
},
"meta": { "error": false }
}References: ID or whole object?
The generator decides what a reference looks like in the payload. What matters is whether the relation allows nested writing:
| Payload | Relation allows nested create (CREATE)? | Relation allows nested update (UPDATE)? | Shape of the reference |
|---|---|---|---|
| CreatePayload | yes | – | full CreatePayload of the target model: the child can be created along with it |
| CreatePayload | no | – | IdWrapperPayload: only id (and @type), the target must already exist |
| UpdatePayload | – | yes | full UpdatePayload of the target model: the child can be changed along with it |
| UpdatePayload | – | no | IdWrapperPayload: link only |
In the DTO, on the other hand, a reference is always a DTO of the target model. How much of it is filled depends on the field selection: with * only the id, expanded as much as you request. The rules for nested writing are in The four cases of nested writing.
The metadata: the blueprint
Every model has a generated MetaService. It describes the model on three levels:
| Part | describes | Examples |
|---|---|---|
MetaClassInfo | the model as a whole | level (system/tenant/user), abstract yes/no, audited yes/no, roles per operation, the related classes |
MetaFieldInfo | each field | name, type, relation yes/no, relation type, other side, allowed nested writing, field roles |
MetaFieldRules | the rules of a field | required, length, pattern, number limits, default value |
The metadata is more than a description. Almost every step of a request asks it:
-
CDMSResolve the field selectionWhich fields does
+or*match? Is the field a reference? -
CDMSCheck permissionsWhich role does this operation, this field require?
-
CDMSWriteWhich fields are copied? May this relation create or change children?
-
CDMSValidateWhich rules apply to this field for this operation?
- A model behaves everywhere the way it is described in the hub
What is generated per model
The generator creates all shapes and the translators between them during the build. You write none of them yourself.
| Shape | Classes |
|---|---|
| Payload | {Model}CreatePayload, {Model}UpdatePayload |
| DTO | {Model}Dto |
| Entity | {Model}Entity |
| Meta | {Model}MetaService |
| Translators | {Model}Payload2DtoMapper, {Model}Entity2DtoMapper, {Model}Dto2EntityMapper, {Model}Map2EntityMapper, {Model}TupleMapperService, {Model}MapperService |
On top of that, each model gets the classes of the layers (Api, System, Database, AuthorizationLayer, AttributeFilter). See Code generation in the build.