Domain-Schicht
Das Herzstück deiner Anwendung: Hier lebt die Geschäftslogik.
Überblick der Domain-Schicht
Die Domain-Schicht ist das Herzstück einer DDD-Anwendung. Sie enthält die Kerngeschäftslogik und sollte unabhängig von Framework, Infrastruktur und Anwendungsfällen sein. In dieser Schicht werden die Geschäftsregeln, Entitäten und Wertobjekte definiert, die das Kerngeschäft repräsentieren.
Die Domain-Schicht sollte keine Abhängigkeiten zu anderen Schichten haben. Sie sollte reine PHP-Klassen enthalten, die die Geschäftslogik kapseln.
Entitäten (Model)
Entitäten sind Objekte mit einer Identität, die sich über die Zeit ändern können. Sie repräsentieren zentrale Bestandteile deiner Geschäftsdomäne.
namespace App\Domain\Model;
class Product
{
private string $id;
private string $name;
private Price $price;
public function __construct(string $id, string $name, Price $price)
{
$this->id = $id;
$this->name = $name;
$this->price = $price;
}
public function rename(string $newName): void
{
$this->name = $newName;
}
public function changePrice(Price $newPrice): void
{
$this->price = $newPrice;
}
}
Wichtige Merkmale von Entitäten:
- Entitäten haben eine eindeutige Identität (oft als ID)
- Sie kapseln Geschäftslogik und Verhalten
- Sie sollten keine Abhängigkeiten zu Framework oder Infrastruktur haben
- Sie verwenden Value Objects für komplexe Attribute
Wertobjekte (Value Objects)
Wertobjekte sind unveränderliche Objekte ohne eigene Identität, die durch ihre Attribute definiert werden. Zwei Wertobjekte mit denselben Attributen sind austauschbar.
namespace App\Domain\ValueObject;
class Price
{
private int $amountInCents;
public function __construct(int $amountInCents)
{
if ($amountInCents < 0) {
throw new \InvalidArgumentException('Preis darf nicht negativ sein.');
}
$this->amountInCents = $amountInCents;
}
public function getAmount(): int
{
return $this->amountInCents;
}
}
Wichtige Merkmale von Wertobjekten:
- Wertobjekte sind unveränderlich (immutable)
- Sie haben keine eigene Identität
- Sie validieren ihre Werte bei der Erstellung
- Sie können Geschäftslogik enthalten, die mit ihren Werten zusammenhängt
Verwende Wertobjekte für alle Attribute, die eine eigene Validierung oder Geschäftslogik haben. Das macht deinen Code robuster und verständlicher.
Repository-Interfaces
Repositories definieren die Schnittstelle für den Zugriff auf Entitäten. Die konkreten Implementierungen gehören in die Infrastrukturschicht.
namespace App\Domain\Repository;
use App\Domain\Model\Product;
interface ProductRepository
{
public function findById(string $id): ?Product;
public function save(Product $product): void;
}
Wichtige Merkmale von Repository-Interfaces:
- Repositories definieren nur die Schnittstelle, nicht die Implementierung
- Sie arbeiten mit Domänenmodellen, nicht mit DTOs oder Infrastruktur-Entitäten
- Sie verwenden Domänensprache in ihren Methodennamen
Domänenservices
Domänenservices enthalten Geschäftslogik, die nicht natürlich in eine einzelne Entität passt, sondern mehrere Entitäten oder externe Dienste koordiniert.
namespace App\Domain\Service;
use App\Domain\Model\Product;
class DiscountService
{
public function applyDiscount(Product $product, int $percent): void
{
$current = $product->getPrice();
$discount = new Price((int) ($current->getAmount() * (1 - $percent / 100)));
$product->changePrice($discount);
}
}
Wichtige Merkmale von Domänenservices:
- Domänenservices enthalten Geschäftslogik, die mehrere Entitäten betrifft
- Sie sollten stateless sein
- Sie können andere Domänenservices und Repositories verwenden
- Interfaces für externe Dienste werden in der Domäne definiert, aber in der Infrastruktur implementiert
Domänen-Events
Domänen-Events repräsentieren wichtige Geschäftsereignisse, die in der Domäne auftreten.
namespace App\Domain\Event;
class ProductRenamed
{
public function __construct(
public readonly string $productId,
public readonly string $oldName,
public readonly string $newName
) {}
}
Wichtige Merkmale von Domänen-Events:
- Events sind unveränderlich und repräsentieren etwas, das bereits geschehen ist
- Sie enthalten alle relevanten Informationen über das Ereignis
- Sie werden typischerweise von Entitäten oder Domänenservices ausgelöst
Aggregates und Aggregate Roots
In komplexeren Domänen solltest du Aggregate definieren, die zusammengehörige Entitäten und Wertobjekte gruppieren. Ein Aggregat hat eine Root-Entität (Aggregate Root), über die alle Zugriffe erfolgen müssen.
namespace App\Domain\Model;
use App\Domain\ValueObject\Price;
class ShoppingCart
{
private string $id;
private array $items = []; // array of Product
public function addProduct(Product $product): void
{
$this->items[] = $product;
}
public function getTotal(): int
{
return array_sum(array_map(fn(Product $p) => $p->getPrice()->getAmount(), $this->items));
}
}
Wichtige Merkmale von Aggregates:
- Ein Aggregat ist eine Gruppe von zusammengehörigen Objekten, die als Einheit behandelt werden
- Das Aggregate Root ist der einzige Zugangspunkt zum Aggregat
- Transaktionsgrenzen sollten mit Aggregatgrenzen übereinstimmen
- Referenzen zwischen Aggregaten sollten nur über IDs erfolgen, nicht über direkte Objektreferenzen
Halte Aggregate klein und fokussiert. Große Aggregate können zu Leistungsproblemen und Konflikten bei gleichzeitigen Änderungen führen.
Zusammenfassung
Die Domain-Schicht ist das Herzstück deiner Anwendung und sollte:
- Unabhängig von Framework und Infrastruktur sein
- Die Geschäftsregeln und -logik kapseln
- Eine klare Ubiquitous Language verwenden
- Aus Entitäten, Wertobjekten, Repositories und Domänenservices bestehen
- Keine Abhängigkeiten zu externen Systemen haben (nur über Interfaces)
Durch die saubere Trennung der Domäne von anderen Schichten wird deine Anwendung flexibler, testbarer und leichter zu verstehen.