Compare commits

5 Commits

Author SHA1 Message Date
jens 468a08c5a3 Doku/Tasks/03_routen.md hinzugefügt 2026-06-17 17:38:10 +00:00
jens 3226205889 Doku/php.md hinzugefügt 2026-06-17 17:09:06 +00:00
jens 0f9913faa2 Doku/architektur.md aktualisiert 2026-06-17 15:50:21 +00:00
jens ba87c9773c Doku/Tasks/02_datenmodel.md hinzugefügt 2026-06-14 16:24:32 +00:00
jens 2094b71335 Merge pull request 'Feat/01 infra' (#1) from feat/01_infra into main
Reviewed-on: #1
2026-06-14 16:23:10 +00:00
4 changed files with 152 additions and 21 deletions
+18
View File
@@ -0,0 +1,18 @@
Dokument 2: Datenmodell & Persistenz (Backend)
Ziel: Implementierung der relationalen Struktur zur Verwaltung des Saatgut-Bestands.
Entity-Definitionen (Doctrine ORM):
Kategorie: id, name (unique), farbe.
Pflanze: id, art_name, sorten_name, kategorie_id (FK → Kategorie), beschreibung.
SaatgutBestand: id, nutzer_id (String aus Keycloak), pflanze_id (FK → Pflanze), menge, kaufdatum, ablaufdatum, notizen.
Daten-Integrität:
Kaskadierendes Löschen: Wenn eine Kategorie gelöscht wird, muss entschieden werden, wie mit den Pflanzen verfahren wird (z.B. Setzen auf null oder Mitlöschen).
Unique Constraint für nutzer_id + pflanze_id, um doppelte Einträge derselben Sorte pro Nutzer zu vermeiden (optional, je nach Wunsch).
Akzeptanzkriterien:
Datenbank-Migrationen sind erstellt und erfolgreich ausgeführt.
Testdaten für den globalen Katalog (Kategorie & Pflanze) wurden importiert.
Die Beziehungen zwischen den Tabellen sind via Doctrine korrekt abgebildet.
+35
View File
@@ -0,0 +1,35 @@
## 1. Zielsetzung
Bereitstellung einer REST-API zur Verwaltung des Saatgut-Inventars inklusive strikter Sicherheitsprüfungen und Logik-Validierungen.
## 2. Endpunkt-Definitionen
### 2.1 Katalog (Public/Read)
| Endpunkt | Methode | Parameter | Antwort | AK / Testfall |
| :--- | :--- | :--- | :--- | :--- |
| `/api/kategorien` | GET | - | `200 OK` [JSON] | Liste ist nicht leer, wenn DB gefüllt ist. |
| `/api/pflanzen` | GET | `?kategorie_id=X`, `?suche=Y` | `200 OK` [JSON] | Filterung reduziert Ergebnismenge korrekt. |
### 2.2 Inventar (Protected / JWT required)
| Endpunkt | Methode | Parameter | Antwort | AK / Testfall |
| :--- | :--- | :--- | :--- | :--- |
| `/api/mein-saatgut` | GET | - | `200 OK` [JSON] | Nur Einträge der eigenen `nutzer_id` sichtbar. |
| `/api/mein-saatgut` | POST | Body (JSON) | `201 Created` | Fehlende Pflichtfelder $\rightarrow$ `400 Bad Request`. |
| `/api/mein-saatgut/{id}`| PATCH | Body (JSON) | `200 OK` | Fremde ID $\rightarrow$ `403 Forbidden`. |
| `/api/mein-saatgut/{id}`| DELETE | - | `204 No Content`| Fremde ID $\rightarrow$ `403 Forbidden`. |
## 3. Logik-Schicht (Service-Layer) & Unit Tests
Die Geschäftslogik wird in dedizierten Services implementiert und unabhängig vom HTTP-Kontext getestet.
### 3.1 Business Rules (AKs für Logik-Tests)
- [ ] **Validierung Ablaufdatum:** Der Service muss eine Exception werfen, wenn das `kaufdatum` zeitlich nach dem `ablaufdatum` liegt.
- [ ] **Katalog-Existenz:** Bevor ein Eintrag in `SaatgutBestand` erstellt wird, muss geprüft werden, ob die `pflanze_id` im Katalog existiert $\rightarrow$ sonst Fehler werfen.
- [ ] **Besitzprüfung:** Die Methode `isOwner(Nutzer, SaatgutEintrag)` muss strikt prüfen, ob die IDs übereinstimmen.
### 3.2 Test-Matrix
| Ebene | Tool | Fokus | Ziel |
| :--- | :--- | :--- | :--- |
| **Functional** | `WebTestCase` | End-to-End API Flow | HTTP Statuscodes & JSON Struktur prüfen. |
| **Unit** | `PHPUnit` | Business Logic | Edge Cases (z.B. Datumsfehler) isoliert testen. |
| **Integration**| JWT/Mock | Security / Isolation | Sicherstellen, dass Nutzer A niemals Daten von B sieht. |
+31 -21
View File
@@ -5,9 +5,9 @@ Das Ziel dieser Architektur ist eine strikte Trennung zwischen technischer Infra
### Grundregeln:
* **Unidirektionaler Fluss:** `UI` $\rightarrow$ `Logic` $\rightarrow$ `Data`. Ein Layer darf niemals Informationen aus einem übergeordneten Layer importieren.
* **Dependency Inversion:** Die `Logic`-Schicht definiert die Anforderungen (Interfaces). Die `Data`-Schicht implementiert diese. Die Logik ist somit unabhängig von der Datenbank-Technologie.
* **Model-Zentrierung:** Das Model ist das Herzstück der Applikation. Es ist ein anämisches POPO (Plain Old PHP Object) und wird primär im gesamten System verwendet.
* **Atomarität:** Business-Operationen sind atomar. Entweder alles wird gespeichert oder nichts (Transaction Management).
* **Dependency Inversion:** Die `Logic`-Schicht definiert die Anforderungen (Interfaces). Die `Data`-Schicht implementiert diese. Die Logik ist somit vollständig unabhängig von der Datenbank-Technologie oder externen APIs.
* **Model-Zentrierung:** Das Model ist das Herzstück der Applikation. Es ist ein anämisches POPO (Plain Old PHP Object) und dient als primärer Datenträger zwischen den Schichten.
* **Atomarität:** Business-Operationen sind atomar. Entweder alles wird gespeichert oder nichts (Transaction Management via `TransactionManagerInterface`).
---
@@ -19,40 +19,40 @@ Die UI-Schicht ist die dünne Eintrittspforte. Sie besitzt keine Geschäftslogik
* **Verantwortlichkeiten:**
* Mapping von Input auf **DTOs**.
* Syntaktische Validierung via Symfony Constraints am DTO.
* Grobe Autorisierungsprüfung (Rollen-basiert, z.B. `ROLE_USER`).
* Grobe Autorisierungsprüfung (Rollen-basiert).
* Aufruf des entsprechenden **UseCase**.
### 🔵 Logic Layer (Business Core)
Hier wird definiert, *was* das System tut. Die Logik ist in Orchestrierung und zustandslose Fachlogik unterteilt.
#### A. Orchestrierung & State
* **UseCase:** Der Dirigent eines Business-Prozesses. Er koordiniert den Ablauf: `Authorization` $\rightarrow$ `Validation` $\rightarrow$ `Calculation` $\rightarrow$ `Save`. Er steuert die Transaktionsgrenzen via `TransactionManagerInterface`.
* **Manager:** Zuständig für das **State Management**. Er implementiert das *Cache-Aside Pattern* (prüft Cache vor dem Provider) und steuert die Cache-Invalidierung nach Schreibvorgängen.
* **UseCase:** Der Dirigent eines Business-Prozesses. Er koordiniert: `Authorization` $\rightarrow$ `Validation` $\rightarrow$ `Calculation` $\rightarrow$ `Save`. Er steuert die Transaktionsgrenzen.
* **Manager:** Zuständig für das **State Management**. Implementiert das *Cache-Aside Pattern* (Prüfung Cache vor Provider) und steuert die Invalidierung via Cache-Tags nach Schreibvorgängen.
#### B. Spezialisierte Logik (Stateless Services)
Um "Fat Managers" zu vermeiden, wird Fachlogik in spezialisierte Klassen ausgelagert:
Zur Vermeidung von "Fat Managers" wird Fachlogik ausgelagert:
* **Calculators:** Pure Functions für Berechnungen $\rightarrow$ gibt Werte zurück.
* **Policies:** Business-Regelprüfungen $\rightarrow$ gibt `boolean` zurück.
* **Validators:** Komplexe Zustandsprüfungen $\rightarrow$ wirft `ValidationException`.
* **Validators:** Komplexe semantische Zustandsprüfungen $\rightarrow$ wirft `DomainException`.
* **Strategies:** Kapselung austauschbarer Algorithmen.
#### C. Domain Objekte
* **Models:** Die primären Datencontainer (z.B. `GardenPlan`). Anämisch und unabhängig von der DB.
* **Models:** Primäre Datencontainer (z.B. `GardenPlan`). Anämisch und unabhängig von der DB.
* **DTOs:** Transportobjekte zwischen UI und Logic.
* **Domain Events:** Dünne Ereignisse (ID & Typ), die via `DomainEventCollector` gesammelt und nach dem Commit gefeuert werden.
* **Domain Events:** Dünne Ereignisse, die manuell im UseCase nach erfolgreichem Commit getriggert werden.
### 🔴 Data Layer (Infrastructure)
Die technische Realisierung. Alles hier ist austauschbar, solange die Interfaces der Logic-Schicht erfüllt werden.
* **Provider / Processor:** Implementierungen der in der Logic definierten Interfaces. Provider = Lesen | Processor = Schreiben.
* **Entities:** Die technischen Repräsentationen für Doctrine (z.B. `GardenPlanEntity`).
* **Mappers:** Explizite Klassen, die zwischen `Entity` $\leftrightarrow$ `Model` transformieren.
* **Provider / Processor:** Implementierungen der Interfaces (`Provider` = Lesen | `Processor` = Schreiben).
* **Entities:** Technische Repräsentationen für Doctrine (z.B. `GardenPlanEntity`).
* **Mappers:** Explizite Klassen, die zwischen `Entity` $\leftrightarrow$ `Model` transformieren (KI-generiert/manuell).
---
## 3. Technischer Workflow & Pipeline
### Der Schreib-Pfad (Write Flow)
`UI (Controller/Command)` $\rightarrow$ `DTO` $\rightarrow$ `UseCase (Start Transaction)` $\rightarrow$ `Auth Check (Voter)` $\rightarrow$ `Validator` $\rightarrow$ `Policy` $\rightarrow$ `Calculator` $\rightarrow$ `Manager` $\rightarrow$ `Processor` $\rightarrow$ `Mapper` $\rightarrow$ `Entity` $\rightarrow$ `DB`.
`UI` $\rightarrow$ `DTO` $\rightarrow$ `UseCase (Start Transaction)` $\rightarrow$ `Auth Check` $\rightarrow$ `Validator` $\rightarrow$ `Policy` $\rightarrow$ `Calculator` $\rightarrow$ `Manager` $\rightarrow$ `Processor` $\rightarrow$ `Mapper` $\rightarrow$ `Entity` $\rightarrow$ `DB` $\rightarrow$ `Commit` $\rightarrow$ `Trigger Events`.
### Der Lese-Pfad (Read Flow)
`UI` $\rightarrow$ `UseCase` $\rightarrow$ `Manager (Cache check)` $\rightarrow$ `Provider Interface` $\rightarrow$ `Implementation` $\rightarrow$ `Mapper` $\rightarrow$ `Model` $\rightarrow$ `UI`.
@@ -61,13 +61,23 @@ Die technische Realisierung. Alles hier ist austauschbar, solange die Interfaces
## 4. Querschnittsfunktionen
### Transaktionssteuerung & Fehler
* **Transaction Management:** Der UseCase nutzt ein `TransactionManagerInterface`, um sicherzustellen, dass alle Änderungen innerhalb eines Prozesses atomar erfolgen.
* **Error Handling:** Eigene Domain-Exceptions in der Logic. Ein zentraler **Symfony ExceptionListener** mappt diese auf HTTP-Statuscodes (400, 403, 404, etc.).
### Dependency Injection & Composition Root
Um eine strikte Entkopplung zu gewährleisten, wird die `Logic`-Schicht niemals direkt auf konkrete Implementierungen referenzieren (kein hartcodiertes `#[Autowire(service: ...)]`).
* **Implicit Autowiring:** Bei einer einzigen Implementierung pro Interface nutzt Symfony das Standard-Autowiring.
* **Composition Root:** Die `services.yaml` fungiert als zentrale Konfigurationsinstanz, in der Interfaces auf konkrete Implementierungen gemappt werden (Alias/Bind), um die Infrastruktur von außen steuerbar zu machen.
### Asynchronität (Messenger)
* **Inbound:** `Message` $\rightarrow$ `Handler` $\rightarrow$ `DTO` $\rightarrow$ `UseCase`.
* **Outbound:** Nachrichten werden erst **nach** dem erfolgreichen Commit der Transaktion verschickt (Post-Commit Trigger), um "Ghost Notifications" zu vermeiden.
### Fehlermanagement & Exception-Handling
Ein zentrales System mappt fachliche Fehler auf HTTP-Standards:
* **Hierarchie:** Alle Fachfehler erben von einer Basis-`DomainException`.
* **Mapping:** Ein `Symfony ExceptionListener` fängt diese ab und mappt sie auf Statuscodes:
* `ResourceNotFoundException` $\rightarrow$ 404
* `AccessDeniedException` $\rightarrow$ 403
* `BusinessRuleViolationException` $\rightarrow$ 422/400
* **Format:** Die Antwort erfolgt im **RFC 7807 (Problem Details for HTTP APIs)** Format, um dem Frontend präzise, maschinenlesbare Fehler zu liefern.
### Transaktionssteuerung & Asynchronität
* **Transaction Management:** Abstrahiert über ein `TransactionManagerInterface`.
* **Messenger:** Outbound-Nachrichten werden erst nach erfolgreichem DB-Commit versendet, um inkonsistente Benachrichtigungen ("Ghost Notifications") zu vermeiden.
---
@@ -114,4 +124,4 @@ src/
│ └── Implementation/
│ ├── Provider/[Feature]/ # Realisierung der Interfaces
│ └── Processor/[Feature]/ # Realisierung der Interfaces
```
```
+68
View File
@@ -0,0 +1,68 @@
# PHP & Symfony Engineering Guidelines
## 1. Core PHP Standard
Wir schreiben modernen, typensicheren und expliziten Code. Ziel ist es, Fehler zur Compile-Zeit bzw. via statischer Analyse zu finden.
* **Strict Typing:** Jede Datei beginnt zwingend mit `declare(strict_types=1);`.
* **Typisierung:**
* Konsequentes Type Hinting für alle Parameter und Rückgabewerte (inkl. `void`).
* Nutzung von Union- und Intersection Types.
* `mixed` ist verboten, außer in absolut unvermeidbaren Generics.
* **Modern Features (PHP 8.2+):**
* **Readonly:** Standard für DTOs, Value Objects und Services (`readonly class`).
* **Constructor Promotion:** Nutzung zur Reduktion von Boilerplate.
* **Enums:** Einsatz anstelle von Konstanten oder Strings für feste Zustände.
* **Match Expression:** Bevorzugt gegenüber `switch`.
* **Anti-Magic:** Magic Methods (`__call`, `__get` etc.) und dynamische Properties sind **strikt verboten**.
## 2. Control Flow & Readability
Lesbarkeit schlägt akademische Perfektion. Code muss für andere Senioren ohne "Rätselraten" sofort verstehbar sein.
* **Control Flow:** Nutzung von **Early Returns** und **Guard Clauses**. Verschachtelte `if-else`-Strukturen sind zu vermeiden, um die kognitive Last gering zu halten.
* **Self-Explaining Code:** Namen müssen den Intent beschreiben (`calculateTotalTax()` statt `calc()`). Booleans beginnen mit `is`, `has` oder `can`.
* **Kommentare:** Kommentare werden nur eingesetzt, wenn:
1. Der Code aufgrund einer Sonderlösung **nicht selbsterklärend** ist (Dokumentation des "Warum").
2. **Arrays/Collections typisiert** werden müssen (`/** @var UserDTO[] $users */`), sofern PHPStan dies nicht anders erfassen kann.
## 3. Data Structures & Collections
Wir vermeiden "Mystery Meat"-Datenstrukturen zugunsten von Typsicherheit.
* **No Associative Arrays:** Die Nutzung von assoziativen Arrays als Pseudo-Objekte/Datencontainer ist verboten. Es werden immer **dedizierte Objekte (DTOs)** verwendet.
* **Collections:**
* **Transport:** Für einfache Listen, die nur durchiteriert werden, reichen typisierte Arrays (`array<T>`).
* **Domain-Logik:** Sobald eine Liste fachliche Logik besitzt (z.B. Filterung, Aggregation), wird eine dedizierte **Collection-Klasse** erstellt (implementiert `IteratorAggregate` und `Countable`).
## 4. Infrastructure & Symfony Implementation
Symfony ist der Adapter zur Außenwelt, nicht das Zentrum der Applikation.
* **Dependency Injection:** Ausschließlich **Constructor Injection**. Kein Service-Locator (`$this->container->get()`). DI erfolgt primär gegen Interfaces (Ports).
* **Controller:** "Thin Controller". Aufgaben sind: Request-Daten validieren/extrahieren $\rightarrow$ Application-Service aufrufen $\rightarrow$ Response transformieren. Keine Business-Logik im Controller.
* **Third-Party SDKs:** Alle externen Services/SDKs werden konsequent hinter einem **eigenen Interface (Port)** in der Infrastructure-Schicht gekapselt. Vendor-Klassen dürfen niemals den Application- oder Domain-Layer erreichen.
## 5. Persistence & Entity Design
Wir minimieren die Koppelung an das ORM, um Performance und Testbarkeit zu sichern.
* **Simple Entities:** Entitäten werden flach gehalten.
* **Avoid Joins:** Gejointe Entitäten (komplexe `@ManyToOne` / `@OneToMany`) sind grundsätzlich zu vermeiden. Referenzen erfolgen über **IDs**. Die Zusammenführung erfolgt explizit in den Repositories/Services.
* **Mapping:** Transformationen zwischen Infrastructure-Entities und Domain-Modellen erfolgen ausschließlich über dedizierte **Mapper-Klassen in der Dataschicht**.
## 6. Validation Pipeline (Shift-Left)
Fehler werden so früh wie möglich abgefangen:
`Request` $\rightarrow$ `DTO Asserts/Validation` $\rightarrow$ `Application Logic` $\rightarrow$ `Domain Complex Tests`.
## 7. Quality Gates & Workflow (Definition of Done)
Bevor Code in ein Review geht, müssen folgende lokale Checks erfolgreich durchlaufen sein:
1. **Syntax Check:** `php -l`
2. **Statische Analyse:** `phpstan analyse` (Ziel Level 8/9). *Pragmatismus-Regel:* Wenn Typisierung die Lesbarkeit massiv zerstört $\rightarrow$ Review-Dialog statt "Force-Typing".
3. **Automatisierte Tests:** `phpunit` (Unit, Integration, Functional).
---
### Die Golden Rules im Überblick:
1. **Kein Framework / Keine Vendor-Klassen im Domain.**
2. **Flache Entitäten / Referenzen über IDs.**
3. **Immutability by Default (`readonly`).**
4. **Keine assoziativen Arrays $\rightarrow$ Dedizierte Objekte.**
5. **Early Returns $\rightarrow$ Flacher Code.**
6. **Local Check: `php -l` $\rightarrow$ `phpstan` $\rightarrow$ `phpunit`.**