CodamAIDocs
Topicdone

One model, four shapes

At runtime a model exists as an entity (database), a DTO (response), a payload (input) and metadata (description). This page explains when each shape is in play and how data is mapped between them.

Variants
EntityDTOCreatePayloadUpdatePayloadPATCH with a free mapreference as IdWrapper {id, @type}reference as nested payloadMeta (MetaClassInfo, MetaFieldInfo, MetaFieldRules)create, replace, update, read

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:

The four shapes of a model
Payload
Input · CustomerCreatePayload, CustomerUpdatePayload
  • what the client sends
  • only writable fields
  • references as an ID or as a whole object, depending on what is allowed
DTO
Output · CustomerDto
  • what the client gets back
  • all fields including system fields
  • references as the DTO of the target model
Entity
Database · CustomerEntity
  • what is stored
  • table, columns, relations
  • the client never sees it
Meta
Blueprint · CustomerMetaService
  • 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

Four operations, four paths

When: POST /create

  1. 1
    Client→CDMS
    sends JSON with data and response
  2. 2
    CDMS
    reads data as a CreatePayload
  3. 3
    CDMS
    converts the payload into a DTO (payload → DTO)
  4. 4
    CDMS
    creates an empty entity and copies field by field, guided by the metadata: default values, validation, relations, hooks
    Fields starting with _ and id are skipped. The server sets them itself.
  5. 5
    CDMS→Database
    stores the entity
  6. 6
    CDMS→Database
    reads the new object back with exactly the fields from response
  7. 7
    CDMS→Client
    converts the entity into a DTO and sends it as JSON

Result: data contains the created object as a DTO.

When: PUT /update/{id}

  1. 1
    Client→CDMS
    sends JSON with the complete data including id
  2. 2
    CDMS
    reads data as an UpdatePayload and converts it into a DTO
  3. 3
    CDMS→Database
    loads the existing entity
  4. 4
    CDMS
    copies every field from the DTO to the entity. What is missing is cleared
  5. 5
    CDMS→Client
    stores, reads back, responds with a DTO

Result: Afterwards the entity matches exactly the state that was sent.

When: PATCH /update/{id}

  1. 1
    Client→CDMS
    sends JSON with the fields to change and id
  2. 2
    CDMS
    reads data as a free map, not as a payload class
    Only a map can tell whether a field is missing (leave it unchanged) or null (clear it). A class would have null for every missing field.
  3. 3
    CDMS→Database
    loads the existing entity
  4. 4
    CDMS
    copies only the keys that are in the map and known to the metadata
  5. 5
    CDMS→Client
    stores, reads back, responds with a DTO

Result: Only the named fields have changed.

When: POST /read/{id}, GET /read/{id}, POST /query

  1. 1
    Client→CDMS
    sends the field selection response
  2. 2
    CDMS
    uses the metadata to work out which fields and references are meant
  3. 3
    CDMS→Database
    reads only these columns as a row (tuple), not the whole table
  4. 4
    CDMS
    builds an entity from the row and converts it into a DTO
  5. 5
    CDMS→Client
    sends the DTO as JSON

Result: The DTO contains the requested fields. Fields that were not requested are null.

The payloads in detail

PayloadEndpointSpecial feature
CreatePayloadPOST /createno id, the server assigns it
UpdatePayloadPUT /update/{id}id is required
free mapPATCH /update/{id}only the named fields, id is required
IdWrapperPayloadinside the othersonly { "id": …, "@type": … }, for references

Every payload is wrapped in an envelope that says what should come back:

Envelope and content of a create request
WritePayload<CustomerCreatePayload>
{
  "data": {                      ← the CreatePayload
    "name": "Muster GmbH",
    "address": { "id": "a1…" }   ← reference as IdWrapper
  },
  "response": ["id", "name"],    ← what should come back
  "createReadMode": "STRICT"     ← optional
}
SingleResponse<CustomerDto>
{
  "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:

What shape does a reference have in the payload?
PayloadRelation allows nested create (CREATE)?Relation allows nested update (UPDATE)?Shape of the reference
CreatePayloadyes–full CreatePayload of the target model: the child can be created along with it
CreatePayloadno–IdWrapperPayload: only id (and @type), the target must already exist
UpdatePayload–yesfull UpdatePayload of the target model: the child can be changed along with it
UpdatePayload–noIdWrapperPayload: 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:

PartdescribesExamples
MetaClassInfothe model as a wholelevel (system/tenant/user), abstract yes/no, audited yes/no, roles per operation, the related classes
MetaFieldInfoeach fieldname, type, relation yes/no, relation type, other side, allowed nested writing, field roles
MetaFieldRulesthe rules of a fieldrequired, length, pattern, number limits, default value

The metadata is more than a description. Almost every step of a request asks it:

Who reads the metadata, and why
  1. CDMS
    Resolve the field selection
    Which fields does + or * match? Is the field a reference?
  2. CDMS
    Check permissions
    Which role does this operation, this field require?
  3. CDMS
    Write
    Which fields are copied? May this relation create or change children?
  4. CDMS
    Validate
    Which rules apply to this field for this operation?
  5. 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.

ShapeClasses
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.

Pitfalls

Sources in the code and the knowledge base
  • CDMS/cdms-rest-api – AbstractRestApi (create2Dto, update2Dto, patchObject)
  • CDMS/cdms-system-layer – AbstractLayer (recursiveCreate, recursiveUpdate, recursivePatch), AbstractSystemLayer
  • CDMS/cdms-persistence-database – projection/SelectionBuilder, TupleMapper
  • CDMS/cdms-generator – RestPayloadProcessor (IdWrapperPayload), Payload2DtoMapperProcessor, Entity2DtoMapperProcessor, TupleMapperProcessor
  • documentation/10-cdms-grundlagen/02-modelle-und-metadaten.md, 20-api/02-payloads.md
Search