Zum Inhalt springen

Offene Punkte

Status: Entwurf · Spec-Kandidat: nein (laufende Liste)

Offene Punkte, die bei der Spec-Aufbereitung aufgetaucht sind. Jeder Punkt sollte vor seiner verantwortlichen Spec geklärt sein.

Initiale offene Punkte

IDFrageVerantwortliche Spec(s)
OP-01Mandantenfähigkeit direkt in Version 1 oder erst später? Gelöst: Tenant-Säule ist Pflicht im Datenmodell (AA_TENANT). V1 nutzt Default-Tenant public; Aktivierung weiterer Tenants ohne Schemaänderung.Architekturprinzipien, Autorisierung
OP-02Welche Typen sind initial verpflichtend? Gelöst (V0): company (mit Self-Relation für Tochter-Unternehmen), address, employee, offer (Subtyp via Attribut kind ∈ {service, material}), property, case. Lebende Liste in docs/business/30-domain/00-entity-catalog.md — wächst, sobald neue Typen genannt werden.DDM_ENTITY_TYPE
OP-03Welche fachlichen Rollen sind verbindlich? Gelöst: Rollen sind tenant-scoped (AA_ROLE.tenant_id NOT NULL, UNIQUE(tenant_id, key)). Globales Default-Set lebt als Template in Migration/Seed; beim Anlegen eines AA_TENANT instanziiert mdm.fn_provision_tenant_defaults das Set in den Tenant. V1-Default-Rollen: administrator, owner, steward, metadata_admin, editor, reader, auditor, exporter. Tenant-eigene Custom-Rollen frei (is_system=false).AA_ROLE, AA_TENANT – Onboarding, Autorisierung
OP-04Welche Reports sind zu Beginn erforderlich? Gelöst (V1): Mandats-Wirtschaftlichkeit (pro Mandat über alle gebuchten Module), Holding-Konsolidierung (Beitrag je Tochter), Modul-Auslastung (über Mandate hinweg), Audit-Reports (AA_AUDIT_LOG-Auszüge je Aktor / Aktion / Zeitraum). Auslieferung über dedizierte REST-Endpunkte mit dynamischer JSONB-Projektion im Query-Layer (OP-16). BI-Anbindung via Aggregation-Layer offen (OP-44).Views, SQL / Reporting
OP-05Welche Systeme müssen integriert werden? Stand 2026-04-29: noch unbekannt — Auftraggeber listet auf, sobald Mandate konkretisiert sind. Outbox + REST stehen, Source-System-Registry DDM_SOURCE_SYSTEM ist V1-bereit.REST API, Events / Webhooks
OP-06Welche regulatorischen Aufbewahrungsfristen gelten? Gelöst (V1): pauschal 10 Jahre angelehnt an HGB §257 / GoBD für Geschäftsunterlagen. Wirkt auf AA_AUDIT_LOG, DDM_ENTITY_VERSION, soft-deleted DDM_ENTITY, DDM_ATTACHMENT und AA_OUTBOX_EVENT (für letztere greift zusätzlich die kurze Outbox-Retention, siehe OP-22). Pro-Tenant-Override über AA_TENANT.metadata.retention_years möglich; granulare Per-Type-Retention bleibt OP-36 vorbehalten.Audit und Historisierung, Backup und Restore, Transport und Sicherheits-Audit

Zusätzliche Punkte aus der Spec-Aufbereitung

