Files
breadcrumb-the-shire/docs/lib-standards.md

128 lines
6.8 KiB
Markdown
Raw Normal View History

# lib-Standards
2026-03-04 15:56:58 +01:00
Letzte Aktualisierung: 2026-03-03
Praktische Regeln nur für `lib/**`. Ergänzt `docs/architektur.md`.
## 1) Repository-Regeln
- Verantwortlich für SQL, Filter, Paging und Mapping.
- Keine HTTP-, Session-, Redirect- oder Policy-Logik.
## 2) Service-Regeln
- Verantwortlich für Fachlogik und Orchestrierung.
- Keine Superglobals (`$_POST`, `$_GET`, `$_SESSION`).
- Keine Header- oder Redirect-Ausgabe.
2026-03-04 15:56:58 +01:00
- Keine direkten `DB::...`-Aufrufe im Service.
- Abhängigkeiten per Constructor Injection.
2026-03-04 15:56:58 +01:00
- DB-Locks und Transaktionen über Repository-Schicht (z. B. `DatabaseSessionRepository`).
- Audit-Hooks für fachliche Schreibaktionen laufen in Services (`SystemAuditService`), nicht in Templates.
- API-Request-System-Audit läuft zentral über `ApiSystemAuditReporter` (Lifecycle in `ApiBootstrap` + `ApiResponse`), nicht pro Endpoint.
- Scheduler-Run-/Job-Ereignisse werden zentral im `SchedulerRunService` in `SystemAuditService` geschrieben.
- CLI-System-Audit für zentrale Commands wird am Entrypoint (`bin/**`) geschrieben, mit minimaler Metadata (ohne Argumentwerte).
## 3) Instanziierung
2026-03-04 15:56:58 +01:00
- Composition Root ist `web/index.php` + `lib/App/registerContainer.php`
(→ Details: `/docs/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 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()`.
- Legacy-Service-Helper (`accessServicesFactory()`, `userServicesFactory()` usw.) werden nicht mehr verwendet.
- Für neue Domain-Logik keine statischen Utility-Klassen.
2026-03-04 15:56:58 +01:00
### 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 "\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
(rg -n 'app\([^\n]*Factory::class\)->create' pages/admin -g '*.php' || true) | wc -l
(rg -n 'Factory::class' pages/admin -g '*.php' || true) | wc -l
```
Erwartung jeweils: `0`.
### 3.2) Architektur-Contract-Test
- Verbindlicher Guardrail: `tests/Architecture/CoreStarterkitContractTest.php`
- Deckt die Hard-Cut-Regeln ab:
- keine direkten `new ...Service|Gateway|Repository` in `pages/**`
- kein `DB::` in `pages/**`
- keine `app(...Factory::class)->create...`-Ketten in `pages/admin/**`
- keine Input-Superglobals in `pages/**`
- kein `ApiResponse::readJsonBody(...)` in `pages/api/v1/**`
## 4) Autorisierung und Scope
- Zentral über `AuthorizationService` und Policy-Abilities.
- Keine verteilten Einzelchecks als primäre Zugriffslogik.
2026-03-04 15:56:58 +01:00
- UI-Capabilities zentral über `UiAccessService` auflösen.
- `$viewAuth['layout']` wird zentral im Front Controller aufgebaut (`web/index.php`), nicht in Templates.
- Templates nutzen nur `$viewAuth['layout']` bzw. `$viewAuth['page']`, keine `can('...')`-Helper.
### 4.1) Erweiterung neuer Permissions (Starter-Kit)
1. Permission-Key in `PermissionService` + Migration/Seed ergänzen.
2. Ability in passender Policy ergänzen (oder neue Operation-Ability).
3. Bei UI-Bedarf Mapping zentral in `UiCapabilityMap` ergänzen.
4. Action setzt benötigte `$viewAuth['page']`-Flags.
5. Template konsumiert nur `$viewAuth`.
6. Architektur-/Service-Tests und Doku aktualisieren.
Details und Sonderfälle: `/docs/rbac-permissions-playbook.md`
2026-03-04 15:56:58 +01:00
## 5) `data().php`-Standard
- `pages/**/data().php` sind ausschließlich GET-Endpunkte (`gridRequireGetRequest()`).
- Query-Normalisierung zentral über `gridParseFilters($query, gridSchemaQuery($filterSchema))`.
- Pro Liste gibt es ein `filter-schema.php` im selben Verzeichnis (Single Source of Truth).
- Schema-Typen: `int`, `number`, `bool`, `string`, `enum`, `uuid`, `date`, `csv_ids`, `csv_uuid`, `csv_strings`, `order`, `dir`.
- Keine Alias-Parameter (`*_id`) in Listen-Querys; IDs immer als CSV über `*_ids`.
- Bei JSON-Listenantworten immer konsistent: `{'data': [...], 'total': <int>}`.
- Für MultiSelect-Filter in Listen:
- Toolbar-Rendering zentral über `renderGridFilterToolbar(...)`.
- Select-/MultiSelect-/Text-/Date-/Hidden-Filter werden aus dem Schema gerendert (kein dupliziertes HTML in Templates).
- Grid-Filterbindung clientseitig zentral über `gridFiltersFromSchema(...)`.
- Grid-Filter verwenden CSV-Parameter (z. B. `event_types`, `actor_user_ids`), keine `[]`-Query-Arrays.
- Repository-Filter über `IN (???)` und serverseitige Limitierung/Validierung.
## 5.1) Status/Outcome-Taxonomien (Backed Enums)
- Persistierte Status-/Outcome-Domänen liegen zentral in `lib/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.
- In Services/Repositories keine verstreuten Literal-Listen für diese Domänen (`success`, `failed`, ...).
- DB-seitig sind die gleichen Domänen per `CHECK`-Constraints abgesichert (`db/init/init.sql` + `db/updates/2026-03-03-status-taxonomy-hard-cut.sql`).
## 6) API-Contract
- Extern UUID-first.
- Interne IDs nur intern auflösen und mappen.
2026-03-04 15:56:58 +01:00
## 7) Input + Validation in `pages/**`
- Request-Daten nur über `requestInput()` lesen.
- Kein direktes `$_POST`, `$_GET`, `$_FILES`, `$_SERVER['REQUEST_METHOD']` in `pages/**`.
- Validierungsfehler intern über `FormErrors` aufbauen (`formErrors()`, `formErrorsFrom()`).
- API-Validation-Antworten einheitlich über `ApiResponse::validationFromFormErrors(...)`.
- Web-Templates bekommen weiterhin kompakte Listen (`$errors`) aus `FormErrors::toFlatList()`.
- `Flash` nicht als primäre Validation-Struktur verwenden; nur für Redirect-/Outcome-Meldungen.
Kurz-How-to: `/docs/request-input-validation.md`
## 8) Definition of Done für `lib/**`
1. Schichtgrenzen eingehalten.
2. AuthZ zentral.
2026-03-04 15:56:58 +01:00
3. Instanziierungsregeln aus Abschnitt 3 eingehalten (inkl. Gate-Checks).
4. Unit-Tests ergänzt, bei Strukturänderung zusätzlich Contract-Test.
5. `phpunit` und `phpstan` grün.