# Backend-Architektur – Veranstaltungs-Tool (Konzept) ## Ziel Mandantenfähiges Backend mit 3 Schichten pro fachlicher Domäne und mehreren Clients/Entry-Points. ## Struktur ``` api/ ├── public/index.php ← Entrypoint └── src/ ├── UI/{ClientName}/{FeatureName}/ ← pro Client + Use-Case (PascalCase, deutsch) │ ├── Controller/ REST-Endpunkte (/api/*), Commands etc. │ ├── Request/ Request-DTOs │ └── Response/ Response-DTOs ├── Logic/{FeatureName}/ ← Business Rules pro Use-Case │ ├── Manager/ Cache, Workflow, Aggregate │ ├── Calculator/ Berechnungen (Statistiken) │ ├── Model/ Feature-Modelle │ ├── Provider/*ProviderInterface Interfaces für Data-Layer │ ├── Processor/*ProcessorInterface Interface für Verarbeitungslogik │ └── Shared/ Feature-spezifisches Shared (wenn notwendig) ├── Data/{FeatureName}/ ← Domain-Objects, DB-Zugriff pro Use-Case │ ├── Entity/ Doctrine Entities │ └── Repository/ Repo-Interfaces + Implementierungen └── Shared/ ← Nur wirklich entitätsübergreifende Basics ├── BaseEntity.php └── UuidGenerator.php ``` ## Client-Namenskonvention - `API` → REST-Schnittstelle (UI/API/*) - `Console` → Symfony Commands (UI/Console/*) ## Feature-Namenskonvention Use-Case-basiert, PascalCode auf Deutsch. ZB: - `Personen` (nicht Personenliste / PersonController) - `Veranstaltungen` (nicht VeranstaltungenIndex) - `Anwesenheit` (nicht AnwesenheitsstatusErfassung) Beispiel-Pfad: `UI/API/Personen/Controller/PersonenController.php` **Model/** pro Feature – Ordner mit ggf. mehreren Models definiert die Feature-Grenze. ## Entry-Points Mehrere Clients via Router-basierte Routing: - `/api/*` → REST-Controller (`UI/API/{Feature}/Controller/`) - Console → Symfony Commands (`UI/Console/{Feature}/Command/`) - Messages → Messenger Handler ## Entscheidungen ### Provider und Processoren **Provider** implementieren die `*ProviderInterface`. Sie rufen Daten ab – per Doctrine Repositories/Entities oder über API-Aufrufe. Das Mapping zwischen Entities und Model-Objekten erfolgt in separaten **Mapper**-Klassen, die in **Data/** liegen. Provider binden so die Data-Schicht an die Logic-Schicht. **Processoren** funktionieren analog für schreibende Prozesse (`*ProcessorInterface`). ### Shared-Klassen Echt entitätsbergreifende Basis-Klassen (`BaseEntity`, `UuidGenerator`) leben in `src/Shared/`. Feature-spezifisches Shared liegt innerhalb des Features, zB: ``` src/Logic/Veranstaltungsmanagement/Shared/JsonSerializer.php ``` ### Multi-Tenant Scoping Alle Repository-Queries werden per **Doctrine SQL Builder Trait** mit `mandant_id` gefiltert. Der Scope wird automatisch angehängt – sowohl lesend als auch schreibend. Tenantübergreifende Operationen sind technisch nicht möglich, da alle Repos implicit scopen. Das schützt vor unbeabsichtigten Mandanten-Überschneidungen. Bei Bedarf kann eine explizite WithoutScope-Schicht eingeführt werden. ### Dependency Injection Nur explizit über `services.yaml`, wenn notwendig. Standardmäßig wird Auto-Wiring mit Konventionen verwendet (`*ProviderInterface`, `*ProcessorInterface`).