refactor: rename lib/ to core/ for clearer core-module separation

Rename the top-level lib/ directory to core/ so the project root
immediately communicates which code is core platform and which lives
in modules/. PHP namespaces (MintyPHP\*) are unchanged — only the
Composer PSR-4 path mapping moves from lib/ to core/.

Module-internal lib/ directories (modules/*/lib/) are untouched.

Updated across the full stack:
- composer.json PSR-4 mapping
- bootstrap entry points (web/index.php, bin/*, tests/bootstrap.php)
- tooling configs (phpstan.neon, phpunit.xml, php-cs-fixer)
- 26 architecture contract tests
- enforcement-policy, guard-catalog, quality-gates
- all documentation (CLAUDE.md, README, 25 docs/, .agents/skills/)
- bin/qa-extended.sh search paths
- .claude/settings.local.json permission paths

Workflow: .agents/runs/CORE-LIB-RENAME-001/ (Analyst → Planner →
Executor → Code Review (4 findings fixed) → Security Review →
Acceptance Test → Finalizer)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 23:20:05 +02:00
parent 7c10fadcb9
commit 0e86925464
371 changed files with 191 additions and 192 deletions

View File

@@ -19,11 +19,11 @@ Verbindliche Kurzregeln für Architekturentscheidungen.
flowchart TD
Browser([Browser / API-Client])
subgraph APP["lib/App/ — Composition Root"]
subgraph APP["core/App/ — Composition Root"]
DI["AppContainer + Registrars"]
end
subgraph SVC["lib/Service/"]
subgraph SVC["core/Service/"]
direction LR
FAC["Factory"]
SERVICE["Service"]
@@ -31,7 +31,7 @@ flowchart TD
POL["Policy"]
end
subgraph REPO["lib/Repository/"]
subgraph REPO["core/Repository/"]
REP["Repository"]
end
@@ -73,8 +73,8 @@ flowchart TD
## Schichtregeln (MUSS)
- `pages/**`: Input, Auth/AuthZ, Orchestrierung, Response.
- `lib/Service/**`: Fachlogik und Anwendungsfluss.
- `lib/Repository/**`: SQL, Filter, Paging, Mapping.
- `core/Service/**`: Fachlogik und Anwendungsfluss.
- `core/Repository/**`: SQL, Filter, Paging, Mapping.
- `templates/**`: Rendering ohne Business-Logik.
- Templates konsumieren nur vom Action-Layer vorbereitete View-Daten (`$viewAuth`, Form/ViewModel), keine direkten Permission-Helper.
- Service-Auflösung in Actions/Helpers nur über `app(Foo::class)`.
@@ -89,11 +89,11 @@ flowchart TD
## Instanziierungsregeln (MUSS)
- Composition Root: `web/index.php` + `lib/App/registerContainer.php`
- Composition Root: `web/index.php` + `core/App/registerContainer.php`
(→ Details: `/docs/explanation-di-container.md`).
- `new ...Factory()` nur im Composition Root und in `*Factory.php`.
- In `lib/Service/**` außerhalb von `*Factory.php` keine direkten `new ...Factory()`.
- In `lib/Service/**` außerhalb von `*Factory.php` keine direkten `new ...Service()`, `new ...Gateway()` oder `new ...Repository()`.
- In `core/Service/**` außerhalb von `*Factory.php` keine direkten `new ...Factory()`.
- In `core/Service/**` außerhalb von `*Factory.php` keine direkten `new ...Service()`, `new ...Gateway()` oder `new ...Repository()`.
- Legacy-Helper (`accessServicesFactory()`, `userServicesFactory()` usw.) sind entfernt und werden nicht mehr genutzt.
## API- und Security-Regeln (MUSS)
@@ -129,8 +129,8 @@ Bei JS-Änderungen zusätzlich Browser-Smoke mit DevTools-Console (keine JS-Erro
Struktur-Gates zusätzlich:
```bash
(rg 'new\s+[^\s(]+Factory\(' lib/Service || true) | wc -l
(rg --glob '!**/*Factory.php' 'new\s+[^\s(]+(Service|Gateway|Repository)\(' lib/Service || true) | wc -l
(rg 'new\s+[^\s(]+Factory\(' core/Service || true) | wc -l
(rg --glob '!**/*Factory.php' 'new\s+[^\s(]+(Service|Gateway|Repository)\(' core/Service || true) | wc -l
(rg "\b(accessServicesFactory|directoryServicesFactory|userServicesFactory|authServicesFactory|tenantServicesFactory|settingServicesFactory|userRepositoryFactory|authRepositoryFactory|authGatewayFactory)\(" pages lib templates web || true) | wc -l
(rg -n 'new\s+[^\s(]+(Service|Gateway|Repository)\(' pages -g '*.php' || true) | wc -l
(rg -n '\bDB::' pages -g '*.php' || true) | wc -l
@@ -147,6 +147,6 @@ docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitCont
## Vertiefung
- `/docs/reference-lib-standards.md`
- `/docs/reference-core-standards.md`
- `/docs/explanation-sicherheitsmodell.md`
- `/docs/howto-rbac-permissions-playbook.md`

View File

@@ -31,7 +31,7 @@ Das ist absichtlich laut, damit fehlende Registrierungen sofort auffallen.
## Aufbau: Registrars
Beim Start der Anwendung läuft `lib/App/registerContainer.php`.
Beim Start der Anwendung läuft `core/App/registerContainer.php`.
Es erstellt den Container und ruft 8 **Registrars** nacheinander auf:
| Registrar | Was er registriert |
@@ -68,7 +68,7 @@ Zirkuläre Abhängigkeiten werden über `static fn (): Foo => $c->get(Foo::class
## Neuen Service registrieren
**1. Service und ggf. Factory erstellen** (wie bisher in `lib/Service/`)
**1. Service und ggf. Factory erstellen** (wie bisher in `core/Service/`)
**2. Im passenden Registrar eintragen:**
@@ -103,6 +103,6 @@ $meinService = app(\MintyPHP\Service\MeinNeuerService::class);
den konkreten Service direkt registrieren und per `app()` holen.
- Der Container lebt **pro HTTP-Anfrage** — er wird in `web/index.php` initialisiert
und über `setAppContainer()` im globalen State abgelegt.
- In CLI-Commands wird der Container separat initialisiert (ueber die Runtime-Bootstrap-Klassen unter `lib/Console/Support/`).
- In `lib/Service/**` außerhalb von `*Factory.php` niemals `new ...Factory()`,
- In CLI-Commands wird der Container separat initialisiert (ueber die Runtime-Bootstrap-Klassen unter `core/Console/Support/`).
- In `core/Service/**` außerhalb von `*Factory.php` niemals `new ...Factory()`,
`new ...Service()` oder `new ...Repository()` direkt instanziieren.

View File

@@ -85,7 +85,7 @@ Loesung: Alle 5 Minuten prueft ein Intervall ob seit dem letzten Sync Interaktio
## Remember-Token
Quelle: `lib/Service/Auth/RememberMeService.php`
Quelle: `core/Service/Auth/RememberMeService.php`
### Token-Struktur
@@ -117,7 +117,7 @@ Token-Rotation bei jedem Auto-Login begrenzt das Fenster bei Cookie-Diebstahl.
### Rate Limiting (Remember-Token)
Quelle: `lib/Service/Auth/RememberMeRateLimiter.php`
Quelle: `core/Service/Auth/RememberMeRateLimiter.php`
- Storage: Memcached (`Cache::add` + `Cache::increment`)
- Limit: 5 Fehlversuche pro IP in 15 Minuten
@@ -166,10 +166,10 @@ Das Secure-Flag wird automatisch anhand des Protokolls gesetzt (`RequestRuntime:
| Komponente | Pfad |
|------------|------|
| Timeout-Pruefung | `web/index.php` |
| AuthService | `lib/Service/Auth/AuthService.php` |
| RememberMeService | `lib/Service/Auth/RememberMeService.php` |
| RememberMeRateLimiter | `lib/Service/Auth/RememberMeRateLimiter.php` |
| Session-Settings | `lib/Service/Settings/SettingsSessionGateway.php` |
| AuthService | `core/Service/Auth/AuthService.php` |
| RememberMeService | `core/Service/Auth/RememberMeService.php` |
| RememberMeRateLimiter | `core/Service/Auth/RememberMeRateLimiter.php` |
| Session-Settings | `core/Service/Settings/SettingsSessionGateway.php` |
| Frontend-Warning | `web/js/components/app-session-warning.js` |
| Session-Ping | `pages/admin/session-ping/data().php` |
| Login-Action | `pages/auth/login().php` |

View File

@@ -19,7 +19,7 @@ Kein Layer allein reicht aus.
## Public vs. Protected
- Public-Routen kommen aus `config/routes.php` (`public => true`).
- Zusätzlich sind Präfixe technisch immer public (`branding/`, `flash/`, `auth/microsoft/`, `api/`) in `lib/Http/AccessControl.php`.
- Zusätzlich sind Präfixe technisch immer public (`branding/`, `flash/`, `auth/microsoft/`, `api/`) in `core/Http/AccessControl.php`.
- Alles andere erfordert Login.
API-Sonderfall:
@@ -47,7 +47,7 @@ API-Sonderfall:
## API-Schutz
- Zentrale API-Rate-Limits laufen in `lib/Http/ApiBootstrap.php`.
- Zentrale API-Rate-Limits laufen in `core/Http/ApiBootstrap.php`.
- API-Audit läuft zentral über `ApiAuditService`:
- loggt Metadaten (Pfad, Status, Dauer, Kontext)
- redigiert sensible Query-Parameter

View File

@@ -8,8 +8,8 @@ Brute-Force und API-Spam begrenzen, ohne Verfügbarkeit zu gefährden.
## Technischer Kern
- Service: `lib/Service/Security/RateLimiterService.php`
- Repository: `lib/Repository/Security/RateLimitRepository.php`
- Service: `core/Service/Security/RateLimiterService.php`
- Repository: `core/Repository/Security/RateLimitRepository.php`
- Storage: `request_rate_limits`
- `scope`, `subject_hash`, `hits`, `window_started_at`, `blocked_until`
@@ -20,7 +20,7 @@ Wichtig:
## API-Limits (global)
Durchgesetzt in `lib/Http/ApiBootstrap.php`:
Durchgesetzt in `core/Http/ApiBootstrap.php`:
1. IP-only (`api.ip`)
- 120 Requests / 60s
@@ -63,7 +63,7 @@ Erfolgreicher API-Login setzt `api.auth.login.email_ip` zurück.
## Remember-Token-Limits
In `lib/Service/Auth/RememberMeRateLimiter.php` (Memcached-basiert):
In `core/Service/Auth/RememberMeRateLimiter.php` (Memcached-basiert):
1. Pro IP (`remember_rl:{ip}`)
- 5 Fehlversuche / 900s

View File

@@ -66,8 +66,8 @@ Unbekannte/manipulierte Parameter werden ignoriert.
- `/pages/admin/tenants/custom-field-update($id).php`
- `/pages/admin/tenants/custom-field-delete($id).php`
- User-Werte:
- `/lib/Service/CustomField/UserCustomFieldValueService.php`
- `/lib/Repository/CustomField/*`
- `/core/Service/CustomField/UserCustomFieldValueService.php`
- `/core/Repository/CustomField/*`
- Address-Book-Filter:
- `/pages/address-book/index().php`
- `/pages/address-book/index(default).phtml`

View File

@@ -49,7 +49,7 @@ Richtwert: zuerst kurz, Details als Verweis auf Referenzseiten.
1. Seite ist in `docs/index.md` eingetragen.
2. Struktur ist kurz, klar, ohne Redundanz.
3. Pfade, Routen, Permissions stimmen mit dem Code.
4. Instanziierungsregeln sind konsistent mit `/docs/explanation-architektur.md` und `/docs/reference-lib-standards.md`.
4. Instanziierungsregeln sind konsistent mit `/docs/explanation-architektur.md` und `/docs/reference-core-standards.md`.
5. Mindestens ein konkretes Beispiel ist enthalten.
6. Begriffe sind konsistent mit `/docs/reference-domain-glossar.md`.

View File

@@ -12,9 +12,9 @@ Ein neuer Entwickler soll den ersten Change strukturiert und reproduzierbar umse
1. Data-Endpoint erweitern (z. B. `pages/admin/users/data().php`)
2. Falls nötig: neue Repository-Methode ergänzen je nach Typ:
- Listenabfrage / Filterung / Paginierung → `lib/Repository/User/UserListQueryRepository.php`
- Einzelnes Objekt lesen → `lib/Repository/User/UserReadRepository.php`
- Schreiboperation → `lib/Repository/User/UserWriteRepository.php`
- Listenabfrage / Filterung / Paginierung → `core/Repository/User/UserListQueryRepository.php`
- Einzelnes Objekt lesen → `core/Repository/User/UserReadRepository.php`
- Schreiboperation → `core/Repository/User/UserWriteRepository.php`
3. Grid-Spalte in `pages/admin/<modul>/index(default).phtml` ergänzen (z. B. `pages/admin/users/index(default).phtml`)
4. i18n-Key für Spaltennamen setzen
5. Service-Auflösung in Action-Dateien nur über `app(Foo::class)` nutzen (kein `new ...Factory()` in `pages/**`)

View File

@@ -9,10 +9,10 @@ Scheduler-Betrieb und Job-Erweiterung kurz und verlässlich dokumentieren.
## Kernkomponenten
- Runner: `bin/console scheduler:run`
- Orchestrierung: `lib/Service/Scheduler/SchedulerRunService.php`
- Job-Konfiguration: `lib/Service/Scheduler/ScheduledJobService.php`
- Registry: `lib/Service/Scheduler/ScheduledJobRegistry.php`
- Schedule-Berechnung: `lib/Service/Scheduler/ScheduleCalculator.php`
- Orchestrierung: `core/Service/Scheduler/SchedulerRunService.php`
- Job-Konfiguration: `core/Service/Scheduler/ScheduledJobService.php`
- Registry: `core/Service/Scheduler/ScheduledJobRegistry.php`
- Schedule-Berechnung: `core/Service/Scheduler/ScheduleCalculator.php`
## Datenmodell
@@ -61,7 +61,7 @@ Registrierte Core-Jobs:
## Neuen Job registrieren (kurz)
1. Handler in `lib/Service/Scheduler/Handler/*` erstellen (`ScheduledJobHandlerInterface`).
1. Handler in `core/Service/Scheduler/Handler/*` erstellen (`ScheduledJobHandlerInterface`).
2. Job-Key + Handler in `ScheduledJobRegistry` ergänzen.
3. Abhängigkeiten in `SchedulerServicesFactory` verdrahten.

View File

@@ -15,9 +15,9 @@ Zwei Endpunkte teilen dieselbe Konfiguration:
Zentrale Fassade:
- `lib/Support/SearchConfig.php`
- Laufzeit-Orchestrierung: `lib/Service/Search/SearchDataService.php`
- SQL-Ausführung: `lib/Repository/Search/SearchQueryRepository.php`
- `core/Support/SearchConfig.php`
- Laufzeit-Orchestrierung: `core/Service/Search/SearchDataService.php`
- SQL-Ausführung: `core/Repository/Search/SearchQueryRepository.php`
Provider:
@@ -65,8 +65,8 @@ Die Entwicklerdoku läuft als Spezialresource `docs`:
## Check vor Merge
1. `php -l lib/Support/SearchConfig.php`
2. `php -l lib/Support/Search/*.php`
1. `php -l core/Support/SearchConfig.php`
2. `php -l core/Support/Search/*.php`
3. Preview testen (`admin/search/data?q=...`)
4. Vollsuche testen (`/search?search=...`)
5. Permission/Scope mit reduziertem User prüfen

View File

@@ -91,8 +91,8 @@ Bei JS-Änderungen zusätzlich Browser-Smoke mit DevTools-Console (keine JS-Erro
## Struktur-Gates (empfohlen)
```bash
(rg 'new\s+[^\s(]+Factory\(' lib/Service || true) | wc -l
(rg --glob '!**/*Factory.php' 'new\s+[^\s(]+(Service|Gateway|Repository)\(' lib/Service || true) | wc -l
(rg 'new\s+[^\s(]+Factory\(' core/Service || true) | wc -l
(rg --glob '!**/*Factory.php' 'new\s+[^\s(]+(Service|Gateway|Repository)\(' core/Service || true) | wc -l
(rg "\b(accessServicesFactory|directoryServicesFactory|userServicesFactory|authServicesFactory|tenantServicesFactory|settingServicesFactory|userRepositoryFactory|authRepositoryFactory|authGatewayFactory)\(" pages lib templates web || true) | wc -l
```

View File

@@ -18,7 +18,7 @@ Schritt-fuer-Schritt Anleitung: vom leeren Verzeichnis zum lauffaehigen Modul mi
|---|---|
| Manifest-Contract (verbindlich) | `/docs/reference-module-manifest-contract.md` |
| Manifest-JSON-Schema | `/.agents/contracts/module-manifest.schema.json` |
| Platform-Contracts (Interfaces) | `lib/App/Module/Contracts/` (5 Interfaces) |
| Platform-Contracts (Interfaces) | `core/App/Module/Contracts/` (5 Interfaces) |
| CLI-Kommandos | `/docs/reference-cli-commands.md` |
| Referenzmodul (vollstaendig) | `modules/notifications/` |
| Einfaches Modul | `modules/bookmarks/` |
@@ -34,7 +34,7 @@ Erzeugt:
```
modules/mein-modul/
module.php ← Manifest
lib/Module/MeinModul/
core/Module/MeinModul/
MeinModulContainerRegistrar.php ← DI-Registrierung
MeinModulAuthorizationPolicy.php ← Berechtigungspruefung
pages/ ← Seiten (file-based routing)
@@ -276,7 +276,7 @@ Fuer globale Assets (auf jeder Seite) stattdessen `layout.head_style` oder `runt
],
```
Interface: `lib/App/Module/Contracts/SessionProvider.php` (Methoden: `populate()`, `clear()`).
Interface: `core/App/Module/Contracts/SessionProvider.php` (Methoden: `populate()`, `clear()`).
**Session-Key-Konvention:** `module.mein-modul.*` (wird per Architektur-Test geprueft).
@@ -290,7 +290,7 @@ Interface: `lib/App/Module/Contracts/SessionProvider.php` (Methoden: `populate()
],
```
Interface: `lib/App/Module/Contracts/LayoutContextProvider.php` (Methode: `provide()`).
Interface: `core/App/Module/Contracts/LayoutContextProvider.php` (Methode: `provide()`).
**Key-Konvention:** Top-Level-Keys muessen namespaced sein: `mein-modul.nav`, nicht `nav`.
@@ -304,7 +304,7 @@ Interface: `lib/App/Module/Contracts/LayoutContextProvider.php` (Methode: `provi
],
```
Interface: `lib/App/Module/Contracts/EventListener.php` (Methode: `handle(string $event, array $payload)`).
Interface: `core/App/Module/Contracts/EventListener.php` (Methode: `handle(string $event, array $payload)`).
### Scheduler-Job

View File

@@ -13,7 +13,7 @@ Ziel: Neue Rechte konsistent und ohne Legacy-Pfade ergänzen.
## Fall A: Ganz neue Permission
1. Permission-Key in `PermissionService` ergänzen.
Datei: `lib/Service/Access/PermissionService.php`
Datei: `core/Service/Access/PermissionService.php`
2. DB-Update für Bestandsinstanzen anlegen (idempotent).
Datei: `db/updates/YYYY-MM-DD-*.sql`
3. `db/init/init.sql` für Neuinstallationen synchron ergänzen.

View File

@@ -87,8 +87,8 @@ Diese Dokumentation ist nach dem Diataxis-Modell in vier Quadranten gegliedert:
- ENV-Variablen und Produktions-Defaults.
- `/docs/reference-konventionen.md`
- Projektweite Konventionen (kurz und verbindlich).
- `/docs/reference-lib-standards.md`
- Regeln speziell fuer `lib/**`.
- `/docs/reference-core-standards.md`
- Regeln speziell fuer `core/**`.
- `/docs/reference-benutzer-lifecycle-policy.md`
- Policy- und Laufzeitreferenz fuer User Lifecycle.
- `/docs/reference-frontend-css.md`

View File

@@ -154,7 +154,7 @@ Voraussetzung fuer den ersten App-Start: `config/config.php` muss vorhanden sein
## Neue Kommandos anlegen
1. Klasse in `lib/Console/Commands/` erstellen, die `MintyPHP\Console\Command` erweitert.
1. Klasse in `core/Console/Commands/` erstellen, die `MintyPHP\Console\Command` erweitert.
2. `name()`, `description()` und `execute()` implementieren.
3. Optional: `usage()` fuer `--help`-Ausgabe ueberschreiben.

View File

@@ -2,7 +2,7 @@
Letzte Aktualisierung: 2026-03-09
Praktische Regeln nur für `lib/**`. Ergänzt `/docs/explanation-architektur.md`.
Praktische Regeln nur für `core/**`. Ergänzt `/docs/explanation-architektur.md`.
## 1) Repository-Regeln
@@ -11,7 +11,7 @@ Praktische Regeln nur für `lib/**`. Ergänzt `/docs/explanation-architektur.md`
## 2) Service-Schicht im Detail
`lib/Service/` enthält vier klar getrennte Typen — jeder hat eine eigene Verantwortung:
`core/Service/` enthält vier klar getrennte Typen — jeder hat eine eigene Verantwortung:
### 2a) Service (`*Service.php`)
@@ -37,36 +37,36 @@ Praktische Regeln nur für `lib/**`. Ergänzt `/docs/explanation-architektur.md`
### 2c) Factory (`*Factory.php` / `*ServicesFactory.php` / `*GatewayFactory.php`)
- Einzige erlaubte Stelle für `new ...Service()`, `new ...Gateway()`, `new ...Repository()` innerhalb von `lib/Service/`.
- Einzige erlaubte Stelle für `new ...Service()`, `new ...Gateway()`, `new ...Repository()` innerhalb von `core/Service/`.
- Erzeugt konfigurierte Service-/Gateway-Gruppen für den DI-Container.
- Keine Fachlogik; reine Kompositions-Helfer.
- Werden im Composition Root (`lib/App/registerContainer.php`) registriert.
- Werden im Composition Root (`core/App/registerContainer.php`) registriert.
### 2d) Policy (`*Policy.php` / `*AuthorizationPolicy.php`)
- Liegen in `lib/Service/Access/`.
- Liegen in `core/Service/Access/`.
- Treffen Authorization-Entscheidungen (Ability-Checks) für einen bestimmten Ressourcentyp.
- Geben `AuthorizationDecision` zurück; kein HTTP-Redirect, keine Fachlogik.
- Werden über `AccessPolicyFactory` + `AuthorizationService` aufgelöst — nie direkt in Actions instanziiert.
## 3) Instanziierung
- Composition Root ist `web/index.php` + `lib/App/registerContainer.php`
- Composition Root ist `web/index.php` + `core/App/registerContainer.php`
(→ Details: `/docs/explanation-di-container.md`).
- Domain-Abhängigkeiten mit `new` nur im Composition Root und in `*Factory.php`.
- In `pages/**`, `templates/**`, `lib/Support/**` und Gateway-/Service-Fallbacks nur über `app(Foo::class)` auflösen.
- In `pages/**`, `templates/**`, `core/Support/**` und Gateway-/Service-Fallbacks nur über `app(Foo::class)` auflösen.
- In Actions bevorzugt direkte Service/Gateway-Bindings (`app(Service::class)`) statt `app(...Factory::class)->create...()`.
- In `pages/admin/**` keine `Factory::class`-Referenzen; nur direkte `app(Service::class)` / `app(Repository::class)`.
- In `lib/Service/**` außerhalb von `*Factory.php` keine direkten `new ...Factory()`.
- In `lib/Service/**` außerhalb von `*Factory.php` keine direkten `new ...Service()`, `new ...Gateway()` oder `new ...Repository()`.
- In `core/Service/**` außerhalb von `*Factory.php` keine direkten `new ...Factory()`.
- In `core/Service/**` außerhalb von `*Factory.php` keine direkten `new ...Service()`, `new ...Gateway()` oder `new ...Repository()`.
- Legacy-Service-Helper (`accessServicesFactory()`, `userServicesFactory()` usw.) werden nicht mehr verwendet.
- Für neue Domain-Logik keine statischen Utility-Klassen.
### 3.1) Schnell-Gates (lokal)
```bash
(rg 'new\s+[^\s(]+Factory\(' lib/Service || true) | wc -l
(rg --glob '!**/*Factory.php' 'new\s+[^\s(]+(Service|Gateway|Repository)\(' lib/Service || true) | wc -l
(rg 'new\s+[^\s(]+Factory\(' core/Service || true) | wc -l
(rg --glob '!**/*Factory.php' 'new\s+[^\s(]+(Service|Gateway|Repository)\(' core/Service || true) | wc -l
(rg "\b(accessServicesFactory|directoryServicesFactory|userServicesFactory|authServicesFactory|tenantServicesFactory|settingServicesFactory|userRepositoryFactory|authRepositoryFactory|authGatewayFactory)\(" pages lib templates web || true) | wc -l
(rg -n 'new\s+[^\s(]+(Service|Gateway|Repository)\(' pages -g '*.php' || true) | wc -l
(rg -n '\bDB::' pages -g '*.php' || true) | wc -l
@@ -122,7 +122,7 @@ Details und Sonderfälle: `/docs/howto-rbac-permissions-playbook.md`
## 5.1) Status/Outcome-Taxonomien (Backed Enums)
- Persistierte Status-/Outcome-Domänen liegen zentral in `lib/Domain/Taxonomy/*`.
- Persistierte Status-/Outcome-Domänen liegen zentral in `core/Domain/Taxonomy/*`.
- Jede Taxonomie liefert einheitlich: `values()`, `tryNormalize()`, `normalizeOr()`, `labelToken()`, `badgeVariant()`.
- Filter-Schemas lesen Allowed-Listen aus `Enum::values()` oder `gridEnumSanitizer(Enum::class)`.
- Data-Endpunkte erzeugen Badge/Label über Enum-Cases, nicht über lokale `if/elseif`-Mappings.
@@ -145,7 +145,7 @@ Details und Sonderfälle: `/docs/howto-rbac-permissions-playbook.md`
Kurz-How-to: `/docs/howto-request-input-validation.md`
## 8) Definition of Done für `lib/**`
## 8) Definition of Done für `core/**`
1. Schichtgrenzen eingehalten.
2. AuthZ zentral.

View File

@@ -15,8 +15,8 @@ Alle Kernbegriffe kurz und einheitlich verstehen.
- `Policy` / `Ability`: zentrale Autorisierungsregel.
- `Tenant-Scope`: Regel, ob Zugriff im Ziel-Tenant erlaubt ist.
- `Action`: Request-Einstieg in `pages/**`.
- `Service`: Fachlogik in `lib/Service/**`.
- `Repository`: SQL und Mapping in `lib/Repository/**`.
- `Service`: Fachlogik in `core/Service/**`.
- `Repository`: SQL und Mapping in `core/Repository/**`.
- `Factory`: erstellt/verdrahtet Service-Objekte (z. B. `UserServicesFactory`).
- `Gateway`: schmale Schnittstelle/Fassade zu Domain-Funktionen oder Querschnitt (z. B. `PermissionGateway`).
- `AppContainer`: zentrale Service-Registrierung und -Auflösung (`app(Foo::class)`).

View File

@@ -15,11 +15,11 @@ Kurze Definition of Done vor Merge.
## Beim Implementieren
- [ ] Kein SQL in `*.phtml`
- [ ] SQL nur in `lib/Repository/**`
- [ ] Business-Logik nur in `lib/Service/**`
- [ ] SQL nur in `core/Repository/**`
- [ ] Business-Logik nur in `core/Service/**`
- [ ] Neue UI-Texte über `t('...')`
- [ ] Listen-Filter konsistent: IDs/Enums als Select/MultiSelect statt Freitext
- [ ] Für persistierte Status/Outcomes nur zentrale Taxonomien aus `lib/Domain/Taxonomy/*` nutzen (keine lokalen String-Literale)
- [ ] Für persistierte Status/Outcomes nur zentrale Taxonomien aus `core/Domain/Taxonomy/*` nutzen (keine lokalen String-Literale)
- [ ] Für neue/angepasste Liste: `filter-schema.php` angelegt/aktualisiert
- [ ] File-Uploads über `templates/partials/app-file-upload.phtml` (kein inline File-Upload-Markup) — `$fileUpload`-Array mit `name`, `accept`, `hint`, optional `currentSrc`/`deleteAction`
- [ ] Listen-Titlebar über `templates/partials/app-list-titlebar.phtml` (kein inline `<div class="app-list-titlebar">`)
@@ -48,9 +48,9 @@ Kurze Definition of Done vor Merge.
- [ ] Runtime-Konfiguration liegt in `page-config-app-components` (default) bzw. `page-config-login-components` (login) und wird ueber `readPageConfig(...)` gelesen
- [ ] Host-gebundene Runtime-Komponenten haben explizite `data-app-component`-Hosts im Markup
- [ ] Global-Utilities ohne natuerliches Host-Element sind explizit runtime-global registriert (`scope: 'global'`)
- [ ] Service-Auflösung in `pages/**`/`templates/**`/`lib/Support/**` nur via `app(Foo::class)`
- [ ] Keine `new ...Factory()` in `lib/Service/**` außerhalb `*Factory.php`
- [ ] Keine direkten `new ...Service()`, `new ...Gateway()`, `new ...Repository()` in `lib/Service/**` außerhalb `*Factory.php`
- [ ] Service-Auflösung in `pages/**`/`templates/**`/`core/Support/**` nur via `app(Foo::class)`
- [ ] Keine `new ...Factory()` in `core/Service/**` außerhalb `*Factory.php`
- [ ] Keine direkten `new ...Service()`, `new ...Gateway()`, `new ...Repository()` in `core/Service/**` außerhalb `*Factory.php`
## Sicherheits- und Scope-Checks
@@ -106,7 +106,7 @@ Kurze Definition of Done vor Merge.
## Vertiefung
- `/docs/reference-lib-standards.md`
- `/docs/reference-core-standards.md`
- `/docs/explanation-sicherheitsmodell.md`
- `/docs/howto-rbac-permissions-playbook.md`
- `/docs/howto-dokumentation-erweitern.md`

View File

@@ -10,13 +10,13 @@ Letzte Aktualisierung: 2026-03-24
## 2) Schichten
- SQL nur in `lib/Repository/**`.
- Fachlogik nur in `lib/Service/**`.
- SQL nur in `core/Repository/**`.
- Fachlogik nur in `core/Service/**`.
- `pages/**` bleibt dünn: Input, Auth/AuthZ, Service-Aufruf, Response.
- Neue Domain-Logik als Instanz-Service mit Factory-Verdrahtung.
- Service-Instanzen in Actions/Helpern nur via `app(Foo::class)`.
- `new ...Factory()` nur im Composition Root (`web/index.php` + `lib/App/registerContainer.php`) oder in `*Factory.php`.
- In `lib/Service/**` außerhalb von `*Factory.php` keine direkten `new ...Factory()`, `new ...Service()`, `new ...Gateway()` oder `new ...Repository()`.
- `new ...Factory()` nur im Composition Root (`web/index.php` + `core/App/registerContainer.php`) oder in `*Factory.php`.
- In `core/Service/**` außerhalb von `*Factory.php` keine direkten `new ...Factory()`, `new ...Service()`, `new ...Gateway()` oder `new ...Repository()`.
- Alte Service-Helper (`accessServicesFactory()`, `userServicesFactory()` usw.) nicht mehr verwenden.
- `pages/**/data().php` nur GET (`gridRequireGetRequest()`), Query-Normalisierung ausschließlich über `gridParseFilters(...)`.
- Für jede Liste ein `filter-schema.php` im selben Verzeichnis; `data().php`, `index().php` und `index(default).phtml` verwenden dieses Schema.

View File

@@ -29,7 +29,7 @@ Kernmodule:
- `config/` Konfiguration
- `db/` Schema/Updates
- `lib/` Fachlogik
- `core/` Fachlogik
- `pages/` Actions + Views
- `templates/` Partials/Layout
- `web/` CSS/JS/Assets

View File

@@ -25,15 +25,15 @@ API-Ergänzung:
## Schichtregeln
- `pages/**`: Input, Auth/AuthZ, Service-Aufruf, Response.
- `lib/Service/**`: Business-Regeln und Orchestrierung.
- `lib/Repository/**`: SQL, Filter, Mapping.
- `core/Service/**`: Business-Regeln und Orchestrierung.
- `core/Repository/**`: SQL, Filter, Mapping.
- `templates/**`: Rendering ohne Business-Logik.
- Request-Daten in Actions nur über `requestInput()` (keine Input-Superglobals).
- Validierungsfehler zentral über `FormErrors`; API-Fehler über `ApiResponse::validationFromFormErrors(...)`.
- Template-AuthZ konsumiert nur vorbereitete `$viewAuth`-Daten (kein `can('...')` im Template).
- Service-Auflösung in Actions/Helpern über `app(Foo::class)`.
- Fachliche Audit-Events laufen zentral über `SystemAuditService`.
- Persistierte Status/Outcome-Werte laufen über die zentrale Taxonomie-Schicht `lib/Domain/Taxonomy/*` (Enums + DB-Checks).
- Persistierte Status/Outcome-Werte laufen über die zentrale Taxonomie-Schicht `core/Domain/Taxonomy/*` (Enums + DB-Checks).
- `pages/**/data().php` sind GET-only (`gridRequireGetRequest()`) und normalisieren `limit`/`offset`/`order`/`dir` über Grid-Helper.
## Einfaches ASCII-Beispiel
@@ -82,5 +82,5 @@ Action
## Vertiefung
- Vollständiger Kompass: `/docs/explanation-architektur.md`
- Regeln für `lib/**`: `/docs/reference-lib-standards.md`
- Regeln für `core/**`: `/docs/reference-core-standards.md`
- RBAC-Rechte erweitern: `/docs/howto-rbac-permissions-playbook.md`

View File

@@ -19,7 +19,7 @@ Eine kleine Backend-Änderung korrekt über Repository und Service umsetzen.
- Service kennt keine Superglobals/Redirects.
- Externe API bleibt UUID-first.
- Service-Auflösung in `pages/**` via `app(Foo::class)`.
- Keine direkten `new ...Factory()` in `lib/Service/**` außerhalb `*Factory.php`.
- Keine direkten `new ...Factory()` in `core/Service/**` außerhalb `*Factory.php`.
## Checks
@@ -30,5 +30,5 @@ docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress
## Vertiefung
- `lib/**`-Regeln: `/docs/reference-lib-standards.md`
- `core/**`-Regeln: `/docs/reference-core-standards.md`
- API-Prozess: `/docs/tutorial-08-api-erweitern.md`