forked from fa/breadcrumb-the-shire
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>
5.8 KiB
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
web/index.phpbootstrappt Routing/Konfiguration und initialisiert denAppContainer.- Guard/Firewall/Session/Locale greifen.
- Action in
pages/**nimmt Input an. - Action delegiert an Service.
- Service nutzt Repository für Datenzugriff.
- 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/**keineFactory::class-Referenzen. - Request-Input in Actions nur über
requestInput()lesen. - Validierungsfehler über
FormErrorsführen und für APIs mitApiResponse::validationFromFormErrors(...)ausgeben. - In
pages/api/v1/**kein direktes JSON-Body-Lesen überApiResponse::readJsonBody(...). Flashnur für Redirect-/Outcome-Meldungen verwenden, nicht als primäre Validation-Struktur.- Listen-Endpoints in
pages/**/data().phpsind GET-only (gridRequireGetRequest()). - Query-Filter in
data().phpübergridParseFiltersFromSchemaFile(...)+filter-schema.phpnormalisieren (enthältlimit,offset,order,dirim 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.phpkeine direktennew ...Factory(). - In
core/Service/**außerhalb von*Factory.phpkeine direktennew ...Service(),new ...Gateway()odernew ...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
403oder404modellieren. - API-Fehlerformat konsistent halten (
error, optionalerrors). - 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::LAYOUTbereitgestellt. - Für einzelne Views setzen Actions explizit
$viewAuth['page'](bevorzugt überUiCapabilityMap+UiAccessService::pageCapabilities()). - In
templates/**keine direktencan('...')- oder Permission-Key-Checks.
Frontend-Page-Module (MUSS)
- In
pages/**undtemplates/**sind inline<script type="module">...</script>ohnesrcverboten. - 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 überreadPageConfig(...)gelesen. - In
pages/**undtemplates/**muessen alle JS-src(eigene und Vendor) ueberassetVersion('...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