Files
vollversammlung/Doku/architektur.md
T

3.5 KiB
Raw Blame History

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