Playbook

DDD im Code: Entity, Value Object und Aggregate (Ddd Im Code Entity Value Object Und Aggregate)

Wie funktionieren taktische DDD-Patterns? Value Objects, Entities, Aggregates, Domain Services, Application Services und Repository-Grenzen am Beispiel einer…

DDD tactical patterns showing value objects, entities, and aggregate boundaries

Die Frage dieses Kapitels

Das erste Kapitel begann mit Geschäftssprache und Regeln. Jetzt lautet die Frage: Wo leben diese Entscheidungen im Code? Wir bleiben bei derselben E-Commerce-Domain: Geld, Bestellpositionen, Bestellung und Zahlungsablauf.

Money + Address + Quantity → Value Object
OrderLine + Order → Entity
Order (Root) + OrderLine → Aggregate
Repository → lädt und speichert den Aggregate Root

Begriffe bei der ersten Verwendung

📦 Value Object
Ein Konzept ohne Identität, das durch seinen Wert definiert und unveränderlich gehalten wird.

📦 Entity
Ein Objekt, das durch seine Identität dasselbe bleibt, auch wenn sich Attribute ändern.

📦 Aggregate
Die Transaction Boundary für Objekte, die gemeinsam konsistent bleiben müssen.

📦 Aggregate Root
Der einzige externe Einstieg in ein Aggregate.

Zwei Money(100, "TRY")-Werte stehen für denselben Wert; zwei Bestellungen sind trotz gleichem Gesamtbetrag verschieden.

Mit Value Objects beginnen

Value Objects vermitteln die DDD-Denkweise besonders klar. Ein decimal ist nicht von selbst Geld: Währung, Rundung, Rabatt und Steuerregeln gehören zum Konzept. Bewahren Sie diese Regeln beim Konzept auf, statt sie über Services zu verstreuen.

public sealed record Money(decimal Amount, string Currency)
{
    public Money Add(Money other)
    {
        if (Currency != other.Currency) throw new DomainException("Currency mismatch");
        return new Money(Amount + other.Amount, Currency);
    }
}

Value Objects sind immutable. Ein geänderter Wert wird zu einer neuen Instanz und verhindert Side Effects zwischen Preis-, Liefer- und Zahlungsschritten. Gleichheit ist strukturell: O(m) für m Felder, aber eine Regel an einer Stelle senkt spätere Änderungskosten.

Entity: Identität und Lebenszyklus

Order ist eine Entity. Adresse und Betrag können sich ändern, dennoch bleibt es dieselbe Bestellung. Entscheidend ist nicht, Setter zu vermeiden, sondern einen Übergang als Geschäftsverhalten zu modellieren.

❌ order.Status = Paid
✓ order.ConfirmPayment(payment)

❌ order.Total = total - discount
✓ order.ApplyDiscount(discount)

ConfirmPayment prüft Zahlungsnachweis, Stornierungszustand und den erlaubten Übergang an einer Stelle. Eine Entity muss ungültige Zustände in ihrem Lebenszyklus verhindern.

Aggregate: keine Objektgruppe, sondern Konsistenzgrenze

Bestellung und Positionen müssen in einer Transaction konsistent bleiben: Menge ist positiv, Total entspricht den Positionen, und nach Bestätigung dürfen Positionen unveränderlich sein. Daher ist Order der Aggregate Root von OrderLine.

Order aggregate
  ├─ OrderLine
  ├─ ShippingAddress
  └─ Total

Externer Code → Order.AddLine() / Order.ConfirmPayment()
Externer Code ↛ schreibt nicht direkt in OrderLine

Ein großes Aggregate schafft keine Sicherheit, sondern Locks und Versionskonflikte. Aktualisieren Sie pro Write möglichst ein Aggregate. Referenzieren Sie andere Aggregates per ID und koordinieren Sie mit Application Orchestration oder Domain Events.

Domain Service, Application Service und Repository