IDFrageVerantwortliche Spec(s)
OP-07Konkrete Performance-Zielwerte (P95/P99, Throughput, Datenvolumen). Gelöst (V1, P95-orientiert): Read-by-ID ≤ 50 ms, gefilterte Liste à 50 Records inkl. Volltext ≤ 200 ms, Single-Insert/Update ≤ 150 ms, relate/unrelate ≤ 200 ms. Throughput pro Tenant 200 Reads/s + 50 Writes/s sustained. Datenvolumen V1: bis 5 Mio Entities und 20 Mio Relations pro Tenant. 50 concurrent Active-User je Tenant. 4 parallele Worker pro AA_JOB.job_kind. P99 ≤ 3× P95 als Toleranz, alles darüber ist Capacity-Issue.Nichtfunktionale Anforderungen
OP-08Wahl Sprache, Web-Framework, ORM/Query-Layer. Aktuelle Präferenz (nicht final): Java (latest) + Spring Boot 4. Frage wird vom Auftraggeber explizit angestoßen, nicht proaktiv von der Spec aufgeworfen.Zielarchitektur, Tech-Stack-Entscheidung
OP-09Wahl OIDC-Provider Gelöst: Authentik (self-hosted). Offen bleibt nur das konkrete Mapping groups-Claim → AA_PRINCIPAL_GROUP.key.Authentifizierung, Autorisierung
OP-10Wahl Migrationswerkzeug. Gelöst (abhängig von OP-08): Flyway falls Java-Stack gewählt wird (Java + Spring Boot 4 ist aktuelle Präferenz), Sqitch falls sprachneutraler Stack. Beide schreiben rohe SQL-Migrations gegen mdm.*; keine framework-eigenen DSLs. Verzeichnis db/migrations/V<seq>__<slug>.sql.CI/CD und Migrationen
OP-11Wahl Container-Orchestrierung. Gelöst: Coolify (self-hosted), Image-Build über docker-compose.yaml, Source-Hosting auf GitHub. Siehe Laufzeitumgebung und CI/CD und Migrationen.Laufzeitumgebung
OP-12Snapshot-Strategie für DDM_ENTITY_VERSION (Frequenz, Inhalt). Gelöst: Voller JSONB-Snapshot bei jedem INSERT und UPDATE der Entität, geschrieben vom Service-Layer in derselben Transaktion. Inhalt: relationaler Kern + attributes. Beziehungen sind nicht Teil des Snapshots (über AA_AUDIT_LOG.relate/unrelate historisiert). Soft-Delete erzeugt keinen neuen Snapshot, Restore schon. Retention/Komprimierung bleibt offen.DDM_ENTITY_VERSION, Audit und Historisierung
OP-13Bidirektionalität von Beziehungen: separater Datensatz oder reine Auswertung? Gelöst: Zwei-Zeilen-Pattern (Lese-Pfad-Optimierung). Service-Layer schreibt bei jeder bidirektionalen DDM_RELATION_TYPE zwei DDM_ENTITY_RELATION-Zeilen (A→B und B→A) in derselben Transaktion. Konsistenz ist Service-Layer-Pflicht; Speicher-Verdoppelung ist akzeptiert (skalierbar), Lese-Performance hat Vorrang.DDM_RELATION_TYPE, DDM_ENTITY_RELATION
OP-13aKardinalitäts- und Pflicht-Constraints für Beziehungen. Gelöst (V1, revidiert 2026-04-29): DDM_RELATION_TYPE.min_to_per_from (default 0) und max_to_per_from (NULL=unbegrenzt) sind beide Service-Layer-Regeln, kein DB-Trigger. Begründung: harte Trigger-Durchsetzung von max blockiert legitime Bulk-Imports und Re-Parenting in Migrations-Jobs; einheitliche Handhabung mit min. Verstöße sind über die Reporting-Views mdm.v_relation_min_violations und mdm.v_relation_max_violations sichtbar; Service-Layer prüft beim relate/Statusübergang. Endpunkt-Typ-Validierung (mdm.validate_entity_relation_types) bleibt unverändert trigger-erzwungen. N-äre Beziehungen weiterhin über Helper-DDM_ENTITY_TYPE (z. B. assignment).DDM_RELATION_TYPE, DDM_ENTITY_RELATION
OP-14Schema des validation_rule-JSONB. Gelöst (gestaffelt): V1 unterstützt nur deklarative Basis-Constraints — required, data_type, enum/multi_enum (über enum_set_id), min/max (numerisch + Datum), min_length/max_length (text), regex, unique_per_type. Schema-Form: { "kind": "<rule>", ...args } als Array unter validation_rule. Cross-Field- und Cross-Entity-Regeln (z. B. Summe-Anteile=100) sind V2 und nutzen CEL (Common Expression Language) als Engine, gespeichert als { "kind": "cel", "expr": "..." }. CEL ist gewählt wegen Sandbox-Sicherheit, Type-Safety und Sprach-Neutralität (Library für Java + Go + Python). Engine-Wahl finalisiert mit Backend-Pick (OP-08).DDM_ENTITY_TYPE_ATTRIBUTE, Validierung
OP-15Internationalisierung (Labels, Fehlermeldungen, Volltextkonfiguration). Gelöst (V1): Workbench wird direkt mehrsprachig ausgeliefert. Labels und Beschreibungen auf DDM_ENTITY_TYPE, DDM_ENTITY_TYPE_ATTRIBUTE, DDM_RELATION_TYPE, DDM_ENUM_VALUE werden zu i18n-Maps { "de": "...", "en": "..." } (jsonb), Default-Sprache deutsch. Fehlermeldungen aus dem Service-Layer kommen als error_code + Parameter-Map; UI rendert über Resource-Bundles. Volltext-Konfig je tenant.metadata.locale (german / english für to_tsvector). Siehe OP-37 (Codelisten-i18n) und OP-38 (Attributwerte-i18n).DDM_ENUM_SET / DDM_ENUM_VALUE, Validierung, Trigger und Funktionen
OP-16Auto-Generierung der Reporting-Views aus Metadaten. Gelöst (2026-04-29) — Option C: Keine statischen mdm.v_<key>-Views in der DB für BI/Reporting. JSONB→Spalten-Projektion läuft dynamisch im Query-Layer (z. B. Drizzle/JOOQ jsonb_to_record). BI-Konsumenten greifen über die REST API zu, nicht über direkten DB-Zugriff. Nur operative/Auth-Views bleiben in mdm.* (v_effective_permission, v_principal, v_relation_*_violations, v_entity_attribute_drift, v_index_drift).Views, SQL / Reporting
OP-17Strategie für Expression-Index-Erzeugung auf Basis von searchable/sortable-Flags. Gelöst (V1, 2026-04-29): Async-Job + UI-Status-Modell, GIN-Catch-all selektiv nur für External-Source-Target-Typen. Expression-Indizes auf searchable/sortable-Attributen werden über AA_JOB.job_kind='index_sync' CONCURRENTLY aufgebaut/abgebaut. Jedes Attribut führt index_status ∈ {pending, building, active, failed} plus index_built_at. UI/REST sperren Filter/Sort, solange index_status != active; Filter-Picker und Quick-Search listen ausschließlich searchable=true-Attribute. Vor Drop bei searchable=false / sortable=false blockt der Service mit Referenz-Liste aus saved_search / report_view; Force-Drop ist auditiert. Numerische / Datum-Indizes werden erst gebaut, wenn mdm.v_entity_attribute_drift für das Attribut leer ist. Job-Failure setzt index_status='failed', erzeugt Outbox-Event index_build_failed, erscheint in mdm.v_index_drift. GIN-Catch-all auf (attributes) jsonb_path_ops wird nur für DDM_ENTITY_TYPE.is_external_source_target=true als partieller Index pro Typ angelegt; Auto-Setzung beim ersten DDM_ENTITY_EXTERNAL_ID- oder DDM_SOURCE_SYSTEM-Mapping. Promotion (intern → external) löst AA_JOB.job_kind='gin_backfill' aus; Demotion ist Admin-Migration. Internal-Only-Typen (z. B. mandant, case, assumption_set, module_pricing) bekommen keinen Catch-all — geschützt durch UI-Sperren + Engineering-Regel „kein Service-Path-Query ohne deklarierten Index”. Match/Merge (OP-29) und External-ID-Heuristiken (OP-30) deklarieren Index-Anforderungen je Regel als gezielte GIN-Path-Indizes.Indizes, Suche und Filterung
OP-18Hard-Delete-Workflow (Genehmigung, Mehraugen). Gelöst (V1): admin-only, kein Approval-Flow. Permission mdm.entity:hard_delete ist nur an System-Rolle administrator gebunden, nicht über ACL delegierbar. Aktion erzeugt AA_AUDIT_LOG-Zeile mit Aktion hard_delete und vollem Pre-Image. Mehraugen-/Approval-Flow für sensible Mutations bleibt OP-35 (V2).Löschen und Archivierung
OP-19Behandlung historischer Datensätze bei Schemaänderung von DDM_ENTITY_TYPE_ATTRIBUTE. Gelöst (V1): Lazy-Validate — bestehende DDM_ENTITY.attributes werden bei Schema-Change nicht migriert; alte Datensätze behalten alte Schlüssel/Werte. Validierung gegen das aktuelle Schema läuft erst beim nächsten Update der Entität. Liste der Entities, die nicht zum aktuellen Schema passen, ist über mdm.v_entity_attribute_drift sichtbar (Stewardship-Inbox). Hartes Massenupdate ist optional als AA_JOB.job_kind='attribute_migration' für seltene Fälle, kein Default.Validierung, DDM_ENTITY_TYPE_ATTRIBUTE
OP-20Audit-Volumen, Partitionierung und separates Read-Modell. Gelöst (V1): geschätzt ~3.000 Audit-Rows/Tag/Tenant ⇒ ~1,1 Mio/Jahr/Tenant ⇒ unterhalb der Partitionierungs-Schwelle. AA_AUDIT_LOG bleibt single-table mit Index auf (tenant_id, target_id, created_at) und (actor_principal_id, created_at). Yearly-Range-Partitionierung wird vorbereitend dokumentiert, aber nicht in V1 aktiviert. Separates Read-Modell ebenfalls deferred. Re-Bewertung sobald reale Volumina vorliegen oder ein Tenant > 50.000 Rows/Tag erreicht.AA_AUDIT_LOG, Audit und Historisierung
OP-21RPO/RTO und Backup-Tooling. V1-Vorschlag (offen, Auftraggeber bestätigt): RPO ≤ 24h via täglichem pg_dump + WAL-Archivierung (15-min-Slots) ⇒ effektives RPO ≤ 15 min, RTO ≤ 4h. Backup-Storage wie Attachments (S3-kompatibel via Coolify).Backup und Restore
OP-22Eventdesign: Webhooks vs. Queue, Outbox-Pattern. Outbox ist gesetzt (AA_OUTBOX_EVENT). Gelöst (Konsument V1): Webhook (HTTP-Callback). Broker-Wahl (Kafka / RabbitMQ / NATS) deferred bis konkrete V2+-Anforderung. Retention gesetzt: Erfolgreich dispatchete Events bleiben 10 Tage in AA_OUTBOX_EVENT und werden danach durch AA_JOB.job_kind='outbox_compact' gelöscht. Failed/poisoned Events bleiben bis manuell quittiert. Audit-Spur der Mutationen liegt parallel in AA_AUDIT_LOG (Retention OP-06: 10 Jahre).Events / Webhooks
OP-23Just-in-Time-Provisionierung unbekannter OIDC-Subjekte (anlegen vs. ablehnen). Gelöst: Auto-Anlage AA_APP_USER (is_active=true) ohne Rollenvergabe. User ist sofort handlungsunfähig (Default-Deny in mdm.v_effective_permission); Admin schaltet via AA_PRINCIPAL_ROLE-Vergabe frei. Kein Auto-Reader. JIT-Anlage wird in AA_AUDIT_LOG mit payload.jit_provisioned=true markiert.Authentifizierung, Autorisierung
OP-24Caching von mdm.v_effective_permission (Request-Scope, Session-Scope, Invalidierung). Gelöst (V1): Redis-Cache (Session-Scope) — Key perm:<tenant>:<principal>:<session>, Wert ist die für den Login-Scope materialisierte Permission-Map. TTL = Session-Lebensdauer (max 24h) plus Lazy-Refresh nach 5 Minuten Inaktivität. Invalidierung: Service-Layer schreibt nach jedem AA_PRINCIPAL_ROLE/AA_ROLE_PERMISSION/AA_ACL_ENTRY/AA_PRINCIPAL_GROUP_MEMBER-Mutation in einen Redis-Channel perm:invalidate:<tenant>:<principal> (oder * bei Rollen-/Permission-Änderungen, die viele Principals betreffen). Konsumenten leeren betroffene Keys. DB-materialisierter Cache (OP-49) erst, wenn Redis-Variante nicht reicht.Autorisierung, Views
OP-25Postgres-RLS als zusätzliche Verteidigungslinie? Gelöst (V1): kein RLS. Autorisierung läuft ausschließlich über Service-Layer-Filter via mdm.v_effective_permission + Redis-Cache (OP-24). Begründung: RLS-Policies gegen ein vollständig ACL-getriebenes Modell mit Group-Vererbung und tenant-scoped Rollen werden komplex, schwer testbar und kollidieren mit dem geplanten Permission-Cache. Verteidigungstiefe wird stattdessen durch (a) konsequenten Tenant-Filter im Service, (b) DB-Tenant-Trigger auf cross-tenant-Verletzungen und (c) Audit-Alarmierung gewährleistet. Re-Bewertung, falls externe Compliance-Audits Bedenken äußern.Autorisierung
OP-26Zykluserkennung für AA_PRINCIPAL_GROUP-Verschachtelung (Tiefenlimit, Detection). Gelöst (V1): dynamische Tiefe ohne hartes Limit, Zyklus-Erkennung per Trigger (mdm.validate_principal_group_acyclic BEFORE INSERT/UPDATE auf AA_PRINCIPAL_GROUP_MEMBER). Funktion läuft eine rekursive CTE über die geplante Membership-Kante; falls die einzufügende Kante einen Pfad zurück zur eigenen Group erzeugen würde, schlägt der Schreibvorgang fehl. Permission-Resolution läuft ebenfalls rekursiv (CTE in v_effective_permission); Performance wird über den Redis-Cache (OP-24) abgefangen.AA_PRINCIPAL_GROUP
OP-27Mapping IdP-Gruppen-Claims auf AA_PRINCIPAL_GROUP.key und Sync-Strategie. Gelöst: Bei jedem Login synchronisiert der API-Layer den Authentik-groups-Claim. Unbekannte Gruppe → Auto-Stub AA_PRINCIPAL_GROUP mit is_active=false, keine Rollen. Memberships, die nicht mehr im Claim stehen, werden soft-gelöscht. Admin schaltet Stub via is_active=true + AA_PRINCIPAL_ROLE-Bindung frei. Inbox-Sicht: mdm.v_principal_group_unassigned. Whitelist-Variante verworfen.Authentifizierung – Group-Sync, AA_PRINCIPAL_GROUP, Autorisierung
OP-28Tenant-Scope (FR-203): Erweiterung von acl_scope und AA_PRINCIPAL_ROLE. Gelöst: acl_scope='tenant', scope_tenant_id in AA_ROLE_PERMISSION/AA_PRINCIPAL_ROLE/AA_ACL_ENTRY.Autorisierung
OP-28aDDM_ENTITY_TYPE system-global oder per Tenant? Gelöst: per-tenant. DDM_ENTITY_TYPE, DDM_ENTITY_TYPE_ATTRIBUTE, DDM_RELATION_TYPE, DDM_RELATION_TYPE_ATTRIBUTE, DDM_ENUM_SET, DDM_ENUM_VALUE liegen in mdm mit tenant_id NOT NULL. Konzern-Tochter darf eigene Typen pflegen.Schema und Konventionen, DDM_ENTITY_TYPE

