Files
breadcrumb-the-shire/docs/reference-lib-standards.md
2026-03-09 18:30:52 +01:00

8.3 KiB

lib-Standards

Letzte Aktualisierung: 2026-03-09

Praktische Regeln nur für lib/**. Ergänzt /docs/explanation-architektur.md.

1) Repository-Regeln

  • Verantwortlich für SQL, Filter, Paging und Mapping.
  • Keine HTTP-, Session-, Redirect- oder Policy-Logik.

2) Service-Schicht im Detail

lib/Service/ enthält vier klar getrennte Typen — jeder hat eine eigene Verantwortung:

2a) Service (*Service.php)

  • Verantwortlich für Fachlogik und Orchestrierung.
  • Koordiniert Repositories und Gateways; enthält Business-Regeln und Validierung.
  • Keine Superglobals ($_POST, $_GET, $_SESSION).
  • Keine Header- oder Redirect-Ausgabe.
  • Keine direkten DB::...-Aufrufe.
  • Abhängigkeiten per Constructor Injection.
  • 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).

2b) Gateway (*Gateway.php)

  • Adapter-Schicht: kapselt eine einzelne externe Abhängigkeit für Services.
  • Typische Abhängigkeiten: ein Repository, Settings-Zugriff, externe HTTP-APIs, Scope-Daten.
  • Kein Fach-Orchestrierungsfluss — ein Gateway löst genau eine Infrastruktur-Frage.
  • Beispiele: AuthPermissionGateway (Permission-Lookup), UserSettingsGateway (Settings-Zugriff), OidcHttpGateway (externe OIDC-API).
  • Abhängigkeiten per Constructor Injection; keine direkten new ...Repository() außerhalb von Factories.

2c) Factory (*Factory.php / *ServicesFactory.php / *GatewayFactory.php)

  • Einzige erlaubte Stelle für new ...Service(), new ...Gateway(), new ...Repository() innerhalb von lib/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.

2d) Policy (*Policy.php / *AuthorizationPolicy.php)

  • Liegen in lib/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 (→ 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 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.

3.1) Schnell-Gates (lokal)

(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.
  • 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/howto-rbac-permissions-playbook.md

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.

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/howto-request-input-validation.md

8) Definition of Done für lib/**

  1. Schichtgrenzen eingehalten.
  2. AuthZ zentral.
  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.