# 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.