Enterprise-MDM-Lücken (aus Vergleich mit gängigen Master-Data-Plattformen)

Die folgenden Punkte sind in Enterprise-MDM-Produkten (Informatica MDM, Reltio, Stibo, Profisee, SAP MDG) typischerweise enthalten und in unserem Modell aktuell nicht abgebildet. Jeder Punkt enthält eine ausführliche Begründung, wozu das Feature dient, welches Problem es löst und wann es relevant wird.

OP-29 · Match / Merge / Survivorship (Golden Record)

Status: V1 (Spec-Detaillierung ausstehend) · Verantwortlich: Suche und Filterung, DDM_ENTITY, Validierung

V1-Entscheidung (2026-04-29): Match/Merge ist V1-Scope. Modell: match_candidate (id, tenant_id, entity_a_id, entity_b_id, score, rule_set, status: open|merged|dismissed, evaluated_at), merge_record (master_id, merged_id, decided_by, decided_at, survivorship jsonb). Match-Regeln deklarativ in CEL (siehe OP-14, V2 für CEL — V1 nutzt feste Regelmenge: exact-match, lower-name, trgm-similarity ≥ 0.85). Merge schreibt Master-Survivorship-Wahl in master.attributes und setzt merged.merged_into_id. Unmerge unterstützt. Score-Engine läuft als AA_JOB.job_kind='match_evaluate'.

