docs: document full and partial update patterns for readonly Models

This commit is contained in:
2026-07-17 11:38:25 +02:00
parent 8858e9d762
commit d3b2fad8a9
+76 -2
View File
@@ -594,7 +594,10 @@ Um die UseCases schlank zu halten, findet die Transformation von Eingabe-DTOs (`
- **Ort**: `src/Logic/{Module}/{Feature}/Mapping/`
- **Verantwortung**: Abbildung der Struktur von einem DTO auf ein Business Model (einschließlich Child-Models).
- **Dependency Rule**: Diese Mappers liegen zwingend in der Logic Layer und kennen keine Entitys oder Provider.
- **Dependency Rule**: Diese Mapper liegen zwingend in der Logic Layer und kennen keine Entities oder Provider.
#### Factory-Pattern (Create-Szenario)
Für neue Objekte liefert die Factory ein komplett neues, valides Business-Model.
```php
namespace App\Logic\Sales\Order\Mapping;
@@ -610,7 +613,7 @@ readonly class OrderModelFactory
{
return new Order(
customerId: $request->customerId,
// Delegierung der Child-Objekte
// Delegierung der Child-Objects
items: array_map(
fn($data) => new OrderItem(productId: $data['productId'], quantity: $data['quantity']),
$request->items
@@ -621,6 +624,77 @@ readonly class OrderModelFactory
}
```
#### Factory-Pattern für Updates (Full Rebuild)
Das einfachste Verfahren. Die Factory erhält den Request-DTO und erstellt wie gewohnt ein neues Model mit allen Daten des Requests — inklusive der ID des bestehenden Objekts. Da Models im Logic Layer rein funktional sind, ist das Erstellen eines neuen Modells billiger als ein Objekt zu kopieren und zu manipulieren.
```php
namespace App\Logic\Sales\Order\Mapping;
use App\Logic\Sales\Order\Dto\UpdateOrderRequest; // Enthält id:, email:, amount: ...
use App\Logic\Sales\Order\Model\Order;
readonly class OrderModelFactory
{
public function updateFromRequest(UpdateOrderRequest $request): Order
{
return new Order(
id: $request->id, // ID aus dem Request übernehmen
customerEmail: $request->customerEmail ?? null,
totalAmount: $request->totalAmount,
// ... restliche Felder (bei Partial Updates ggf. per Provider ergänzt)
);
}
}
```
#### Kopier-Pattern für Partial-Updates (Model.copyWith)
Wenn nur bestimmte Felder eines bestehenden Business-Models geändert werden sollen, empfiehlt es sich, eine **copyWith**-Methode im Model selbst zu implementieren:
```php
namespace App\Logic\Sales\Order\Model;
readonly class Order
{
public function __construct(
public string $id,
public string $customerEmail,
public float $totalAmount,
public int $version,
) {}
/**
* Erstellt eine Kopie und überschreibt nur die mitgegebenen Felder.
*/
public function with(
?string $customerEmail = null,
?float $totalAmount = null,
): self {
return new self(
id: $this->id,
customerEmail: $customerEmail ?? $this->customerEmail,
totalAmount: $totalAmount ?? $this->totalAmount,
version: 0, // Wird beim Speichern durch den Data Layer aktualisiert.
);
}
/**
* Erstellt eine Version-Kopie für ein neues Update (Concurrency Handling).
*/
public function nextVersion(): self
{
return new self(
id: $this->id,
customerEmail: $this->customerEmail,
totalAmount: $this->totalAmount,
version: $this->version + 1,
);
}
}
// Verwendung im UseCase oder in der Factory
$updatedOrder = $existingOrder->with(customerEmail: 'new@example.com', totalAmount: 123.45);
```
### Code Beispiel (Data Layer Mapping)
#### Mapper Implementierung