Files
breadcrumb-the-shire/docs/explanation-architektur.md
fs 143d887a5c fix: patch remaining lib/ references missed in core/ rename
CliAppBootstrap and ModuleCliRuntime still required from the old
lib/ path, which would break all CLI commands. Also fixes stale
lib/ references in docs, test assertion messages, and a PHPDoc
comment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 07:54:02 +02:00

5.8 KiB

Architektur-Kompass

Letzte Aktualisierung: 2026-03-25

Ziel

Verbindliche Kurzregeln für Architekturentscheidungen.

Systembild

  • Server-gerenderte Admin-App + REST-API unter /api/v1.
  • Multi-Tenant-Isolation ist Standard.
  • Autorisierung läuft zentral über Policies.
  • Externe API-Verträge sind UUID-first.

Schichtenübersicht

flowchart TD
    Browser([Browser / API-Client])

    subgraph APP["core/App/ — Composition Root"]
        DI["AppContainer + Registrars"]
    end

    subgraph SVC["core/Service/"]
        direction LR
        FAC["Factory"]
        SERVICE["Service"]
        GW["Gateway"]
        POL["Policy"]
    end

    subgraph REPO["core/Repository/"]
        REP["Repository"]
    end

    subgraph pages["pages/**"]
        ACT["Action (*.php)"]
        VIEW["View (*.phtml)"]
    end

    subgraph TPL["templates/"]
        LAY["Layouts & Partials"]
    end

    DB[(MariaDB)]

    DI -->|"ruft zur Laufzeit"| FAC
    FAC -->|"erzeugt"| SERVICE
    FAC -->|"erzeugt"| GW

    Browser --> ACT
    ACT -->|"Input · AuthZ · Response"| SERVICE
    ACT --> POL
    ACT --> VIEW
    VIEW --> LAY
    SERVICE --> GW
    GW --> REP
    SERVICE --> REP
    REP --> DB

Request-Flow

  1. web/index.php bootstrappt Routing/Konfiguration und initialisiert den AppContainer.
  2. Guard/Firewall/Session/Locale greifen.
  3. Action in pages/** nimmt Input an.
  4. Action delegiert an Service.
  5. Service nutzt Repository für Datenzugriff.
  6. Response wird als HTML/JSON/Datei ausgegeben.

Schichtregeln (MUSS)

  • pages/**: Input, Auth/AuthZ, Orchestrierung, Response.
  • 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).
  • In Actions bevorzugt direkte Klassen-Bindings (app(Service::class)) statt Factory-Ketten.
  • In pages/admin/** keine Factory::class-Referenzen.
  • Request-Input in Actions nur über requestInput() lesen.
  • Validierungsfehler über FormErrors führen und für APIs mit ApiResponse::validationFromFormErrors(...) ausgeben.
  • In pages/api/v1/** kein direktes JSON-Body-Lesen über ApiResponse::readJsonBody(...).
  • Flash nur für Redirect-/Outcome-Meldungen verwenden, nicht als primäre Validation-Struktur.
  • Listen-Endpoints in pages/**/data().php sind GET-only (gridRequireGetRequest()).
  • Query-Filter in data().php über gridParseFiltersFromSchemaFile(...) + filter-schema.php normalisieren (enthält limit, offset, order, dir im Ergebnis).

Instanziierungsregeln (MUSS)

  • 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 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)

  • Tenant-Scope und Permission nur serverseitig erzwingen.
  • Denials je Endpunkt als 403 oder 404 modellieren.
  • API-Fehlerformat konsistent halten (error, optional errors).
  • OpenAPI bei API-Änderungen im selben Merge aktualisieren.

RBAC-UI-Standard (MUSS)

  • Ability-Entscheidungen laufen serverseitig über AuthorizationService.
  • Für Layout/Partials werden globale Capabilities zentral über UiAccessService::layoutCapabilities() + UiCapabilityMap::LAYOUT bereitgestellt.
  • Für einzelne Views setzen Actions explizit $viewAuth['page'] (bevorzugt über UiCapabilityMap + UiAccessService::pageCapabilities()).
  • In templates/** keine direkten can('...')- oder Permission-Key-Checks.

Frontend-Page-Module (MUSS)

  • In pages/** und templates/** sind inline <script type="module">...</script> ohne src verboten.
  • Seiteninitialisierung liegt in dedizierten ESM-Page-Modulen unter web/js/pages/*.
  • Pro Seite wird Konfiguration per <script type="application/json" id="page-config-<page-key>">...</script> bereitgestellt und im Modul über readPageConfig(...) gelesen.
  • In pages/** und templates/** muessen alle JS-src (eigene und Vendor) ueber assetVersion('...js...') laufen.

Mindestchecks vor Merge

docker compose exec php vendor/bin/phpunit
docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress

Bei JS-Änderungen zusätzlich Browser-Smoke mit DevTools-Console (keine JS-Errors).

Struktur-Gates zusätzlich:

(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 core 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
(rg -n 'ApiResponse::readJsonBody\(' pages/api/v1 -g '*.php' || true) | wc -l

Architektur-Contract:

docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php

Vertiefung

  • /docs/reference-core-standards.md
  • /docs/explanation-sicherheitsmodell.md
  • /docs/howto-rbac-permissions-playbook.md