In jeder Stammdatenwelt entstehen Duplikate: derselbe Kunde wird im CRM als “Müller GmbH”, in der Buchhaltung als “Mueller GmbH” und im Webshop als “müller-gmbh-berlin” angelegt. Ohne Match/Merge wachsen diese Dubletten unkontrolliert, Reports zählen denselben Kunden mehrfach, Marketing schickt drei Mailings, Compliance bekommt drei Risiko-Profile.

Was fehlt: ein Modell für Match-Kandidaten (Score zwischen Records nach Regelwerk: exakter Name, Trigram-Ähnlichkeit, Adress-Match, Steuer-ID-Match, …), ein Merge-Workflow (zwei Records werden zu einem Master-Record verschmolzen, die Quellen bleiben über merged_into_id referenzierbar) und Survivorship-Regeln (welcher Wert pro Attribut im Master gewinnt: Quellpriorität — SAP > CRM > Web; Aktualität — neuestes updated_at gewinnt; manuelle Steward-Wahl). Dazu kommt Unmerge für irrtümliche Verschmelzungen.

Wann relevant: ab dem Moment, wo Stammdaten aus mehreren Quellsystemen integriert werden oder Anwender selbst freitext-anlegen. Bei rein steuerinternen Modellen mit eindeutigem code aus SAP nicht zwingend.

OP-30 · Cross-Reference / External-System-Mapping

Status: gelöst · Verantwortlich: DDM_SOURCE_SYSTEM, DDM_ENTITY_EXTERNAL_ID, AA_AUDIT_LOG

Ein Stammdatensatz hat oft Identitäten in mehreren Quellsystemen: Kunde “C-1001” in MDM ist 0000123 in SAP, acct_42 in Salesforce, 12345 in der Webshop-DB. Ohne explizites Mapping kann das MDM keine Updates vom Quellsystem zurückspielen, keine Webhooks über System-IDs auslösen und kein Reporting “wie viele unserer SAP-Kunden haben einen Webshop-Account?” beantworten.

Gelöst (V1):

  • Quellsystem-Registry DDM_SOURCE_SYSTEM (tenant-scoped, key, trust_level, webhook_config). trust_level ist V1 dokumentiert, aktiv ab OP-29.
  • Mapping-Tabelle DDM_ENTITY_EXTERNAL_ID mit FK auf Registry, strict 1:1 je (tenant, source_system, entity) und je (tenant, source_system, external_id).
  • Hard-Delete der Mapping-Zeilen, Historie über AA_AUDIT_LOG mit neuen Aktionen external_id_link / external_id_unlink.
  • B-Tree-Indizes für Inbound-(source_system_id, external_id) und Outbound-Lookup (entity_id) reichen V1.
  • Service-Layer und manuelle Stewardship-UI dürfen Mappings anlegen.
  • Tenant-Konsistenz per Trigger mdm.validate_external_id_tenant erzwungen.
  • Confidence-Spalte ist als Erweiterung dokumentiert, V1 nicht aktiv (Migrationsweg in DDM_ENTITY_EXTERNAL_ID).

OP-31 · Datenherkunft / Lineage / Provenance auf Attribut-Ebene

Status: V2 · Verantwortlich: DDM_ENTITY, Audit und Historisierung

Entscheidung (2026-04-29): V2 — bei Multi-Source-Integration relevant. V1 protokolliert Source-System-Identität nur über DDM_ENTITY_EXTERNAL_ID (OP-30) und Audit-Aktor; Pro-Attribut-Lineage folgt mit Match/Merge-Reife.

AA_AUDIT_LOG zeigt wer wann was geändert hat — aber nicht woher der Wert ursprünglich kommt. Bei mehreren Quellsystemen (SAP liefert Adresse, CRM liefert Telefonnummer, Web-Self-Service liefert E-Mail) ist Lineage essenziell: “warum steht hier eine Adresse aus 2019?” → “weil SAP zuletzt am 2019-03-04 synchronisiert hat, CRM seit 2022 nicht mehr antwortet”.

Was fehlt: pro Attribut ein zusätzlicher Datensatz attribute_lineage (entity_id, attribute_key, value, source_system, source_record_id, ingested_at, confidence). Damit lassen sich konkurrierende Werte aus unterschiedlichen Quellen sauber dokumentieren und Survivorship-Entscheidungen begründen.

Wann relevant: ab Multi-Source-Integration. Auch ein typisches Audit-Compliance-Thema (BaFin, GDPR-Art. 5 Datenrichtigkeit).

OP-32 · Bitemporalität (fachliche Gültigkeit vs. Transaktionszeit)

Status: V1 (Spec-Detaillierung ausstehend) · Verantwortlich: DDM_ENTITY, DDM_ENTITY_VERSION, AA_AUDIT_LOG

V1-Entscheidung (2026-04-29): DDM_ENTITY und DDM_ENTITY_RELATION bekommen optional valid_from / valid_to (timestamptz, NULL = unbeschränkt) als fachliche Gültigkeit — neben den vorhandenen Transaktions-Zeitstempeln. Pro DDM_ENTITY_TYPE aktivierbar via Flag is_bitemporal (default false). Bei is_bitemporal=true erzwingt der Service ein nicht-überlappendes valid_from/valid_to-Intervall pro (tenant, entity_type, code, business_key) für aktive Records. Abfragen werden bitemporal: as_of_business_time + as_of_transaction_time. Pflicht-Kontexte: Verträge, Tarife, Mieter-Beziehungen.

Aktuell speichern wir nur Transaktionszeit (created_at/updated_at). Damit lässt sich beantworten: “wie sah der Datensatz X am 01.03.2024 in der Datenbank aus?” — aber nicht: “wie sah der Datensatz X fachlich am 01.03.2024 aus, unabhängig davon, wann das jemand gepflegt hat?”. Klassisches Beispiel: Adressänderung wird erst 4 Wochen verspätet eingegeben, gilt aber rückwirkend zum Umzugsdatum.

Was fehlt: pro Wert (oder pro Datensatz) zusätzlich valid_from/valid_to als fachliche Gültigkeit — neben den schon vorhandenen technischen Zeitstempeln. Abfragen werden bitemporal: “as_of_business_time AND as_of_transaction_time”. Dazu Constraints, dass Gültigkeitsintervalle pro Schlüssel nicht überlappen, und Trigger für lückenlose Historie.

Wann relevant: sobald Verträge, Preise, Mitgliedschaften oder Compliance-Status modelliert werden. Pflicht in Versicherung, Banking, Healthcare. In einem reinen “wer ist unser Kunde”-Datensatz oft entbehrlich.

OP-33 · Hierarchien (Org-Strukturen, Konzern, Produktkategorien)

