Add OrderManagerInterface to Manager example; type all dependencies on interfaces

This commit is contained in:
2026-07-16 18:25:37 +02:00
parent 9e14bd4a71
commit 4682029017
+33 -20
View File
@@ -132,11 +132,11 @@ namespace App\Logic\Sales\Order\Query;
use App\Logic\Common\UseCaseInterface; use App\Logic\Common\UseCaseInterface;
use App\Logic\Sales\Order\Dto\GetOrdersRequest; use App\Logic\Sales\Order\Dto\GetOrdersRequest;
use App\Logic\Sales\Order\Dto\GetOrdersResponse; use App\Logic\Sales\Order\Dto\GetOrdersResponse;
use App\Logic\Sales\Order\Manager\OrderManager; use App\Logic\Sales\Order\Manager\OrderManagerInterface;
readonly class GetOrdersQuery implements UseCaseInterface readonly class GetOrdersQuery implements UseCaseInterface
{ {
public function __construct(private OrderManager $orderManager) {} public function __construct(private OrderManagerInterface $orderManager) {}
public function execute(mixed $request): GetOrdersResponse public function execute(mixed $request): GetOrdersResponse
{ {
@@ -151,16 +151,27 @@ readonly class GetOrdersQuery implements UseCaseInterface
} }
``` ```
#### Manager (Read + Cache) #### Manager Interface (Logic Layer)
```php ```php
namespace App\Logic\Sales\Order\Manager; namespace App\Logic\Sales\Order\Manager;
use App\Data\Sales\Order\Provider\OrderProvider; interface OrderManagerInterface
{
public function findOrders(array $filters, int $page): array;
public function invalidateCache(): void;
}
```
readonly class OrderManager #### Manager Implementierung (Read + Cache)
```php
namespace App\Logic\Sales\Order\Manager;
use App\Logic\Sales\Order\OrderProviderInterface;
readonly class OrderManager implements OrderManagerInterface
{ {
public function __construct( public function __construct(
private OrderProvider $orderProvider, private OrderProviderInterface $orderProvider,
// private CacheInterface $cache, <-- Symfony/PSR-6 Cache (injected via DI) // private CacheInterface $cache, <-- Symfony/PSR-6 Cache (injected via DI)
) {} ) {}
@@ -276,12 +287,12 @@ namespace App\Logic\Sales\Order\UseCase;
use App\Logic\Common\UseCaseInterface; use App\Logic\Common\UseCaseInterface;
use App\Logic\Sales\Order\Dto\CreateOrderRequest; use App\Logic\Sales\Order\Dto\CreateOrderRequest;
use App\Logic\Sales\Order\Dto\CreateOrderResponse; use App\Logic\Sales\Order\Dto\CreateOrderResponse;
use App\Logic\Sales\Order\Manager\OrderManager; use App\Logic\Sales\Order\Manager\OrderManagerInterface;
readonly class CreateOrderUseCase implements UseCaseInterface readonly class CreateOrderUseCase implements UseCaseInterface
{ {
public function __construct( public function __construct(
private OrderManager $orderManager, private OrderManagerInterface $orderManager,
// TransactionManager ggf. für Multi-Processor-Szenarien // TransactionManager ggf. für Multi-Processor-Szenarien
) {} ) {}
@@ -417,28 +428,28 @@ interface TransactionManagerInterface
} }
``` ```
#### 2. Anwendung im UseCase (`src/Logic`) #### Anwendung im UseCase (`src/Logic`)
Der UseCase nutzt den TransactionManager, um sicherzustellen, dass entweder alle oder keine Änderungen persistiert werden (All-or-Nothing). Der UseCase nutzt den TransactionManager, um sicherzustellen, dass entweder alle oder keine Änderungen persistiert werden (All-or-Nothing). Die Orchestrierung zwischen Managern erfolgt innerhalb der Transaktions-Closure.
```php ```php
readonly class CreateOrderUseCase implements UseCaseInterface readonly class CreateOrderUseCase implements UseCaseInterface
{ {
public function __construct( public function __construct(
private TransactionManagerInterface $transactionManager, private TransactionManagerInterface $transactionManager,
private OrderProcessorInterface $orderProcessor, private OrderManager $orderManager,
private StockProcessorInterface $stockProcessor private StockManager $stockManager
) {} ) {}
public function execute(mixed $request): CreateOrderResponse public function execute(mixed $request): CreateOrderResponse
{ {
return $this->transactionManager->transactional(function() use ($request) { return $this->transactionManager->transactional(function() use ($request) {
// 1. Produktbestand reduzieren // 1. Produktbestand reduzieren (Manager orchestriert Processor + Cache)
$this->stockProcessor->reduceStock($request->items); $this->stockManager->reduceStockForOrder($request->items);
// 2. Bestellung anlegen // 2. Bestellung anlegen (Manager orchestriert Processor + Cache)
$order = $this->orderProcessor->save($request->toModel()); $order = $this->orderManager->createOrder($request);
return new CreateOrderResponse(orderId: $order->id); return new CreateOrderResponse(orderId: $order.id);
}); });
} }
} }
@@ -466,9 +477,11 @@ readonly class DoctrineTransactionManager implements TransactionManagerInterface
### Vorteile ### Vorteile
- **Kein Leaking**: Die Logic Layer weiß nicht, dass Doctrine verwendet wird. - **Kein Leaking**: Die Logic Layer weiß nicht, dass Doctrine verwendet wird.
- **Atomarität**: Mehrere Processor können konsistent in einer Transaktion kombiniert werden. - **Atomarität**: Mehrere Manager können konsistent in einer Transaktion orchestriert werden.
- **Testbarkeit**: Der TransactionManager kann in Unit Tests einfach durch einen Mock ersetzt werden, der den Closure direkt ausführt. - **Testbarkeit**: Der TransactionManager kann in Unit Tests einfach durch einen Mock ersetzt werden, der den Closure direkt ausführt.
Die Manager-Konzepte werden in Sektion 3 (Write Pattern) detailliert beschrieben. Die UseCases arbeiten auf Managern, die ihrerseits Processors aufrufen.
## 6. Entity vs. Business Model (`src/Data` $\rightarrow$ `src/Logic`) ## 6. Entity vs. Business Model (`src/Data` $\rightarrow$ `src/Logic`)
Um die Geschäftslogik vollständig vom Framework und dem ORM zu entkoppeln, wird eine strikte Trennung zwischen Persistenz-Objekten (Entities) und Domänen-Objekten (Business Models) eingeführt. Um die Geschäftslogik vollständig vom Framework und dem ORM zu entkoppeln, wird eine strikte Trennung zwischen Persistenz-Objekten (Entities) und Domänen-Objekten (Business Models) eingeführt.