Worum es geht
Manche Daten gibt es nur einmal: die Einstellungen einer Person, die Konfiguration eines Mandanten, die Grundeinstellungen der ganzen Installation. Für solche Fälle markierst du ein Modell im Hub als Singleton.
Ein Objekt pro was?
Wie viele Objekte es insgesamt gibt, bestimmt die Modell-Ebene:
- ein Objekt für die ganze Installation
- in der System-Datenbank
- Beispiel: Grundeinstellungen der Plattform
- ein Objekt je Mandant
- in der Mandanten-Datenbank
- Beispiel: Konfiguration einer Firma
- ein Objekt je Person
- in der Mandanten-Datenbank, gefiltert nach
_userId - Beispiel: persönliche Einstellungen
Die Endpunkte ohne ID
| Normales Modell | Singleton | |
|---|---|---|
| anlegen | POST /create | POST /create |
| lesen | POST /read/{id} | POST /read |
| schnell ansehen | GET /read/{id} | GET /read |
| ersetzen | PUT /update/{id} | PUT /update |
| ändern | PATCH /update/{id} | PATCH /update |
| löschen | DELETE /delete/{id} | DELETE /delete |
| suchen | POST /query | gibt es nicht |
| Datei herunterladen | GET /{id}/file | GET /file |
| Historie | POST /{id}/history | POST /history |
| Rollback | POST /{id}/rollback/{revision} | POST /rollback/{revision} |
Das Objekt hat trotzdem eine id. Sie steht in der Antwort, der Client muss sie aber nie mitschicken.
Nur die Suche fehlt, denn es gibt nichts zu suchen. Historie und Rollback gibt es dagegen genau wie bei jedem anderen auditierten Modell: Die Historie ist keine Liste von Objekten, sondern der Verlauf eines Objekts über die Zeit.
Wie der Server das eine Objekt findet
Vor jeder Operation sucht der Server das vorhandene Objekt, mit denselben Regeln wie eine normale Suche:
-
1CDMSwählt die Datenbank nach der Modell-Ebene (System-DB oder die des Mandanten aus dem Token)
-
2CDMShängt bei Benutzer-Singletons den Filter
_userId = angemeldete Personan -
3CDMS→Databasesucht das Objekt
-
4CDMSgefunden → dessen
idwird für die Operation benutzt. Nicht gefunden → es gibt noch keins
Alle Operationen im Ablauf
Wann: POST /create
-
1CDMSGibt es schon ein Objekt in diesem Scope?
-
2CDMS→Clientja → 400
object-already-exists|use-update -
3CDMS→Databasenein → legt das Objekt an wie bei einem normalen Modell, mit Rechteprüfung, Validierung und Hooks
Ergebnis: Das eine Objekt existiert jetzt.
Wann: POST /read mit response, oder GET /read
-
1CDMSermittelt das Objekt
-
2CDMS→Clientnicht vorhanden → 404
no-object-found -
3CDMS→Clientvorhanden → liefert es mit den angeforderten Feldern
Wann: PUT /update
-
1CDMSermittelt das Objekt
-
2CDMS→Clientnicht vorhanden → 404
no-data-exists|use-create -
3CDMSvorhanden → setzt dessen
idselbst in die Payload ein und ersetzt wie ein normales PUT
Wann: PATCH /update
-
1CDMSermittelt das Objekt
-
2CDMS→Clientnicht vorhanden → 404
no-object-found -
3CDMSvorhanden → setzt die
idselbst ein und ändert wie ein normales PATCH: fehlendes Feld bleibt,nullleert
Wann: DELETE /delete
-
1CDMSermittelt das Objekt
-
2CDMSvorhanden → löscht es wie ein normales DELETE. Nicht vorhanden → nichts zu tun, kein Fehler
Ergebnis: Danach gibt es kein Objekt mehr; ein neues create ist wieder möglich.
Wann: GET /file, POST /history
Wie beim normalen Modell, nur ohne id im Pfad. Beide antworten mit 404, wenn es noch kein Objekt gibt. Die Historie gibt es nur bei auditierten Modellen.
Wann: POST /rollback/{revision}, bei auditierten Modellen mit Rollback-Endpunkt
-
1CDMSermittelt das Objekt
-
2CDMS→Clientnicht vorhanden → 404
data-not-found -
3CDMSvorhanden → prüft die Rollback-Rolle, führt die ROLLBACK-Hooks aus und stellt die genannte Revision wieder her, genau wie beim normalen Modell
Ergebnis: Der alte Stand ist zurück, als neue Revision. Die Revisionsnummer stammt aus der Historie (revisionMeta.ref).
Die Lebenslinie eines Singletons
stateDiagram-v2
direction LR
[*] --> Leer
Leer --> Vorhanden: POST /create
Leer --> Leer: read / update / patch → 404
Vorhanden --> Vorhanden: read, PUT, PATCH, rollback
Vorhanden --> Vorhanden: create → 400 use-update
Vorhanden --> Leer: DELETE
Typisches Muster im Client
Weil create und update getrennt sind, braucht ein Einstellungsdialog eine kleine Weiche:
| Hat GET /read beim Öffnen ein Objekt geliefert? | Beim Speichern aufrufen |
|---|---|
| ja | PATCH /update mit den geänderten Feldern |
| nein (404) | POST /create mit allen Feldern |