Status: V1 (Spec-Detaillierung ausstehend) · Verantwortlich: DDM_RELATION_TYPE, Indizes

V1-Entscheidung (2026-04-29): Hierarchien sind V1-Scope (Konzern → Tochter → Liegenschaft, Mandate über Tochter-Strukturen). Umsetzung: DDM_RELATION_TYPE.is_hierarchy boolean DEFAULT false und Closure-Table mdm.DDM_ENTITY_HIERARCHY (ancestor_id, descendant_id, depth, relation_type_id) plus Trigger mdm.maintain_hierarchy_closure auf DDM_ENTITY_RELATION für is_hierarchy-Typen (Insert + Soft-Delete halten Closure synchron). Zyklen werden vom Closure-Trigger zurückgewiesen. Materialized-Path (ltree) ist Erweiterung, nicht Default — Closure deckt typische Konzern-Tiefen ≤ 6 problemlos ab.

Beziehungen sind aktuell flach (from-to). Klassische MDM-Anforderung sind aber rekursive Hierarchien: Konzern → Tochter → Enkel; Produkt-Kategorie → Unterkategorie → Artikel; Kostenstelle → Abteilung → Team. Über DDM_ENTITY_RELATION rekursiv möglich, aber ohne Performance-Hilfen werden Abfragen “alle Töchter des Konzerns Acme” zu rekursiven CTEs auf jeder Lese-Anfrage.

Was fehlt: optionale materialisierte Pfade (hierarchy_path als ltree oder text[]) bzw. eine Closure-Table (entity_ancestor (ancestor_id, descendant_id, depth)), gepflegt per Trigger bei DDM_ENTITY_RELATION-Änderung. Dazu Constraints gegen Zyklen und ein DDM_RELATION_TYPE.is_hierarchy-Flag, damit der Trigger weiß, welche Beziehungen die Hierarchie aufspannen.

Wann relevant: sobald hierarchische Auswertungen (“rolle Umsatz auf Konzern-Ebene”) oder hierarchische Permissions (“editor für Acme inkl. aller Töchter”) gefordert sind.

OP-34 · Datenqualitäts-Score und DQ-Rules

Status: V2 · Verantwortlich: Validierung, DDM_ENTITY

Entscheidung (2026-04-29): V2. V1-Validierung bleibt binär. Sobald Bestandsdaten aus Altsystemen integriert werden (treibende Anforderung), DQ-Score mit dq_rule + dq_finding nachziehen.

Validierung ist heute binär: ein Datensatz ist gültig oder ein Schreibvorgang scheitert. In einer Stammdatenwelt mit Bestandsdaten aus Altsystemen ist das zu hart: 80 % der bestehenden Kunden haben keine Telefonnummer — die kann man nicht alle ablehnen, aber man möchte sie als “rote” Records erkennen und stewardship-getrieben bereinigen.

Was fehlt: eine separate Tabelle dq_rule (id, entity_type_id, rule_kind, rule_definition jsonb, severity, weight) und eine dq_finding (entity_id, rule_id, status, evaluated_at)-Tabelle. Pro Datensatz aggregiert ein DQ-Score (0–100), der z. B. in DDM_ENTITY.metadata.dq_score cached wird. Reports und Stewardship-Inboxen filtern nach Score.

Wann relevant: sobald die Plattform Bestandsdaten aufnimmt, die nicht zwangsweise vollständig sein können, und Datenqualität gemessen / verbessert werden soll.

OP-35 · Stewardship / Approval-Workflow / Change Requests

Status: V2 · Verantwortlich: DDM_ENTITY, Löschen und Archivierung, Autorisierung

Entscheidung (2026-04-29): V2. V1 nimmt Mutationen sofort wirksam. Hard-Delete bleibt admin-only ohne Approval (OP-18). 4-Augen-Workflow folgt sobald regulatorischer Bedarf (BaFin/MaRisk/GxP) konkretisiert wird.

Aktuell wird jede Änderung sofort wirksam. In regulierten Umgebungen ist das nicht akzeptabel: Eine Bonitätsklassen-Änderung muss vom 4-Augen-Prinzip durchlaufen, Hard-Delete braucht Approval, neue Lieferanten brauchen Compliance-Review.

Was fehlt: eine Tabelle change_request (id, entity_id, requested_changes jsonb, requested_by, status: draft|pending|approved|rejected, approver, decided_at, reason) mit eigenem Workflow im Service-Layer. Optional eine Inbox-/Queue-Sicht (v_steward_inbox pro Rolle). Hard-Delete-Workflow (OP-18) ist Sonderfall davon.

Wann relevant: sobald regulatorische Anforderungen (BaFin, MaRisk, GxP) oder unternehmensinterne Governance-Regeln das verlangen — d. h. praktisch in jedem mittleren bis großen Konzern.

OP-36 · Governance-Metadaten (Owner, Steward, Klassifikation, PII, Retention)

Status: V2 · Verantwortlich: DDM_ENTITY_TYPE, DDM_ENTITY_TYPE_ATTRIBUTE, Audit und Historisierung

Entscheidung (2026-04-29): V2 (verschoben — „später”). DSGVO-Mindestpflichten werden V1 über pauschale Retention (OP-06: 10 Jahre) und konsequente Audit-Spur abgedeckt. PII-Flag, Klassifikation, legal_hold und Per-Type-Retention folgen in V2 konsistent über alle relevanten Tabellen.

DSGVO, ISO 27001 und interne Datenschutz-Reviews verlangen, dass jedes Attribut bzw. jeder Entitätstyp eine Daten-Klassifikation trägt (public / internal / confidential / restricted), einen Daten-Owner und einen Daten-Steward hat, ggf. als personenbezogen markiert ist (mit Rechtsgrundlage und Zweckbindung) und eine Aufbewahrungsfrist mitbringt.

Was fehlt: zusätzliche Spalten auf DDM_ENTITY_TYPE (owner_principal_id, steward_principal_id, classification, is_pii) und DDM_ENTITY_TYPE_ATTRIBUTE (is_pii, classification, legal_basis, purpose). Auf DDM_ENTITY ein optionales retention_until und legal_hold-Flag, das Soft-Delete und Hard-Delete blockiert.

Wann relevant: für jede Plattform, die personenbezogene Daten oder regulierte Branchen-Daten speichert — also fast jede MDM-Installation.

OP-37 · Codelisten-Governance (i18n, Versionierung, Cross-Mapping)

Status: V1 (Spec-Detaillierung ausstehend) · Verantwortlich: DDM_ENUM_SET / DDM_ENUM_VALUE, Validierung

V1-Entscheidung (2026-04-29): Workbench startet mehrsprachig (siehe OP-15). DDM_ENUM_VALUE.label wird zu label_i18n jsonb (Schema { "de": "...", "en": "..." }). DDM_ENUM_VALUE.parent_id (uuid, NULL = Top-Level) für Hierarchie. DDM_ENUM_VALUE.valid_from / valid_to für Versionierung. Cross-Mapping-Tabelle mdm.DDM_ENUM_VALUE_MAPPING (from_value_id, to_value_id, mapping_kind) ergänzend.

