# Architektur Patterns Diese Datei definiert die konkreten Implementierungsmuster für die verschiedenen Layer der Anwendung. ## 1. UseCase Pattern (`src/Logic`) Das UseCase Pattern bildet das Herzstück der Business-Logik. Jeder Anwendungsfall wird als eigenständige Klasse implementiert, um eine klare Trennung der Verantwortlichkeiten und eine hohe Testbarkeit zu gewährleisten. ### Struktur & Definition - **Interface**: Alle UseCases müssen das `UseCaseInterface` implementieren. - **Methode**: Die primäre Logik liegt in der Methode `execute()`. - **Eingabe/Ausgabe**: Daten werden strikt über DTOs (Data Transfer Objects) ausgetauscht. - **Rückgabewert**: Ein UseCase gibt entweder ein Response-DTO zurück oder `void` (bei reinen Schreiboperationen). - **Fehlerbehandlung**: Business-Fehler werden über spezifische **Domain-Exceptions** signalisiert. ### DTOs (Data Transfer Objects) - **Typ**: Readonly-Klassen (PHP 8.2+). - **Namenskonvention**: Suffix `Request` für Eingabe, `Response` für Ausgabe. - **Typisierung**: Maximale native Typisierung; PHPDocs nur bei komplexen Types (z.B. Generics in Arrays). ### Namenskonvention & Verzeichnisstruktur - **Klassennamen**: Suffix `UseCase`. - **Pfad**: `src/Logic/{Module}/{Feature}/{Typ}/...` **Beispiel-Struktur:** - `src/Logic/Sales/Order/UseCase/CreateOrderUseCase.php` - `src/Logic/Sales/Order/Dto/CreateOrderRequest.php` - `src/Logic/Sales/Order/Dto/CreateOrderResponse.php` ### Code Beispiel #### Interface ```php namespace App\Logic\Common; /** * @template TRequest * @template TResponse */ interface UseCaseInterface { /** * @param TRequest $request * @return TResponse|void */ public function execute(mixed $request): mixed; } ``` #### Implementierung ```php namespace App\Logic\Sales\Order\UseCase; use App\Logic\Common\UseCaseInterface; use App\Logic\Sales\Order\Dto\CreateOrderRequest; use App\Logic\Sales\Order\Dto\CreateOrderResponse; use App\Logic\Sales\Order\Exception\InsufficientStockException; readonly class CreateOrderUseCase implements UseCaseInterface { public function execute(mixed $request): CreateOrderResponse { if (!$request instanceof CreateOrderRequest) { throw new \InvalidArgumentException('Invalid request type'); } // Business Logik hier... if ($this->stockTooLow()) { throw new InsufficientStockException(); } return new CreateOrderResponse(orderId: '123'); } private function stockTooLow(): bool { return false; } } ``` #### DTOs ```php namespace App\Logic\Sales\Order\Dto; readonly class CreateOrderRequest { public function __construct( public string $customerId, public array $items, ) {} } readonly class CreateOrderResponse { public function __construct( public string $orderId, ) {} } ## 2. Read Pattern (`src/Logic` $\rightarrow$ `src/Data`) Das Read Pattern beschreibt den Weg einer Datenabfrage von der UI bis zur Datenquelle. Es stellt sicher, dass die UI-Layer niemals direkt auf die Data Layer zugreift und Business-Modelle konsistent zurückgegeben werden. ### Der Datenfluss `UI Layer` $\rightarrow$ **BusinessQuery** $\rightarrow$ **Manager** $\rightarrow$ **Provider** #### BusinessQuery (Logic Layer) - **Zweck**: Dient als fachlicher Einstiegspunkt für Leseoperationen. Sie orchestriert Manager oder andere Queries, um ein Endresultat zu formen. - **Struktur**: Implementiert das gleiche Muster wie UseCases (`execute()` Methode). - **Input/Output**: Nutzt Request-DTOs (besonders für Filter und Pagination) und gibt Response-DTOs zurück. - **Pfad**: `src/Logic/{Module}/{Feature}/Query/...` #### Manager (Logic Layer) - **Zweck**: Steuert die Bereitstellung der Daten. Hier erfolgt primär die Cache-Logik ("Cache-Aside") und die Koordination von Providern. - **Verantwortung**: Entscheidung, ob Daten aus dem Cache oder frisch vom Provider geladen werden müssen. #### Provider (Data Layer) - **Zweck**: Führt die technische Abfrage gegen die Infrastruktur aus (DB, API). - **Rückgabewert**: Liefert die **Kern-Business-Models** zurück, nicht notwendigerweise die technischen Entities der Datenbank. ### Code Beispiel #### BusinessQuery & DTOs ```php namespace App\Logic\Sales\Order\Query; use App\Logic\Common\UseCaseInterface; use App\Logic\Sales\Order\Dto\GetOrdersRequest; use App\Logic\Sales\Order\Dto\GetOrdersResponse; use App\Logic\Sales\Order\Manager\OrderManager; readonly class GetOrdersQuery implements UseCaseInterface { public function __construct(private OrderManager $orderManager) {} public function execute(mixed $request): GetOrdersResponse { if (!$request instanceof GetOrdersRequest) { throw new \InvalidArgumentException('Invalid request type'); } $orders = $this->orderManager->findOrders($request->filter, $request->page); return new GetOrdersResponse(orders: $orders); } } ``` #### Manager & Provider ```php namespace App\Logic\Sales\Order\Manager; use App\Data\Sales\Order\Provider\OrderProvider; readonly class OrderManager { public function __construct(private OrderProvider $orderProvider) {} public function findOrders(array $filters, int $page): array { // Cache-Logik hier... return $this->orderProvider->fetchOrders($filters, $page); } } // --- Data Layer --- namespace App\Data\Sales\Order\Provider; readonly class OrderProvider { public function fetchOrders(array $filters, int $page): array { // DB Abfrage und Mapping zu Business-Models return []; } } ``` ```