Kairo-Jinkendo/docs/reference/design-principles/mitai/REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md

316 lines
16 KiB
Markdown
Raw Normal View History

# Registry- & Plugin-Muster – Designprinzipien (Extraktion)
**Status:** Analyse / Arbeitspapier
**Stand:** 2026-07-04
**Geltungsbereich:** Querschnittsmuster für erweiterbare Registries — drei Implementierungen in Mitai (Platzhalter, Dashboard-Widgets, CSV-Import)
**Serie:** Designprinzipien für Produktfamilie · Dokument 4 von n
**Vorgänger:** [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md)
**Die drei Registries:**
| Registry | Backend-Kanon | Frontend-/Runtime-Registry | Leitfaden |
|----------|---------------|----------------------------|-----------|
| **Platzhalter** | `placeholder_registry.py` + `placeholder_registrations/` | `PLACEHOLDER_MAP` in `placeholder_resolver.py` | `PLACEHOLDER_REGISTRY_FRAMEWORK.md` |
| **Dashboard-Widgets** | `widget_catalog.py` | `registerDashboardWidgets.js` → `dashboardWidgetRegistry.jsx` | `DASHBOARD_WIDGETS_AGENT_GUIDE.md` |
| **CSV-Import-Module** | `csv_parser/module_registry.py` | — (Executor + Admin-UI konsumieren API) | `UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` |
---
## Modul
**Registry- & Plugin-Muster** (Meta-Schicht)
Wiederkehrendes Architekturmuster: **Zentral registrierte, ID-basierte Erweiterungspunkte** mit Metadaten, Validierung und getrennten Konsumenten (GUI, API, Batch). Kein einzelnes Runtime-Modul — ein **Familien-Designpattern**, das in Mitai dreimal konkret umgesetzt ist.
---
## Fachliche Verantwortung
Registries übernehmen:
1. **Autoritative ID-Liste** — Was existiert, was ist erlaubt, in welcher Reihenfolge (optional).
2. **Metadaten für Mensch & Maschine** — Titel, Beschreibung, Typen, Abhängigkeiten, semantische Verträge.
3. **Validierung** — Unbekannte IDs werden abgelehnt; Konfigurationen gegen Kanon geprüft.
4. **Entkopplung** — Implementierung (Resolver, React-Komponente, Import-Executor) registriert sich an den Kanon, nicht umgekehrt.
5. **Erweiterbarkeit ohne Schema-Explosion** — Neue Einträge über Code-Registrierung (+ ggf. DB-Overrides), nicht über neue DB-Spalten pro Feature.
Registries übernehmen **nicht**:
- Fachliche Berechnung (→ Data Layer)
- Entitlement-Auflösung (→ Feature System; Widgets *referenzieren* Features)
- Auth / Mandanten
### Gemeinsames Strukturschema
```
┌─────────────────────────────────────────────────────────┐
│ REGISTRY (Kanon) │
│ ID + Metadaten + optionale Policies/Abhängigkeiten │
└────────────────────┬────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
Implementierung Validierung Konsumenten
(Resolver/ (Schema/ (GUI-Picker,
Component/ Tests) API, Executor)
Executor)
```
### Vergleich der drei Implementierungen
| Aspekt | Platzhalter | Dashboard-Widgets | CSV-Import |
|--------|-------------|-------------------|------------|
| **Primär-ID** | `key` (snake_case) | `id` (snake_case) | Modul-Name (`nutrition`, `activity`, …) |
| **Kanon-Speicher** | Python Singleton + Cluster-Module | Python-Liste `WIDGET_CATALOG` | Python-Dict `MODULE_DEFINITIONS` |
| **Metadaten-Tiefe** | Sehr hoch (22+ Felder, Evidence) | Mittel (title, description, requires_feature) | Hoch (fields, types, duplicate_key, aggregates) |
| **Runtime-Registry** | `register_placeholder()` beim Import | `registerDashboardWidget()` idempotent | Keine — Executor liest Dict |
| **Frontend-Spiegel** | PlaceholderPicker, Admin-Prompt-Modal | `ensureDashboardWidgetsRegistered()` | Admin CSV Template Editor |
| **Validierung** | `metadata.validate()`, Governance-Docs | Pydantic Layout + `validate_widget_entry_config` | `validate_field_mappings`, `validate_csv_template` |
| **Tests** | `test_placeholder_metadata.py`, … | `test_widget_catalog.py` | `test_template_validator.py`, … |
| **DB-Override** | Nein (nur Code) | Ja (`widget_feature_requirements`, Layout pro Profil) | Ja (Vorlagen, Nutzer-Mappings) |
| **Entitlements** | Indirekt (Data/Features) | `requires_feature` → `check_feature_access` | Feature-Limits beim Import |
---
## Designprinzipien (übergreifend)
### 1. Single Source of Truth für erlaubte IDs
| | |
|---|---|
| **Prinzip** | Jede erweiterbare Einheit hat **eine** autoritative ID-Liste; Router und UI duplizieren keine Feld-/Widget-/Platzhalter-Listen. |
| **Begründung** | Verhindert „funktioniert in der UI, scheitert in der API“ und umgekehrt. |
| **Quelle** | `module_registry.py` Kommentar; `widget_catalog.py`; `PLACEHOLDER_REGISTRY_FRAMEWORK.md` §2.3 |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Platzhalter: paralleles `PLACEHOLDER_MAP` neben Registry. |
### 2. ID-Stabilität als Vertrag
| | |
|---|---|
| **Prinzip** | IDs/Keys sind stabile API-Verträge; Umbenennung = neuer Key + Deprecation, nicht stilles Rename. |
| **Begründung** | Prompts, Layouts und CSV-Vorlagen referenzieren IDs persistent. |
| **Quelle** | `docs/PLACEHOLDER_GOVERNANCE.md` §4.2–4.3; Widget-Layout in `profiles.dashboard_layout` |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Nicht überall runtime-erzwungen (Platzhalter-Governance prozessual). |
### 3. Metadaten getrennt von Implementierung
| | |
|---|---|
| **Prinzip** | Registry speichert **Was** (ID, Beschreibung, Typ, Policies); Implementierung lebt in separaten Modulen. |
| **Begründung** | GUI, Export, Validierung und Docs können Metadaten nutzen ohne Resolver/Component zu laden. |
| **Quelle** | `PlaceholderMetadata` Dataclass; `WidgetCatalogEntry`; `MODULE_DEFINITIONS.fields` |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Platzhalter bindet `_resolver_func` optional an Metadata-Objekt. |
### 4. Zwei-Phasen-Registrierung (Backend-Kanon + Runtime-Binding)
| | |
|---|---|
| **Prinzip** | Phase A: Kanon definiert IDs und Metadaten. Phase B: Implementierung registriert sich (Platzhalter-Cluster-Import, `registerDashboardWidget`, Executor nutzt Modul-Def). |
| **Begründung** | Backend bleibt autoritativ; Frontend/plugins können nachziehen, Tests können Lücken finden. |
| **Quelle** | `import placeholder_registrations` in `main.py`; `ensureDashboardWidgetsRegistered()` |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Fehlende Frontend-Registrierung zeigt „Unbekanntes Widget“ — kein Build-Time-Fail. |
### 5. Auto-Registration via Package-Import
| | |
|---|---|
| **Prinzip** | Side-Effect-Import eines Packages triggert vollständige Registrierung (`placeholder_registrations/__init__.py`). |
| **Begründung** | Keine vergessene manuelle Registrierungsliste in `main.py` pro Eintrag. |
| **Quelle** | `placeholder_registrations/__init__.py`; `main.py` |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Import-Reihenfolge und zirkuläre Imports beachten. |
### 6. Validierung am Registry-Rand
| | |
|---|---|
| **Prinzip** | Unbekannte Keys/Widget-IDs/Feld-Mappings werden an Registry-Grenzen abgewiesen, nicht erst in der Business-Logik. |
| **Begründung** | Frühes, klares Fehlerbild für Admins und Entwickler. |
| **Quelle** | `DashboardWidgetEntry` + `ALLOWED_WIDGET_IDS`; `validate_field_mappings()`; `get_unknown_placeholders()` |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Prompt-Templates können unbekannte Platzhalter erst zur Laufzeit offenbaren. |
### 7. Konsumenten-Agnostik
| | |
|---|---|
| **Prinzip** | Dieselbe Registry bedient mehrere Konsumenten (KI, Charts, Export, Admin-Browser) ohne duplizierte Metadaten. |
| **Begründung** | DRY für Beschreibungen, Kategorien, Beispielwerte. |
| **Quelle** | `PLACEHOLDER_REGISTRY_FRAMEWORK.md` §2.2; `get_placeholder_catalog()` |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Legacy-Export-Pfade mergen noch „Registry + Legacy“. |
### 8. Erweiterungs-Checkliste statt Ad-hoc
| | |
|---|---|
| **Prinzip** | Jedes Registry hat dokumentierte Schritte A→G (Katalog-Eintrag, Validierung, Tests, Version-Bump). |
| **Begründung** | Agenten und Menschen erweitern konsistent; Review an Checkliste. |
| **Quelle** | `DASHBOARD_WIDGETS_AGENT_GUIDE.md` §2; `UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` §2; `PLACEHOLDER_DEVELOPMENT_GUIDE.md` |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Drei separate Guides — kein unified „Registry Agent Guide“. |
### 9. Tests auf Katalog-Konsistenz
| | |
|---|---|
| **Prinzip** | Automatisierte Tests prüfen Eindeutigkeit, Reihenfolge, Payload-Shape, ID-Abgleich Kanon ↔ abgeleitete Sets. |
| **Begründung** | Regression wenn Katalog wächst but Registry/Layout nicht mitzieht. |
| **Quelle** | `test_widget_catalog.py`; Placeholder-Metadata-Tests |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Kein Cross-Registry-Test „Frontend widget IDs == backend catalog“. |
### 10. Optionale Entitlement-Referenz im Kanon
| | |
|---|---|
| **Prinzip** | Registry-Einträge **referenzieren** Feature-IDs (`requires_feature`), lösen Entitlements aber nicht selbst auf. |
| **Begründung** | Tier-Logik bleibt in `check_feature_access`; Katalog bleibt deklarativ. |
| **Quelle** | `WidgetCatalogEntry.requires_feature`; `dashboard_widget_entitlements.py` |
| **Tragfähigkeit** | **hoch** |
| **Einschränkung** | Platzhalter haben kein direktes `requires_feature` — Gating nur indirekt. |
---
## Designprinzipien (spezifisch pro Registry)
### Platzhalter-Registry
| # | Prinzip | Tragfähigkeit | Schwäche |
|---|---------|---------------|----------|
| P1 | **Semantischer Vertrag** (`semantic_contract`) pro Key — Platzhalter sind API, nicht Prompt-Hilfe | hoch | Viele Legacy-Keys mit schwachem Vertrag |
| P2 | **Evidence-Tagging** — Herkunft jedes Metadatenfelds nachvollziehbar | mittel | Pflegeaufwand |
| P3 | **Cluster-Module** — Registrierung nach Domäne (`nutrition_part_a`, `body_metrics`, …) | hoch | 114 Keys Sync mit `PLACEHOLDER_MAP` |
| P4 | **Data-Layer-Referenz** in Metadata (`data_layer_function`) — Bindung an Layer 1 | hoch | Nicht alle Keys vollständig verknüpft |
| P5 | **Singleton** `get_registry()` — globaler Kanon | hoch | Test-Isolation braucht Disziplin |
### Dashboard-Widget-Registry
| # | Prinzip | Tragfähigkeit | Schwäche |
|---|---------|---------------|----------|
| W1 | **Backend-Katalog = ALLOWED_WIDGET_IDS** — Layout-Schema leitet ab | hoch | Frontend-Registry manuell parallel |
| W2 | **`merge_missing_catalog_widgets`** — neue Katalog-IDs erscheinen im Layout ohne User-Reset | hoch | — |
| W3 | **Strikte `config`-Validierung** nur für whitelisted Widgets (`WIDGETS_ALLOWING_CONFIG`) | hoch | Config-Schemas pro Widget heterogen |
| W4 | **Graceful Degradation** — unregistrierte ID → Fehler-Karte, nicht Crash | mittel | Maskiert Deploy-Fehler |
| W5 | **WidgetErrorBoundary** pro Instanz | hoch | — |
### CSV-Modul-Registry
| # | Prinzip | Tragfähigkeit | Schwäche |
|---|---------|---------------|----------|
| C1 | **`MODULE_DEFINITIONS` = einzige Feldliste** — Router duplizieren nicht | hoch | Activity erweitert dynamisch um `training_parameters` |
| C2 | **Deklarative Duplikat-Strategie** (`duplicate_key`, `update`/`skip`) | hoch | Modul-spezifische Executor-Sonderfälle |
| C3 | **`import_row_processing`** — Aggregation in Registry, nicht im Router | hoch | Legacy-Defaults in Modul-Def |
| C4 | **`validate_field_mappings`** vor Persistenz | hoch | Nutzer-Mappings teils ohne volle Validator-Parität (#71) |
| C5 | **Persistenz-Orchestrator** liest Registry-Felder (`activity_persistence_orchestrator`) | hoch | Nur Activity voll ausgebaut |
---
## Anti-Pattern: Doppel-Registry
Mitai zeigt an **Platzhaltern** das Risiko explizit:
```
placeholder_registrations/ ──register──► PlaceholderRegistry (Metadata)
│ ▲
└── resolver in code ──► PLACEHOLDER_MAP (Runtime, 114 Keys)
```
**Regel für Produktfamilie:** Runtime-Auflösung soll Metadata-Registry **lesen**, nicht spiegeln.
Widgets sind näher am Ideal: Backend `WIDGET_CATALOG` ist Kanon; Frontend muss IDs manuell in `registerDashboardWidgets.js` binden — akzeptabel, aber testbar machen (Cross-Check).
---
## Nicht übernehmen
1. **Parallele Runtime-Maps** — `PLACEHOLDER_MAP` + Registry; eine Quelle für Keys und Resolver-Pfad.
2. **Frontend-Registry ohne Build-/Test-Gate** — fehlende `registerDashboardWidget`-Einträge erst zur Laufzeit sichtbar.
3. **Metadaten-Duplikation in Export-Code** — hardcodierte Beschreibungen außerhalb der Registry.
4. **Registry-Einträge ohne Validierungs-Tests** — besonders bei 100+ Platzhaltern.
5. **Import-Feldlisten in Routern** — alles über `module_registry` (Mitai-Zielbild, noch nicht überall).
6. **Evidence-/Metadata-Pflicht für einfache Plugins** — 22 Felder für Widgets wären Overkill; **Metadaten-Tiefe an Risiko anpassen**.
7. **Dynamische Registry aus DB ohne Versionierung** — Widget-Feature-Overrides OK; kompletter Kanon nur in DB wäre schwer testbar.
8. **Registry ohne Deprecation-Pfad** — Breaking Key-Changes still (Platzhalter-Governance existiert, durchsetzen).
9. **Entitlements in Registry auflösen** — Widgets richtig: referenzieren; nicht Tier-Logik im Katalog.
10. **Schlaf-Modul leeres `fields: {}`** — Sondermodus (`import_mode`) statt sauberem Registry-Eintrag; technische Schuld.
---
## Entscheidungsmatrix: Welche Registry-Tiefe?
| Wenn … | dann Metadaten-Tiefe … | Beispiel |
|--------|------------------------|----------|
| Externe Verträge / KI / Compliance | Hoch (Vertrag, Evidence, Missing-Policy) | Platzhalter |
| UI-Plugin mit optionaler Config | Mittel (ID, title, config schema, feature ref) | Widgets |
| Daten-Ingest / Schema-Mapping | Hoch (Typen, keys, constraints) | CSV-Module |
| Internes Hilfsmodul | Minimal (ID + Handler-Ref) | — |
---
## Modul-Inventar (Querschnitt)
```
backend/
├── placeholder_registry.py
├── placeholder_registrations/ # Auto-import Cluster
├── placeholder_resolver.py # PLACEHOLDER_MAP (Legacy-Spiegel)
├── placeholder_registry_export.py
├── widget_catalog.py
├── dashboard_layout_schema.py
├── dashboard_widget_config.py
├── dashboard_widget_entitlements.py
├── widget_feature_requirements_db.py
└── csv_parser/
└── module_registry.py
frontend/src/
├── widgetSystem/
│ ├── registerDashboardWidgets.js
│ └── dashboardWidgetRegistry.jsx
└── components/workflow/panels/PlaceholderPicker.jsx
```
---
## Verwandte Dokumentation
- Platzhalter: [PLACEHOLDER_REGISTRY_FRAMEWORK.md](../../technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md), [PLACEHOLDER_GOVERNANCE.md](../../../../docs/PLACEHOLDER_GOVERNANCE.md)
- Widgets: [DASHBOARD_WIDGETS_AGENT_GUIDE.md](../../technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md)
- Import: [UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md](../../technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md)
- Entitlements (Widget-Gating): [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md)
- Data Layer (Platzhalter-Berechnung): [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md)
- Prompt Engine (Platzhalter-Konsument): [PROMPT_ENGINE_DESIGN_PRINCIPLES.md](./PROMPT_ENGINE_DESIGN_PRINCIPLES.md)
---
## Geplante Folgedokumente (Serie)
| # | Modul | Status |
|---|-------|--------|
| 1 | Prompt Engine | ✅ |
| 2 | Data Layer | ✅ |
| 3 | Feature & Entitlement | ✅ |
| 4 | Registry-/Plugin-Muster | ✅ dieses Dokument |
| 5 | Auth & Session | ✅ `AUTH_SESSION_DESIGN_PRINCIPLES.md` |
| 6 | Universal Import | ✅ |
| 7 | Dashboard Widgets | ✅ |
| 8 | Navigation / IA | ✅ |
| 9 | Migration & Deploy | ✅ |
*Hinweis:* Dokumente 6 und 7 vertiefen Einzel-Registries; dieses Meta-Dokument ist die übergreifende Extraktion.