DDM_ENUM_SET / DDM_ENUM_VALUE sind heute einsprachig, unversioniert und stehen für sich allein. In Enterprise-Realität braucht man: Übersetzungen der Labels (Englisch, Deutsch, …), Versionierung der Codeliste mit Gültigkeitsfenster (Branchen-Codes ändern sich; alte Werte dürfen historisch nicht verschwinden), Hierarchien innerhalb der Codeliste (NACE-Codes haben 4 Ebenen) und Cross-Mapping zwischen Codelisten (NACE → ISIC → SIC).

Was fehlt: enum_value_translation (enum_value_id, locale, label), DDM_ENUM_VALUE.parent_id für Hierarchie, DDM_ENUM_VALUE.valid_from/valid_to für Versionierung, separate Tabelle enum_value_mapping (from_value_id, to_value_id, mapping_kind) für Cross-Mapping.

Wann relevant: sobald die UI mehrsprachig sein soll, oder externe Codelisten (Branchencode, Land, Währung, Maßeinheit) gepflegt werden, oder Reports mit Codelisten-Joins kommen.

OP-38 · Internationalisierung von Attributwerten

Status: V1 (Spec-Detaillierung ausstehend) · Verantwortlich: DDM_ENTITY, Trigger und Funktionen

V1-Entscheidung (2026-04-29): attribute_data_type='text_i18n' und 'text_i18n_long' als neue Datentypen — Inhalt jsonb { "de": "...", "en": "..." }, Default-Sprache aus AA_TENANT.metadata.locale. Volltextindex baut to_tsvector('<locale>', value) pro Sprache mit ||-Verkettung. DDM_ENTITY.name bleibt einsprachig (technischer Anzeigename), zusätzliche display_name_i18n als optionales Pattern-Attribut.

DDM_ENTITY.name und attributes sind einsprachig. Marketing-Texte, Produktbeschreibungen, Kategorienamen müssen oft mehrsprachig sein. Die JSONB-Lösung “halt drei Felder name_de, name_en, name_fr” skaliert nicht und macht Volltextsuche kompliziert.

Was fehlt: entweder ein konventionelles Schema in attributes ({ "name": { "de": "...", "en": "..." } }) mit Service-Layer-Konvention, oder eine separate Tabelle attribute_translation (entity_id, attribute_key, locale, value). Volltextindex muss locale-aware werden (to_tsvector('german', …) vs. 'english'). Hängt mit OP-15 zusammen.

Wann relevant: sobald die Anwendung in mehreren Sprachen erscheint, oder Produktdaten exportiert werden, oder mehrsprachige Reports nötig sind.

OP-39 · Maßeinheiten, Währungen, Geo

Status: V1 (money aktiv; quantity/currency/fx_rate/Geo Spec-Detaillierung ausstehend) · Verantwortlich: DDM_ENTITY_TYPE_ATTRIBUTE, Indizes, Validierung

Numerische Felder ohne Einheit sind eine Quelle subtiler Fehler (“Preis 100” — Euro? Cent? USD?). Genauso bei Geo-Daten (“Länge 50.1” — Grad? Radians?).

Gelöst (V1): attribute_data_type='money' mit JSON-Payload { "value": "<decimal-string>", "currency": "<ISO-4217>" }. Service-Layer-Validierung erzwingt String-Format, ISO-4217-Code, Skala je Währung (Detail siehe Validierung – money-spezifische Service-Layer-Prüfungen).

V1-Entscheidung (2026-04-29): alle drei Erweiterungen in V1 (Spec-Detaillierung ausstehend):

  • attribute_data_type='quantity' (Wert + Einheit) plus Stammtabelle mdm.DDM_UNIT_OF_MEASURE (key, label_i18n, dimension, base_unit_id, factor).
  • Stammtabelle mdm.DDM_CURRENCY (ISO-4217, Skala, label_i18n) — ersetzt die Regex-Validierung des Codes für money.
  • Wechselkurse mdm.DDM_FX_RATE (from_currency, to_currency, rate, valid_from, valid_to) für Cross-Currency-Aggregation. Bitemporal nutzbar (siehe OP-32).
  • Geo: point / polygon via PostGIS, GiST-Indizes; PostGIS-Extension wird bei Tenant-Provisioning aktiviert.

Wann relevant: in jeder produktbezogenen, internationalen oder location-basierten MDM-Installation.

OP-40 · Anhänge / Dokumente / Bilder

Status: gelöst · Verantwortlich: DDM_ATTACHMENT, Anhänge-Verhalten, Anhänge-API

Stammdatensätze haben oft Dokumente: Vertragsscan, Zertifikat, Produktbild, Datenblatt-PDF.

Gelöst (V1):

  • Tabelle DDM_ATTACHMENT (tenant-scoped, optionales entity_id, logical_key für versionierte Slots, version_no/is_current, sha256, storage_uri, virus_scan_status).
  • Storage in S3-kompatiblem Object-Store (V1: MinIO via Coolify; gleicher Adapter spricht später AWS S3 / R2 / B2). Tenant-Konfig in AA_TENANT.metadata.attachments.
  • Upload-Pfad Pre-Signed-URL (/attachments:initiate + direkter S3-PUT + /attachments:commit); Reservierung in AA_UPLOAD_SESSION.
  • Virus-Scan asynchron über neuen AA_JOB.job_kind='virus_scan'. Download blockiert bis clean/skipped.
  • Permission erbt von Entity (V1). Eigener acl_scope='attachment' und Klassifikation (public/internal/confidential) bleibt OP-36 vorbehalten.
  • Email-Anhang per FK: email_send-Payload kind='attachment_id' referenziert DDM_ATTACHMENT.id. Free-Text-storage_uri aus dem Email-Payload entfernt.
  • Append-only-Versionierung über (entity_id, logical_key, version_no) mit partiellem UNIQUE-Index für aktive Slot-Version.
  • Soft-Delete + Cleanup-Job entfernt Storage-Object nach Tenant-Retention. Hard-Delete (:purge) manage_metadata-only.
  • Audit erweitert um attachment_uploaded/scanned/deleted/restored/hard_deleted mit metadata.storage_uri + metadata.sha256.
  • Tenant-Konsistenz per Trigger mdm.validate_attachment_tenant.

Bewusst nicht V1: retention_until, legal_hold, Klassifikationsschema (public/internal/…) — gehören zu OP-36 (Governance-Metadaten) und werden dort konsistent über alle Tabellen ergänzt.

OP-41 · Bulk-Import / Staging / ETL-Eingangskanal

Status: V1 (Spec-Detaillierung ausstehend) · Verantwortlich: REST API, Validierung, AA_AUDIT_LOG

