DDM_ENTITY_EXTERNAL_ID
Status: Entwurf · Spec-Kandidat: ja
Zweck
Mapping-Tabelle: verbindet eine DDM_ENTITY mit ihrer Identität in einem Quellsystem (DDM_SOURCE_SYSTEM). Beantwortet zwei zentrale Operationen:
- Inbound: „Webhook von SAP für
0000123— welcheDDM_ENTITYist das?” ((source_system, external_id) → entity_id). - Outbound: „Wir wollen den Customer in Salesforce updaten — welche externe ID hat er?” (
(entity_id, source_system) → external_id).
Ohne diese Tabelle gibt es keinen verlässlichen Weg, Updates zwischen MDM und Drittsystemen idempotent zu fahren oder Cross-System-Reports („SAP-Kunden, die auch im Webshop sind”) zu beantworten.
Felder
| Feld | Typ | Pflicht | Hinweise |
|---|---|---|---|
id | uuid | ja | PK |
tenant_id | uuid | ja | FK → AA_TENANT. Konsistenz mit entity.tenant_id und source_system.tenant_id per Trigger erzwungen |
entity_id | uuid | ja | FK → DDM_ENTITY ON DELETE RESTRICT |
source_system_id | uuid | ja | FK → DDM_SOURCE_SYSTEM ON DELETE RESTRICT |
external_id | text | ja | wie das Quellsystem es liefert; kein lower()-Trim, weil manche Quellsysteme case-sensitive Schlüssel führen |
metadata | jsonb | ja | default '{}' (Sync-Status, letzter-Sync-Zeitpunkt, Quellsystem-Subtyp, …) |
created_at / created_by | – | – | Wann + durch wen das Mapping entstand |
Bewusst weggelassen:
- Soft-Delete (
deleted_at,deleted_by). Mappings werden hart gelöscht (unlink-Endpoint), Historie steckt imAA_AUDIT_LOGmitaction='external_id_unlink'. updated_at/updated_byund Update-Audit. Mappings sind im Normalfall unveränderlich — wird ein anderes Mapping benötigt, wird das alte gelöscht und ein neues angelegt (zwei Audit-Zeilen). Damit ist die Quellsystem-Korrespondenz immer eindeutig in einem Punkt-in-Zeit-Snapshot.
Confidence (Option, nicht V1-aktiv)
Für künftige Match/Merge-Survivorship (OP-29) ist eine confidence numeric(3,2)-Spalte vorgesehen, Werte ∈ [0.00, 1.00]. V1 verzichtet darauf, weil noch keine Survivorship-Regeln auswerten. Implementierungs-Hinweis für die spätere Migration:
ALTER TABLE mdm.DDM_ENTITY_EXTERNAL_ID ADD COLUMN confidence numeric(3,2) NOT NULL DEFAULT 1.00, ADD CONSTRAINT entity_external_id_confidence_chk CHECK (confidence BETWEEN 0.00 AND 1.00);confidence=1.00 ist die natürliche Default-Annahme für hand-erfasste oder direkt vom Quellsystem mit gleicher ID-Struktur gepflegte Mappings. Niedrigere Werte würden bei probabilistischem Auto-Match (Trigram, Adress-Ähnlichkeit, OP-29) entstehen.
Constraints
entity_external_id_metadata_is_object_chk:jsonb_typeof(metadata) = 'object'entity_external_id_per_source_uk:UNIQUE (tenant_id, source_system_id, external_id)— eine externe ID gehört zu höchstens einer MDM-Entität (Inbound-Lookup ist eindeutig).entity_external_id_per_entity_per_source_uk:UNIQUE (tenant_id, source_system_id, entity_id)— strikte 1:1-Bindung. Eine MDM-Entität hat pro Quellsystem höchstens eine externe ID. Wird relevant, sobald OP-29 (Match/Merge) zwei Quellsystem-Records auf eine MDM-Entität verschmelzen will — dann muss zuerst der bisherige Mapping gelöscht oder das Pattern erweitert werden.
Indizes
ix_entity_external_id_lookup:(source_system_id, external_id)— schneller Inbound-Lookup für Webhook-Eingang.ix_entity_external_id_entity:(entity_id)— alle externen IDs einer MDM-Entität auf einen Schlag.- Composite-Tenant-Index entfällt, weil die UNIQUE-Constraints diesen Pfad bereits abdecken.
V1-Annahme: B-Tree-Indizes reichen aus (Volumen < 10 M Mappings). Bei deutlich höherem Volumen oder Hot-Path-Latenz auf Webhook-Eingängen ist später ein Read-Cache (Redis) oder Materialized Lookup-View denkbar — keine Pflicht V1.
Trigger
trg_validate_external_id_tenant: erzwingt, dasstenant_idder Mapping-Zeile mitDDM_ENTITY.tenant_idundDDM_SOURCE_SYSTEM.tenant_idübereinstimmt. Cross-Tenant-Mappings sind unzulässig (analog zur Cross-Tenant-Sperre aufDDM_ENTITY_RELATION).- Kein
set_updated_at-Trigger — die Tabelle hat keineupdated_at-Spalte (siehe oben).
Verhalten
Wer schreibt (gesetzt)
Beide Pfade sind erlaubt:
- Service-Layer (automatisch): Inbound-Sync-Worker oder Bulk-Import-Job (OP-41) legt Mappings an, sobald ein neuer Datensatz aus einem Quellsystem materialisiert wird. Dabei wird in derselben Transaktion
DDM_ENTITY+DDM_ENTITY_EXTERNAL_ID+AA_AUDIT_LOG(action='external_id_link') geschrieben. - Manuelle Pflege durch Steward: UI-Endpoint (
POST /entities/<id>/external-ids) für nachträgliches Mapping. Permission:manage_metadataoder zukünftig dedizierte Steward-Permission (Zusammenhang mit OP-35).
Audit (gesetzt)
mdm.audit_action wird um zwei Werte erweitert:
external_id_link— Anlage einer Mapping-Zeile.metadata.external_id,metadata.source_system_key.external_id_unlink— Hard-Delete einer Mapping-Zeile. Beim Unlink trägt der Audit-Eintrag den vorherigen Stand inold_data, damit Historie nicht verloren geht.
Re-Mapping = unlink + link (zwei Audit-Zeilen, optional mit correlation_id).
Hard-Delete-Pfad
Der Service liest die Mapping-Zeile, schreibt AA_AUDIT_LOG.action='external_id_unlink' mit old_data, dann DELETE FROM mdm.DDM_ENTITY_EXTERNAL_ID WHERE id = …. Alles in einer Transaktion. Permission: manage_metadata (V1) — feinere Permissions später.
Beziehung zum DDM_ENTITY-Lebenszyklus
- Soft-gelöschte (
DDM_ENTITY.deleted_at IS NOT NULL) Entitäten behalten ihre Mappings — Reaktivierung (Restore) bringt das Mapping wieder live. - Hard-Delete einer Entität ist durch
ON DELETE RESTRICTblockiert, solange Mappings existieren. Erst nachdem alle externen IDs entfernt sind, ist Hard-Delete möglich. Damit kann ein versehentlicher Hard-Delete nicht still die Verbindung zum Quellsystem zerreißen.
Beispiel
DDM_ENTITY: id=c-1001-uuid, code='C-1001', tenant=acme
DDM_ENTITY_EXTERNAL_ID: (entity=c-1001, source_system='sap_prod', external_id='0000123') (entity=c-1001, source_system='salesforce', external_id='acct_42') (entity=c-1001, source_system='webshop', external_id='12345')Webhook-Eingang aus SAP (0000123 hat neue Adresse) → Lookup (sap_prod, 0000123) → c-1001-uuid → Service updated DDM_ENTITY.attributes. Outbound an Salesforce-Webhook → Lookup (c-1001-uuid, salesforce) → acct_42 → Payload geht raus.