Co-authored-by: Cursor <cursoragent@cursor.com>
16 KiB
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
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:
- Autoritative ID-Liste — Was existiert, was ist erlaubt, in welcher Reihenfolge (optional).
- Metadaten für Mensch & Maschine — Titel, Beschreibung, Typen, Abhängigkeiten, semantische Verträge.
- Validierung — Unbekannte IDs werden abgelehnt; Konfigurationen gegen Kanon geprüft.
- Entkopplung — Implementierung (Resolver, React-Komponente, Import-Executor) registriert sich an den Kanon, nicht umgekehrt.
- 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
-
Parallele Runtime-Maps —
PLACEHOLDER_MAP+ Registry; eine Quelle für Keys und Resolver-Pfad. -
Frontend-Registry ohne Build-/Test-Gate — fehlende
registerDashboardWidget-Einträge erst zur Laufzeit sichtbar. -
Metadaten-Duplikation in Export-Code — hardcodierte Beschreibungen außerhalb der Registry.
-
Registry-Einträge ohne Validierungs-Tests — besonders bei 100+ Platzhaltern.
-
Import-Feldlisten in Routern — alles über
module_registry(Mitai-Zielbild, noch nicht überall). -
Evidence-/Metadata-Pflicht für einfache Plugins — 22 Felder für Widgets wären Overkill; Metadaten-Tiefe an Risiko anpassen.
-
Dynamische Registry aus DB ohne Versionierung — Widget-Feature-Overrides OK; kompletter Kanon nur in DB wäre schwer testbar.
-
Registry ohne Deprecation-Pfad — Breaking Key-Changes still (Platzhalter-Governance existiert, durchsetzen).
-
Entitlements in Registry auflösen — Widgets richtig: referenzieren; nicht Tier-Logik im Katalog.
-
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, PLACEHOLDER_GOVERNANCE.md
- Widgets: DASHBOARD_WIDGETS_AGENT_GUIDE.md
- Import: UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md
- Entitlements (Widget-Gating): FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md
- Data Layer (Platzhalter-Berechnung): DATA_LAYER_DESIGN_PRINCIPLES.md
- Prompt Engine (Platzhalter-Konsument): 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.