DDM_ENTITY_RELATION
Status: Entwurf · Spec-Kandidat: ja
Zweck
Konkrete Beziehung zwischen zwei DDM_ENTITY-Datensätzen, klassifiziert durch einen DDM_RELATION_TYPE. Optional zeitlich begrenzt (valid_from, valid_to). Soft Delete.
Felder
| Feld | Typ | Pflicht | Hinweise |
|---|---|---|---|
id | uuid | ja | PK |
tenant_id | uuid | ja | FK → AA_TENANT, muss mit Tenant der beiden Endpunkte übereinstimmen (Trigger) |
relation_type_id | uuid | ja | FK → DDM_RELATION_TYPE |
from_entity_id | uuid | ja | FK → DDM_ENTITY |
to_entity_id | uuid | ja | FK → DDM_ENTITY |
valid_from | timestamptz | nein | optionaler Gültigkeitsbeginn |
valid_to | timestamptz | nein | optionales Gültigkeitsende |
sort_order | integer | ja | default 100 |
attributes | jsonb | ja | default '{}' — fachliche Eigenschaften der Beziehung; validiert gegen DDM_RELATION_TYPE_ATTRIBUTE |
metadata | jsonb | ja | default '{}' — technische Felder (Quellsystem-Hash, Sync-Flags) |
created_at, created_by, updated_at, updated_by | – | – | Standardfelder |
deleted_at, deleted_by | – | – | Soft Delete |
Constraints
entity_relation_from_to_chk:from_entity_id <> to_entity_id(keine Selbstreferenz im einfachen Fall)entity_relation_validity_chk:valid_from <= valid_tofalls beide gesetztentity_relation_attributes_is_object_chk,entity_relation_metadata_is_object_chk- Partial Unique:
uq_entity_relation_activeauf(tenant_id, relation_type_id, from_entity_id, to_entity_id) WHERE deleted_at IS NULL
Trigger
trg_entity_relation_set_updated_attrg_validate_entity_relation_types: prüft bei INSERT und bei UPDATE der Felderrelation_type_id,from_entity_id,to_entity_id,tenant_id, dass die Entitätstypen der Endpunkte zum Relationstyp passen und beide Endpunkte sowie die Beziehung selbst zum gleichen Tenant gehören. Bei Verletzung wird die Transaktion mit Exception abgebrochen.
Indizes
ix_entity_relation_from(partial, aktiv)ix_entity_relation_to(partial, aktiv)
Verhalten
- Anlage erfolgt nur über den Service-Layer; UI/API muss vor Insert die Endpunkte gegen den Relationstyp prüfen, um aussagekräftige Fehler zu liefern (DB-Trigger ist Sicherheitsnetz).
- Service-Layer validiert
attributesgegen Definitionen ausDDM_RELATION_TYPE_ATTRIBUTE(Pflicht, Datentyp, Enum, Regex, Min/Max,validation_rule). - Soft Delete via
deleted_at. Hard Delete nur administrativ. - Reaktivierung: nicht durch Datenmodell vorgesehen; bei Bedarf neuer Datensatz.
Bidirektionale Relationstypen — Zwei-Zeilen-Pattern
Bei DDM_RELATION_TYPE.is_bidirectional=true (siehe OP-13) schreibt der Service-Layer zwei Zeilen in derselben Transaktion:
relate(rel_type, A, B, attrs): INSERT entity_relation (relation_type_id, from=A, to=B, attributes=attrs, ...) INSERT entity_relation (relation_type_id, from=B, to=A, attributes=attrs, ...) → ein Audit-Eintrag, ein Outbox-Event (correlation_id verbindet beide)Bei unrelate(rel_type, A, B) werden beide Zeilen zugleich soft-gelöscht. Die Konsistenz der zwei Zeilen ist Service-Layer-Pflicht — die Datenbank erzwingt sie nicht. Speicher-Verdoppelung ist akzeptiert; Lese-Pfad-Performance hat Priorität (kein zusätzlicher View-Join).
Eindeutigkeit pro Richtung wird durch uq_entity_relation_active automatisch gewährleistet (das Partial-Unique-Tupel enthält from_entity_id/to_entity_id).
Offen
- Selbstreferenz: gibt es Beziehungstypen, die
from = toerlauben sollen? Aktuell durch Constraint ausgeschlossen. - Eindeutigkeit historisch: aktuell wird durch Partial Unique nur „eine aktive Beziehung gleicher Form” verhindert. Mehrfach-Aktivierung über Zeit (
valid_from/valid_toohne Soft Delete) ist nicht durch Index abgedeckt.