V1-Entscheidung (2026-04-29): Erst-Befüllung aus Excel/Altsystemen ist V1-Pflicht. Tabellen mdm.AA_IMPORT_JOB (id, tenant_id, source, started_by, status, mode: validate_only|dry_run|commit, total_rows, error_rows, started_at, finished_at) und mdm.AA_IMPORT_ROW (job_id, row_no, raw jsonb, status, error_code, error_message, target_entity_id). Job läuft als AA_JOB.job_kind='import'. Wieder-Aufnahme nach Abbruch über row_no-Cursor. Fehlerhafte Zeilen blockieren nicht den Job; UI zeigt Fehler-Report mit Zeilennummern.

Erst-Befüllung und regelmäßige Synchronisation aus Altsystemen brauchen einen Staging-Bereich: rohe Daten landen erst in import_staging, werden dort auf Duplikate / Validität geprüft, fehlerhafte Datensätze blockieren nicht den ganzen Job, der Anwender bekommt einen Fehler-Report mit Zeilennummern.

Was fehlt: Tabellen import_job (id, source, started_by, status, total_rows, error_rows, started_at, finished_at) und import_row (job_id, row_no, raw jsonb, status, error_message, target_entity_id). Service-Layer mit Modi validate_only / dry_run / commit. Wieder-Aufnahme nach Abbruch.

Wann relevant: bei jeder Migration aus Altsystemen, jeder Excel-Massenpflege durch Fachanwender, jeder regelmäßigen Datei-Synchronisation.

OP-42 · Feldverschlüsselung / Maskierung

Status: V1 (Spec-Detaillierung ausstehend) · Verantwortlich: DDM_ENTITY_TYPE_ATTRIBUTE, Autorisierung

V1-Entscheidung (2026-04-29): Pro Attribut opt-in aktivierbar via DDM_ENTITY_TYPE_ATTRIBUTE.encrypt_at_rest boolean DEFAULT false mit KMS-Key-Referenz kms_key_ref text (V1: AES-256-GCM mit Coolify-Secret oder externem KMS). Bei encrypt_at_rest=true schreibt der Service das Attribut chiffriert in attributes ({ "ciphertext": "...", "key_ref": "..." }); Service entschlüsselt vor Auslieferung. Maskierung über mask_strategy: full|partial|last4|none plus ACL-Bindung — nur Principals mit Permission mdm.attribute:read_clear sehen Klartext, alle anderen die maskierte Form. Volltextindex enthält nur die maskierte Form.

Sensible Felder (IBAN, Sozialversicherungsnummer, Gehalt) sollten at rest verschlüsselt sein und für die meisten Rollen maskiert angezeigt werden (“DE89****0532”). Heutiges Modell speichert alles im Klartext und delegiert an Postgres-TDE.

Was fehlt: pro Attribut ein Flag encrypt_at_rest mit KMS-Key-Referenz und ein Mask-Profile (mask_strategy: full | partial | last4 | none) mit ACL-Bindung („nur Rolle hr sieht Klartext, alle anderen partial”). Service-Layer wendet das an, bevor das Feld die Datenbank verlässt.

Wann relevant: sobald Finanz-, Personal- oder Gesundheitsdaten gespeichert werden — DSGVO Art. 32 verlangt “Verschlüsselung wo angemessen”.

OP-43 · Match-Index / Fuzzy-Suche

Status: V1 (Spec-Detaillierung ausstehend) · Verantwortlich: Suche und Filterung, Indizes

V1-Entscheidung (2026-04-29): pg_trgm-Extension wird beim Tenant-Provisioning aktiviert. GIN-trgm-Indizes auf lower(name) und konfigurierbar pro DDM_ENTITY_TYPE_ATTRIBUTE.searchable=true über neues Flag fuzzy boolean DEFAULT false. UI-Autocomplete und Match/Merge-Engine (OP-29) nutzen diese Indizes. Externer Index (OpenSearch / Elastic) bleibt OP-43a (V2+).

tsvector ist gut für Volltext, aber schwach für Match/Merge (OP-29) und für UX-Suchen wie “Mueller” findet “Müller” / “Mueller GmbH” / “Mller”. Dafür braucht es Trigram-Index (pg_trgm) und/oder externen Index (OpenSearch, Elastic).

Was fehlt: Aktivierung pg_trgm, GIN-trgm-Indizes auf lower(name) und konfigurierbar auf einzelne Attribute. Optional Outbox-Worker, der Änderungen in einen externen Index repliziert (OP-43a).

Wann relevant: sobald Anwender selbst suchen (UI-Autocomplete) oder Match/Merge implementiert wird.

OP-44 · Materialized Views / Read Models / Reporting-Layer

Status: offen — Auftraggeber stellt Frage zurück · Verantwortlich: Views, SQL / Reporting

Stand 2026-04-29: Reporting-Schema vs. Materialized Views vs. API-Layer ist absichtlich offen, bis BI-Anbindung konkretisiert wird. V1 liefert Reports (OP-04) über dedizierte REST-Endpunkte mit dynamischer Query-Layer-Projektion (OP-16). Bei Lag-/Throughput-Druck Materialized Views nachziehen.

REST-API-basiertes Reporting ist operativ ausreichend für V1-Volumina. Für BI / Dashboards mit Millionen Records ist eine separate Aggregationsschicht nötig. Klassische Lösung: Reporting-Schema (Star/Snowflake) oder materialisierte Views mit Refresh-Strategie, gespeist aus der Outbox.

Was fehlt: Konvention mdm_reporting-Schema mit Fact-/Dimension-Tabellen, Refresh-Worker, Definition welche Views materialisiert werden, Lag-SLO (z. B. „Reports sind ≤ 5 Minuten alt”).

Wann relevant: sobald BI-Tools angeschlossen werden, die nicht direkt auf der OLTP-DB laufen sollen.

OP-45 · Tag- / Label-System / gespeicherte Suchen

Status: offen (V2-Kandidat, nicht entschieden) · Verantwortlich: DDM_ENTITY, Suche und Filterung

Anwender wollen Records ad-hoc gruppieren ohne Schemaänderung: “Marketing-Liste Frühjahr 2026”, “Pilotkunden”, “Risiko-Watchlist”. Das überlappt nicht mit DDM_ENTITY_TYPE (Typ ist fest), nicht mit DDM_ENUM_VALUE (das ist Wertvorrat eines Attributs) und nicht mit DDM_RELATION_TYPE (das ist eine fachliche Beziehung).

Was fehlt: einfaches tag (id, key, label, created_by) plus entity_tag (tag_id, entity_id) mit Permission-Modell (wer darf wessen Tags sehen). Optional gespeicherte Suchen (saved_search mit Filterausdruck), die in der UI als Smart-List erscheinen.

Wann relevant: sobald die Anwendergruppe wächst und über reine CRUD hinaus arbeitet.

OP-46 · Schema-Evolution / Attribut-Lifecycle

Status: deferred — „wenn es soweit ist” · Verantwortlich: DDM_ENTITY_TYPE_ATTRIBUTE, Validierung

