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:
@@ -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`
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -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/**`)
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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)`).
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -29,7 +29,7 @@ Kernmodule:
|
||||
|
||||
- `config/` Konfiguration
|
||||
- `db/` Schema/Updates
|
||||
- `lib/` Fachlogik
|
||||
- `core/` Fachlogik
|
||||
- `pages/` Actions + Views
|
||||
- `templates/` Partials/Layout
|
||||
- `web/` CSS/JS/Assets
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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`
|
||||
|
||||
Reference in New Issue
Block a user