Skip to content

Feature-Prinzipien

Alles ist ein Feature

Auth, Tenant, User, Audit, Search, Jobs, Config, Custom Fields — alles sind Features. Es gibt keine “magische” Core-Logik neben dem Feature-System. Der Core liefert nur die Infrastruktur (Engine, Pipeline, Dispatcher, DB, Search-Adapter, SSE), nicht Geschaefts-Semantik.

Folgen:

  • Neue Funktionalitaet wird immer als Feature gebaut, nie als Sonderfall im Core.
  • Wenn etwas im Core zu landen scheint, ist das ein Signal: fehlt ein Extension-Point?
  • Core Features (packages/framework/src/features/) folgen derselben defineFeature()-API wie App Features. Der einzige Unterschied ist ihr Speicherort und dass andere Features sie via requires() einbinden koennen.

Features sind maximal entkoppelt

Ein Feature kennt idealerweise nur sich selbst. Abhaengigkeiten werden explizit und sparsam deklariert.

Werkzeuge zur Entkopplung:

MittelWofuer
r.requires("feature")Harte Abhaengigkeit — Boot schlaegt fehl wenn fehlend
r.optionalRequires("feature")Weiche Abhaengigkeit — Integration nur wenn geladen
r.extendsRegistrar()Extension-Point fuer andere Features
Events (r.defineEvent(), Listener)Cross-Feature-Kommunikation ohne Import
Registrar-Extensions (r.customFields(), r.tags(), …)Opt-in fuer Querschnittsfunktionen

Nicht erlaubt:

  • Direkte Imports aus anderen Features (jenseits definierter Extension-APIs)
  • “Weiss-Features” im Core, die Feature-Namen kennen
  • Hardcoded Spezialfaelle fuer bestimmte Entities

Feature an/aus darf nichts kaputt machen

Jedes Feature muss so gebaut sein, dass das Entfernen es aus der Feature-Liste die uebrigen Features nicht bricht. Das heisst konkret:

  • Keine ALTER TABLE auf fremden Entities (eigene Tabellen sind ok)
  • Keine globalen Mutationen an Core-Verhalten beim Laden
  • optionalRequires + defensive if (registry.has(...))-Checks
  • Saubere Cleanup-Hooks fuer eigene Daten

Wann Framework, wann Core-Feature, wann App-Feature

Entscheidungsregel bei neuem Plan:

Framework-Infrastruktur

Code der nicht via defineFeature() ausgedrueckt werden kann und immer laeuft — unabhaengig davon welche Features geladen sind.

Kriterien (eines reicht):

  • Wirkt auf jeden Request, nicht nur auf Features die opt-in sagen
  • Aendert Kern-Pipeline (Engine, Dispatcher, Lifecycle, DB-Layer, Event-Transport)
  • Andere Features koennen nicht korrekt arbeiten wenn es fehlt — aber es ist kein Feature zum “required”, sondern Baustein

Beispiele: Engine, Dispatcher, Registry, Tenant-DB-Context, Event-Transport (Outbox), Transactions, Lifecycle-Pipeline.

Wohnt in packages/framework/src/engine/, pipeline/, db/, api/, etc. — nie in features/.

Core-Feature

Normales Feature via defineFeature(), das jede Kumiko-Deployment typischerweise braucht.

Kriterien:

  • Per defineFeature() ausdrueckbar (Entities, Handler, Hooks, Events)
  • Von vielen App-Features als Abhaengigkeit genutzt (Auth, Config, Tenant, Secrets, Jobs, Files)
  • Bleibt entfernbar — Apps die es nicht brauchen schalten es ab
  • Compliance- oder Sicherheits-Basis, die man nicht jeder App einzeln aufdraengen will

Beispiele: core-auth, core-config, core-tenant, core-files, core-jobs, core-secrets, core-rate-limiting, core-tenant-export.

Wohnt in packages/framework/src/features/. Nutzt die gleiche defineFeature()-API wie App-Features.

App-Feature

Feature der konkreten App — Geschaeftslogik des Kunden.

Beispiele: orders, invoicing, fleet, mietnomade.

Wohnt ausserhalb vom Framework-Package (features/, packages/bundled-features/, oder pro App-Repo).

Faustregeln

  • Wenn etwas aussieht wie Framework-Infra, aber per defineFeature() ausdrueckbar waere → wahrscheinlich Core-Feature. Framework-Infra ist der letzte Ausweg, nicht der erste.
  • Wenn ein Core-Feature nur von einer bestimmten App-Domain gebraucht wird → wahrscheinlich App-Feature, nicht Core.
  • Wenn Framework-Infra sich nach Geschaeftsregeln anfuehlt (Rollen, Workflows, Limits) → ist’s zu hoch im Stack, runter ins Feature.

Warum

Dieses Framework soll fuer viele unterschiedliche Apps mit unterschiedlichen Feature-Kombinationen tragen. Kopplung macht Features unauswahlbar. Extension-Points statt Core-Aenderungen halten das System offen.