Entscheidung (2026-04-29): Kein dedizierter Lifecycle in V1. Schema-Drift wird über Lazy-Validate (OP-19) absorbiert; Drift-Sichtbarkeit über mdm.v_entity_attribute_drift. Vollständiger Lifecycle (deprecated_at, replaced_by_attribute_id, migration_hint, attribute_change_log) wird ausgearbeitet, sobald die erste echte Migration gefordert ist.

Wenn ein Attribut umbenannt, ersetzt oder als deprecated markiert wird: was passiert mit historischen Datensätzen? Aktuell wird das Schema einfach geändert, alte Daten bleiben unter altem Key liegen, niemand weiß noch warum.

Was fehlt: DDM_ENTITY_TYPE_ATTRIBUTE.deprecated_at, replaced_by_attribute_id, migration_hint (Service-Layer-Code, der Werte rüberkopiert). Plus eine attribute_change_log-Tabelle, die jede Änderung an DDM_ENTITY_TYPE_ATTRIBUTE zur späteren Rekonstruktion festhält. Hängt eng mit OP-19 zusammen.

Wann relevant: ab dem Tag 2 nach Produktiv-Setzung — Schema ändert sich immer früher als gedacht.

OP-47 · Plausibilitäts-/Geschäftsregel-Engine

Status: separat abgebildet — zurückgestellt · Verantwortlich: DDM_ENTITY_TYPE_ATTRIBUTE, Validierung

Entscheidung (2026-04-29): Cross-Entity-/Cross-Field-Regeln laufen V1 ad-hoc im Service-Layer. Eine deklarative Engine (CEL, siehe OP-14) wird als V2-Modul gespect, sobald genug konkrete Regeln vorliegen. V1 deckt Pflicht/Type/Enum/Min/Max/Regex über die Basis-Validierung ab.

validation_rule jsonb existiert, aber die Sprache / Engine ist nicht definiert (OP-14). Enterprise-MDM unterstützt typischerweise deklarative Regeln (“wenn country=DE dann muss tax_id Format DE\d{9} haben”), abhängige Pflichtfelder (“wenn customer_type=enterprise dann ist vat_id Pflicht”) und Cross-Entity-Regeln (“Summe der share_percent über alle Verantwortlichkeitsbeziehungen eines Kunden = 100”).

Was fehlt: Definition der Regelsprache (JsonLogic, CEL, Common Expression Language, oder eigenes DSL), Auswertungsstelle (Service-Layer, optional DB-Trigger für harte Cross-Entity-Regeln), Fehler-Reporting mit Regel-ID.

Wann relevant: sobald fachliche Plausibilitätsregeln über Pflicht/Datentyp/Enum/Min/Max hinausgehen.

OP-48 · Entity-Lebenszyklus / Status-Workflow

Status: V1 (Spec-Detaillierung ausstehend) · Verantwortlich: DDM_ENTITY, Autorisierung

V1-Entscheidung (2026-04-29): Pro DDM_ENTITY_TYPE konfigurierbare State-Machine über mdm.DDM_STATUS_TRANSITION (entity_type_id, from_status, to_status, required_role, requires_approval). Service prüft beim Status-Wechsel, ob der Principal die Transition hat; requires_approval=true ist für V1 reserviert (Approval-Engine kommt mit OP-35 in V2 — bis dahin verhalten sich solche Transitionen wie required_role-only). Default-Set (draft → active → inactive → archived) wird beim Tenant-Provisioning angelegt und ist editierbar.

DDM_ENTITY.status ist heute ein freier Enum (draft, active, inactive, archived). Übergänge sind nicht modelliert: jeder mit Update-Permission kann jeden Status auf jeden anderen setzen.

Was fehlt: pro DDM_ENTITY_TYPE eine konfigurierbare State Machine (status_transition (entity_type_id, from_status, to_status, required_role, requires_approval)). Service prüft beim Status-Wechsel, ob der aktuelle Principal die Transition machen darf, und ob Approval (OP-35) nötig ist.

Wann relevant: sobald fachliche Lebenszyklen modelliert werden (Vertrag „Entwurf → eingereicht → freigegeben → aktiv → gekündigt”).

OP-49 · Materialisierte Effektive-Permissions als Read-Cache

Status: deferred — erst wenn OP-24 (Redis-Cache) nicht reicht · Verantwortlich: Autorisierung, Views

Entscheidung (2026-04-29): V1 nutzt Redis-Session-Cache (OP-24). DB-materialisierter effective_permission_cache mit Trigger-Invalidierung wird erst aktiviert, wenn (a) Redis nicht verfügbar oder (b) cold-start-Latenz auf großen Tenants signifikant ist. Re-Bewertung anhand der V1-Performance-Ziele (OP-07).

v_effective_permission ist ein View. Bei jeder Lese-Anfrage wird er ausgewertet, was bei großem RBAC/ACL-Bestand teuer ist. Klassische MDM-Lösung: ein vorberechneter Read-Cache (Materialized View oder Tabelle) pro Principal, der bei Permission-Änderung invalidiert wird.

Was fehlt: Tabelle effective_permission_cache (principal_type, principal_id, action, scope, scope_*, attribute_key, effect, computed_at) plus Invalidierungs-Trigger auf AA_ROLE_PERMISSION, AA_PRINCIPAL_ROLE, AA_ACL_ENTRY, AA_PRINCIPAL_GROUP_MEMBER. Hängt mit OP-24 zusammen, geht aber weiter (persistenter Cache vs. Request-Cache).

Wann relevant: sobald Auth-Performance Bottleneck wird — typischerweise bei > 10k Records pro Liste oder > 1k Permission-Einträgen.

OP-50 · API-Contracts / OpenAPI-Versionierung

Status: V1 (Spec-Detaillierung ausstehend) · Verantwortlich: REST API

V1-Entscheidung (2026-04-29): Alle REST-Routen liegen unter /api/v1/... ab Tag 1. OpenAPI-Spec ist Build-Artefakt (Backend-Build erzeugt openapi.yaml als CI-Asset). Breaking-Changes laufen über parallele Major-Version (/api/v2/...). Sunset-Policy: deprecated Major-Versionen bleiben mindestens 6 Monate parallel verfügbar; Deprecation-Header (Sunset, Deprecation) werden gesetzt.

REST-Schemata werden sich entwickeln. Klassische Probleme: Breaking-Change ohne Versionsnummer, fehlende OpenAPI-Spec, kein Changelog. Enterprise-MDM bringt typischerweise /api/v1/..., /api/v2/... parallel, deprecation-Header und automatisch generierte Client-SDKs.

Was fehlt: API-Versions-Strategie, OpenAPI-Spec als Build-Artefakt, Sunset-Policy für alte Versionen.

Wann relevant: sobald externe Konsumenten an die API angeschlossen werden — spätestens bei der ersten Drittpartei-Integration.