Worum es geht
Du modellierst im Hub ein Modell, zum Beispiel customer. Im Code gibt es davon aber nicht eine Klasse, sondern mehrere. Jede hat eine eigene Aufgabe:
- das, was der Client schickt
- nur schreibbare Felder
- Referenzen je nach Erlaubnis als ID oder als ganzes Objekt
- das, was der Client zurückbekommt
- alle Felder inklusive Systemfeldern
- Referenzen als DTO des Zielmodells
- das, was gespeichert wird
- Tabelle, Spalten, Beziehungen
- der Client sieht sie nie
- beschreibt das Modell
- Felder, Typen, Beziehungen, Regeln, Rollen
- steuert Lesen, Schreiben, Prüfen
Wo welche Gestalt lebt
flowchart LR
C(["Client"])
subgraph REST["REST-Layer"]
P["Payload"]
end
subgraph SYS["System-Layer"]
D["DTO"]
end
subgraph DB["Persistenz"]
E["Entity"]
end
M[["Meta"]]
C -- "JSON rein" --> P
P -- "Payload → DTO" --> D
D -- "Feld für Feld" --> E
E -- "Entity → DTO" --> D
D -- "JSON raus" --> C
M -.-> P
M -.-> D
M -.-> E
Der Client kennt nur JSON. Welche Klasse sein JSON auf dem Server wird, hängt von der Operation ab.
Der Weg durch die Gestalten
Wann: POST /create
-
1Client→CDMSschickt JSON mit
dataundresponse -
2CDMSliest
dataals CreatePayload ein -
3CDMSwandelt die Payload in ein DTO um (Payload → DTO)
-
4CDMSlegt eine leere Entity an und überträgt Feld für Feld, geführt von den Metadaten: Defaultwerte, Validierung, Beziehungen, HooksFelder, die mit
_beginnen, undidwerden dabei übersprungen. Sie setzt der Server selbst. -
5CDMS→Databasespeichert die Entity
-
6CDMS→Databaseliest das neue Objekt mit genau den Feldern aus
responsezurück -
7CDMS→Clientwandelt die Entity in ein DTO um und schickt es als JSON
Ergebnis: data enthält das angelegte Objekt als DTO.
Wann: PUT /update/{id}
-
1Client→CDMSschickt JSON mit vollständigem
datainklusiveid -
2CDMSliest
dataals UpdatePayload ein und wandelt es in ein DTO um -
3CDMS→Databaselädt die vorhandene Entity
-
4CDMSüberträgt jedes Feld aus dem DTO auf die Entity. Was fehlt, wird geleert
-
5CDMS→Clientspeichert, liest zurück, antwortet mit einem DTO
Ergebnis: Die Entity entspricht danach genau dem geschickten Zustand.
Wann: PATCH /update/{id}
-
1Client→CDMSschickt JSON mit den zu ändernden Feldern und
id -
2CDMSliest
dataals freie Map ein, nicht als Payload-KlasseNur eine Map kann unterscheiden, ob ein Feld fehlt (unverändert lassen) odernullist (leeren). Eine Klasse hätte für jedes fehlende Feldnull. -
3CDMS→Databaselädt die vorhandene Entity
-
4CDMSüberträgt nur die Schlüssel, die in der Map stehen und in den Metadaten bekannt sind
-
5CDMS→Clientspeichert, liest zurück, antwortet mit einem DTO
Ergebnis: Nur die genannten Felder haben sich geändert.
Wann: POST /read/{id}, GET /read/{id}, POST /query
-
1Client→CDMSschickt die Feldauswahl
response -
2CDMSermittelt mit den Metadaten, welche Felder und Referenzen gemeint sind
-
3CDMS→Databaseliest nur diese Spalten als Zeile (Tuple), nicht die ganze Tabelle
-
4CDMSsetzt aus der Zeile eine Entity zusammen und wandelt sie in ein DTO um
-
5CDMS→Clientschickt das DTO als JSON
Ergebnis: Das DTO enthält die angeforderten Felder. Nicht angeforderte Felder stehen als null darin.
Die Payloads im Einzelnen
| Payload | Endpunkt | Besonderheit |
|---|---|---|
CreatePayload | POST /create | ohne id, die vergibt der Server |
UpdatePayload | PUT /update/{id} | id ist Pflicht |
| freie Map | PATCH /update/{id} | nur die genannten Felder, id ist Pflicht |
IdWrapperPayload | innerhalb der anderen | nur { "id": …, "@type": … }, für Referenzen |
Um jede Payload liegt noch eine Hülle, die sagt, was zurückkommen soll:
{
"data": { ← die CreatePayload
"name": "Muster GmbH",
"address": { "id": "a1…" } ← Referenz als IdWrapper
},
"response": ["id", "name"], ← was zurückkommen soll
"createReadMode": "STRICT" ← optional
}{
"data": { ← das DTO
"id": "5a2b…",
"name": "Muster GmbH",
"_createdOn": "2026-09-21 10:12:00", …
},
"meta": { "error": false }
}Referenzen: ID oder ganzes Objekt?
Wie eine Referenz in der Payload aussieht, legt der Generator fest. Entscheidend ist, ob die Beziehung verschachteltes Schreiben erlaubt:
| Payload | Beziehung erlaubt verschachteltes Anlegen (CREATE)? | Beziehung erlaubt verschachteltes Ändern (UPDATE)? | Form der Referenz |
|---|---|---|---|
| CreatePayload | ja | – | vollständige CreatePayload des Zielmodells: Kind kann mit angelegt werden |
| CreatePayload | nein | – | IdWrapperPayload: nur id (und @type), das Ziel muss es schon geben |
| UpdatePayload | – | ja | vollständige UpdatePayload des Zielmodells: Kind kann mit geändert werden |
| UpdatePayload | – | nein | IdWrapperPayload: nur verknüpfen |
Im DTO ist eine Referenz dagegen immer ein DTO des Zielmodells. Wie viel davon gefüllt ist, bestimmt die Feldauswahl: mit * nur die id, ausgebaut so viel, wie du anforderst. Die Regeln für verschachteltes Schreiben stehen unter Die vier Fälle beim verschachtelten Schreiben.
Die Metadaten: der Bauplan
Jedes Modell hat einen generierten MetaService. Er beschreibt das Modell auf drei Ebenen:
| Teil | beschreibt | Beispiele |
|---|---|---|
MetaClassInfo | das Modell als Ganzes | Ebene (System/Mandant/Benutzer), abstrakt ja/nein, auditiert ja/nein, Rollen je Operation, die zugehörigen Klassen |
MetaFieldInfo | jedes Feld | Name, Typ, Beziehung ja/nein, Beziehungstyp, Gegenseite, erlaubtes verschachteltes Schreiben, Feldrollen |
MetaFieldRules | die Regeln eines Feldes | Pflichtfeld, Länge, Muster, Zahlengrenzen, Defaultwert |
Die Metadaten sind nicht nur Beschreibung. Fast jeder Schritt einer Anfrage fragt sie:
-
CDMSFeldauswahl auflösenWelche Felder trifft
+oder*? Ist das Feld eine Referenz? -
CDMSRechte prüfenWelche Rolle verlangt diese Operation, dieses Feld?
-
CDMSSchreibenWelche Felder werden übertragen? Darf über diese Beziehung angelegt oder geändert werden?
-
CDMSValidierenWelche Regeln gelten für dieses Feld bei dieser Operation?
- Ein Modell verhält sich überall so, wie es im Hub beschrieben ist
Was pro Modell erzeugt wird
Alle Gestalten und die Übersetzer zwischen ihnen erzeugt der Generator beim Build. Du schreibst keine davon selbst.
| Gestalt | Klassen |
|---|---|
| Payload | {Model}CreatePayload, {Model}UpdatePayload |
| DTO | {Model}Dto |
| Entity | {Model}Entity |
| Meta | {Model}MetaService |
| Übersetzer | {Model}Payload2DtoMapper, {Model}Entity2DtoMapper, {Model}Dto2EntityMapper, {Model}Map2EntityMapper, {Model}TupleMapperService, {Model}MapperService |
Dazu kommen je Modell die Klassen der Schichten (Api, System, Database, AuthorizationLayer, AttributeFilter). Siehe Codegenerierung im Build.