monorepo - smart-village-solutions/sva-studio GitHub Wiki
Dieses Dokument beschreibt die aktuelle Organisation des Nx-/pnpm-Workspaces, die Paketrollen und die verbindlichen Konventionen für neue Projekte.
| Projekt | Typ | Pfad | Zweck |
|---|---|---|---|
sva-studio-react |
App | apps/sva-studio-react/ |
TanStack-Start-Frontend |
auth-runtime |
Library | packages/auth-runtime/ |
Authentifizierung, Session, OIDC, Runtime-Routen und Auth-Middleware |
core |
Library | packages/core/ |
Framework-agnostische Kernlogik |
data |
Library | packages/data/ |
Datenzugriff, Migrationen, Seeds |
iam-admin |
Library | packages/iam-admin/ |
Benutzer-, Rollen-, Gruppen- und Organisationsverwaltung |
iam-governance |
Library | packages/iam-governance/ |
Governance, Rechtstexte und Data-Subject-Rights |
instance-registry |
Library | packages/instance-registry/ |
Instanzverwaltung und Keycloak-Provisioning |
monitoring-client |
Library | packages/monitoring-client/ |
Logging, Metriken, OTel-Anbindung |
plugin-news |
Library | packages/plugin-news/ |
Produktives News-Plugin für CMS-Erweiterungspunkte |
routing |
Library | packages/routing/ |
Typsichere Routing-Factories und Route-Definitionen |
sdk |
Library | packages/sdk/ |
Server-/Observability-Bausteine für interne Konsument:innen |
sva-mainserver |
Library | packages/sva-mainserver/ |
Serverseitige Integration des externen SVA-Mainservers |
- apps/: laufende Anwendungen (z. B. sva-studio-react)
- packages/: publishable Libraries und Plugins
- tooling/: gemeinsame Tools und Konfigurationen
- scripts/: Automations-Skripte
Wir unterscheiden Packages nach Rolle und Wiederverwendbarkeit.
Beispiele: @sva/core, @sva/sdk
Kriterien:
- werden von mehreren Projekten genutzt
- definieren stabile Public APIs und zentrale Contracts
- sind kritisch für Architektur und langfristige Wartbarkeit
- haben klare Ownership und verbindliche Tests
Konvention:
-
tags:scope:coreoderscope:sdk, plustype:lib - keine app-spezifische UI-/Route-Logik
- Breaking Changes nur mit Doku- und Migrationshinweis
Beispiele: @sva/data, @sva/auth-runtime, @sva/iam-admin, @sva/iam-governance, @sva/instance-registry
Kriterien:
- kapseln wiederverwendbare Fachlogik oder Datenzugriff
- sind nicht nur für eine einzelne App relevant
- haben klaren Fachfokus (kein "misc"-Sammelpaket)
Konvention:
-
tags: domain-spezifisch (scope:data) plustype:lib - dürfen nur in erlaubte niedrigere Layer importieren (Boundary-Regeln)
Beispiele: @sva/plugin-*
Kriterien:
- optionales Feature oder Erweiterungspunkt
- klarer Integrationspunkt (z. B. Feature Extension)
- kein Zugriff auf interne App-Details
Konvention:
- Name:
@sva/plugin-<name> -
tags:scope:plugin, plustype:lib - Host-APIs nur über
@sva/sdkimportieren; direkte Imports aus@sva/coresind verboten
Code bleibt in apps/<app>/src, wenn:
- nur die eine App ihn nutzt
- API-Stabilität oder Wiederverwendung noch unklar ist
- Experiment/Spike ohne langfristigen Wartungsanspruch
Nutze diese Checkliste vor dem Anlegen eines neuen Packages:
- Braucht der Code eine stabile Public API?
- Gibt es eine klare Ownership und Teststrategie?
- Passt der Code in eine bestehende Scope-Kategorie?
- Vermeidet ein eigenes Package duplizierte Logik?
Entscheidung:
- Wenn mindestens 3/4 "Ja": eigenes Package anlegen
- Wenn weniger als 3/4 "Ja": in App lassen und später neu bewerten
- per Nx Generator anlegen
-
project.jsonmit mindestensbuild,lintundtest:unit -
tagskorrekt setzen (scope:*,type:*) -
src/index.tsals klare Public API -
README.mdmit Purpose, erlaubten Abhängigkeiten, Owner - Breaking Changes benötigen ADR + Migration-Guide
- Interne Deps: workspace:*
- Gemeinsame externe Deps: nur in Root package.json
- Peer Dependencies bei Plugins: In README dokumentieren
- Version-Alignment: pnpm dedupe bei Konflikten
@nx/js:lib Generator verwenden — das ist der Nx-Standard und garantiert:
- ✅ Korrekte project.json mit build/test/lint Targets
- ✅ TypeScript-Setup (tsconfig.json, tsconfig.lib.json)
- ✅ Automatische Integration in tsconfig.base.json
- ✅ Sofortige Sichtbarkeit im Nx-Projektgraphen
- ✅ Caching und affected-Commands funktionieren sofort
pnpm nx g @nx/js:lib my-package \
--directory=packages/my-package \
--bundler=tsc \
--linter=eslint \
--unitTestRunner=vitest \
--strict \
--useProjectJsonnx g @nx/js:lib my-package \
--directory=packages/my-package \
--importPath=@sva/my-package \
--tags=scope:shared,type:lib \
--bundler=tsc \
--publishable=falseWas der Generator automatisch macht:
- Erstellt
packages/my-package/mit src/index.ts - Generiert
project.jsonmit Nx Targets - Erstellt
tsconfig.jsonundtsconfig.lib.json - Aktualisiert
tsconfig.base.jsonmit Path-Mapping - Registriert Package im Nx-Projektgraphen
Nach dem Generator (falls nötig):
- Ergänze Abhängigkeiten in
package.json(z.B. Peer Dependencies für Plugins) - Implementiere Code in
src/ - Exportiere Public API über
src/index.ts
Weitere Informationen:
Plugin mit React-Dependencies:
pnpm nx g @nx/js:lib plugin-foo \
--directory=packages/plugin-foo \
--tags=scope:plugin,type:lib \
--bundler=tsc \
--linter=eslint \
--unitTestRunner=vitest \
--strict \
--useProjectJson
# Dann in package.json peerDependencies hinzufügen:
# "@tanstack/react-router": "^1.x", "react": "^19.x", etc.Publishable Library (für npm Publishing):
nx g @nx/js:lib my-lib \
--directory=packages/my-lib \
--importPath=@sva/my-lib \
--publishable=true \
--bundler=tsc
⚠️ Nur in Ausnahmefällen verwenden. Der Generator ist flexibel genug für 90% der Fälle.
Wann manuell?
- Externe bestehende Library wird ins Monorepo integriert
- Extrem spezifisches Build-Setup (sehr selten)
- Experimentelle Struktur
Nachteile manueller Setups:
- ❌ Keine automatischen Targets
- ❌ Keine path-Mapping-Integration
- ❌ Nicht im Nx-Projektgraphen (bis manuell registriert)
- ❌ affected-Commands berücksichtigen es nicht
- ❌ Höhere Fehleranfälligkeit
Falls du manuell starten musst:
- Lege einen Ordner unter packages/ an
- Erstelle package.json, project.json, tsconfig.json, tsconfig.lib.json
- Exportiere die Public API über src/index.ts
- Füge einen Pfad in tsconfig.base.json hinzu
- Prüfe die Erkennung im Workspace:
pnpm nx show projects
Dann: Wende den Generator-Workflow retrospektiv an, um Targets zu ergänzen.
Wir nutzen Nx, weil es als integrierte Monorepo-Plattform mehr liefert als „nur“ Task-Running:
- Projektgraph & affected commands: Nx modelliert Abhängigkeiten zwischen Apps und Packages und kann dadurch in CI/CD gezielt nur die betroffenen Projekte bauen/testen.
-
Generatoren & Konsistenz: Neue Apps/Packages lassen sich mit
nx g @nx/js:libetc. scaffolden — das bedeutet automatisches Setup von Targets, TypeScript-Konfiguration und Projektgraph-Integration. Das reduziert manuellen Aufwand und hält Konventionen über Zeit konsistent (weniger Copy/Paste, weniger Drift). - Architektur-Governance: Mechanismen wie Tags/Boundaries helfen, Schichten (Core vs. Plugins) langfristig sauber zu halten.
-
Caching & Skalierung: Lokales Caching ist integriert; im Repository setzen wir auf lokalen Cache plus
affected-Workflows.
Details und Trade-offs: siehe openspec/specs/monorepo-structure/design.md
Standardisierte Targets:
-
build: produktiver Build oder TypeScript-Kompilierung -
lint: ESLint-basierter Qualitätscheck -
test:unit: schneller Standard-Testlauf - optional
test:coverage: Coverage-Run für Projekte mit Messung - optional
test:integration: infra- oder serviceabhängige Tests - optional
test:typesodertypecheck: dedizierte Typprüfung, wenn Projekt-spezifisch nötig - App-spezifisch zusätzlich z. B.
serve,preview,test:e2e, domänenspezifischecheck:*Targets
Die Root-Skripte im Workspace aggregieren diese Targets bewusst nicht 1:1 pro Projekt. pnpm test:types bündelt beispielsweise Library-Builds plus sva-studio-react:typecheck, obwohl nicht jedes Projekt ein eigenes test:types Target besitzt.
Zur langfristigen Architektur-Governance erzwingen wir Import-Grenzen mit
@nx/enforce-module-boundaries in eslint.config.mjs.
-
scope:coredarf nur vonscope:coreabhängen -
scope:datadarf vonscope:core,scope:dataabhängen -
scope:sdkdarf vonscope:core,scope:data,scope:sdkabhängen -
scope:plugindarf vonscope:sdk,scope:pluginabhängen -
scope:appdarf vonscope:core,scope:data,scope:sdk,scope:pluginabhängen
- Lint-Regel:
eslint.config.mjs - Tags pro Projekt:
apps/*/project.jsonundpackages/*/project.json(tags)
- Gesamter Workspace:
pnpm test:eslint - Einzelprojekt:
pnpm nx run <project>:lint
- Passende
tagsimproject.jsonsetzen (z. B.scope:plugin,type:lib) - Falls eine neue Scope-Kategorie entsteht,
depConstraintsineslint.config.mjserweitern - Lint lokal ausführen und Rule-Verletzungen vor dem Commit beheben
- TanStack Start läuft im Workspace auf der jeweils aktuellen Node-LTS-Linie
- Routing erfolgt über eine Code-Registry; siehe docs/routing.md
- Package-Manager ist pnpm (siehe pnpm-workspace.yaml)