Ein Domain Service modelliert reine Domain-Logik, die keinem einzelnen Aggregate gehört, etwa Wechselkurs-Preisfindung. Ein Application Service orchestriert den Use Case: Bestellung laden, Verhalten aufrufen, speichern, Transaction abschließen.

Layer Verantwortung
Value Object / Entity / Aggregate Geschäftsregeln und Invariants schützen
Domain Service Reine Domain-Berechnung ohne einzelnen Aggregate Owner
Application Service Use-Case-Orchestrierung, Transactions, Adapter

Ein Repository ist keine Tabellen-API. Es bietet Collection-artige Persistenz nur für Aggregate Roots. Direktes Laden einer OrderLine umgeht Root und Regeln.

order = orderRepository.get(orderId)
order.confirmPayment(payment)
orderRepository.save(order)

Diese Grenze macht Domain-Verhalten auch mit einem Fake Repository testbar, ohne das Model nach einem ORM zu formen.

Zuordnungen, die falsche Sicherheit erzeugen

❌ Value Object = kleiner DTO
✓ Es trägt Wert, Validierung und Verhalten.

❌ Aggregate = größtmöglicher Objektgraph
✓ Es ist eine bewusst kleine Transaction und Consistency Boundary.

❌ Domain Service = Ablage für Regeln
✓ Es ist reine Domain-Logik, die keinem einzelnen Aggregate gehört.

❌ Repository = CRUD API für jede Tabelle
✓ Es ist die Persistenzgrenze eines Aggregate Root.

Implementierungs-Checkliste

  1. Wandern Money, Email, Address und Quantity als Primitive durch den Code?
  2. Verhindert jedes Entity-Verhalten einen echten ungültigen Zustand?
  3. Aktualisiert ein Write mehrere Aggregates atomar?
  4. Entscheidet der Application Service Regeln oder orchestriert er nur?
  5. Laden Repositories nur Aggregate Roots?

Beginnen Sie mit der teuersten Geschäftsregel; wandeln Sie nicht das ganze System gleichzeitig um.

Was im Gedächtnis bleiben sollte

Ein Value Object trägt Wert und Regeln eines Konzepts. Eine Entity schützt Identität und Lebenszyklus. Ein Aggregate zieht eine kleine Transaction Boundary für Konsistenz. Ein Repository verbindet diese Grenze mit Persistenz.

Taktisches DDD bedeutet nicht mehr Klassen, sondern verhindert, dass Geschäftsregeln in die falsche Schicht gelangen.

Im nächsten Kapitel betrachten wir diese Grenzen im großen System: Bounded Contexts, Context Mapping und Trennung innerhalb eines Monolithen.

FAQ

Häufige Fragen

Was ist Value Object?

Ein Konzept ohne Identität, das durch seinen Wert definiert und unveränderlich gehalten wird.

Was ist Entity?

Ein Objekt, das durch seine Identität dasselbe bleibt, auch wenn sich Attribute ändern.

Stimmt es, dass „Value Object = kleiner DTO“?

Es trägt Wert, Validierung und Verhalten.

Was legt dieser Teil fest?

Das erste Kapitel begann mit Geschäftssprache und Regeln. Jetzt lautet die Frage: Wo leben diese Entscheidungen im Code? Wir bleiben bei derselben E-Commerce-Domain: Geld, Bestellpositionen, Bestellung und Zahlungsablauf. Das erste Kapitel begann mit Geschäftssprache und Regeln. Jetzt lautet die Frage: Wo leben diese Entscheidungen im Code? Wir bleiben bei derselben E-Commerce-Domain: Geld, Bestellpositionen, Bestellung und Zahlungsablauf.

Gelernte Engineering-Prinzipien

  • Value Objects verbinden primitive Daten mit Geschäftssemantik und Validierung.
  • Aggregates sollen kleine, explizite Konsistenzgrenzen sein.
  • Application Services orchestrieren; Domain-Verhalten entscheidet.

Weiterlesen

Weiterlesen

Verwandte Beitrage

Verwandte Beitrage

Verwandte Beitrage

Paylaş