Aggregat

Kern der Implementierung von Domänenmodellen sind die sogenannten Aggregate. Ein Aggregat stellt eine (meist transaktionale) Konsistenzgrenze innerhalb des Domänenmodells eines Landes dar.

Dieses Muster beschreibt das Konzept von Aggregaten und soll aufzeigen, wie diese im Programmcode angewendet werden.

Design

Für das Design wird die Verwendung eines Domänenmodells zugrunde gelegt. Dabei ist ein Domänenmodell ein Objektmodell der Anwendungsdomäne, das Verhalten und Daten beinhaltet. Dieses ist unabhängig vom Kontext, d.h. beispielsweise, dass es sich nicht selber persistiert oder selber darstellt (vgl. Fowler: DomainModel). Zur Implementierung werden dabei POCOs (plain old csharp objects - simple Klassen) verwendet.

Jede Domänenmodell-Klasse repräsentiert dabei ein Begriff der Anwendungsdomäne und nutzt dabei die Ubiqitous Language. Aber auch Attribut- und Methodennamen werden der Ubiquitous Language entnommen! In folgender Abbildung sind dies beispielsweise Buch, Inhaltsangabe und Seite als Ausschnitt vom gesamten Domänenmodell die ein Aggregate bilden.

Domänenmodelle werden nicht über Ländergrenzen hinweg wiederverwendet.

Ein Aggregat - in obiger Abbildung durch gestrichelte Linien zu erkennen - stellt die Menge der Domänenobjekte dar, die gleichzeitig aus der Persistenzschicht (z.B. mit dem OR-Mapper) geladen und gespeichert werden können.

Objekte einer Klasse werden nur genau einem Aggregat zugeordnet (keine Überschneidungen). Externe Beziehungen (d.h. Beziehungen zu anderen Aggregaten) des Aggregates erfolgen nur über das sogenannte Aggregatwurzelobjekt.

Als Aggregatwurzel wird die Klasse bezeichnet, die als Einstiegspunkt in das Domänenmodell dient. Nur diese Objekte werden durch das ihm zugeordnete Repository ausgehändigt. Von dieser Klasse aus assoziierte Objekte werden nur über die Aggregatwurzel ermittelt. Aggregate können in UML nicht notiert werden, daher wird ein Stereotyp wie in obiger Abbildung zu sehen verwendet.

Das Durchführen des Speicherns eines Aggregats wird durch ein zugehöriges Repository durchgeführt.

Regeln
  • Die Aggregatwurzel hat globale Identität (Kriterium zur Identifizierung)
  • Entitäten innerhalb der Grenze haben lokale Identität, eindeutig innerhalb des Aggregats (Kriterium zur Identifizierung)
  • Die Aggregatwurzel ist verantwortlich für die Prüfung von Invarianten (Beispiel: Gesamtanzahl Seiten = Inhaltsangabeseiten + Seiten)
  • Assoziierte Objekte werden nur über die Aggregatwurzel ausgehändigt
  • Nichts außerhalb der Aggregatgrenze kann eine Referenz zu inneren Objekten halten - außer der Aggregatwurzel
    • Die Aggregatwurzel kann diese inneren Objekte aber aushändigen
  • Nur Aggregatwurzel direkt über Databankabfragen erhältlich - alles andere: Traversierung
  • Löschen innerhalb der Grenze darf nur auf einmal erfolgen (kein Löschen aus der Mitte )
  • Siehe hierzu: Muster Aggregat-Verknüpfung
  • Aggregatübergreifende Verknüpfungen erfolgen nur über EnitityIds

Ein Aggregate besteht aus Entitys und ValueObjects. ValueObject werden zu unrecht eher weniger verwendet, sorgen für bessere Lesbarkeit und Typsicherheit. Daher: verwenden! Die optimierte Variante

Implementierung

Es ist das aus der Analyse entstandene Fachklassendiagramm zu verwenden und u.a. anhand der oben stehenden Regeln das Domänenmodell in Aggregate einzuteilen. Da hier modelliert wird, kann es keinen stringenten Weg geben! Dennoch hier ein paar Indikatoren für das Einteilen

  • In unterschiedlichen Use Cases werden unterschiedliche "Bereiche" des Domänenmodells persistiert. Da Aggregate stets vollständig gelesen und geschrieben werden, kann man hierüber gute Aggregat-Schnitte erkennen. Daher ist es hilfreich, viele Use Cases zu berücksichtigen.
  • Ein spezieller Use Case ist das Löschen von "Daten". Die Objekte eines Domänenmodells, die zusammen gelöscht werden, bilden potenziell ein Aggregat.

Zur Implementierung werden Entitys und Value Objects verwendet. Eine mögliche Implementierung kann dann so aussehen:

public partial class Buch : AggregateRoot<BuchId>
{
	private Autor autor;
	private string titel;
	private Inhaltsangabe inhaltsangabe;
	// ...

	public Buch(BuchId id, Isbn isbn, string titel, Autor autor)
		: base(id)
	{
		if (string.IsNullOrEmpty(titel)) { throw new ArgumentNullException(nameof(titel)); }
		this.titel = titel;
		this.autor = autor;
		this.isbn = isbn ?? throw new ArgumentNullException(nameof(isbn));
		// ...
	}

	public virtual void StelleZurVerfuegung()
	{
		// ...
	}
	// ... 
}

public partial class Inhaltsangabe : Entity<InhaltsangabeId>
{
	private ISet<Seite> seiten;

	public Inhaltsangabe(InhaltsangabeId id, IEnumerable<Seite> seiten)
		: base(id)
	{
		if (seiten == null) { throw new ArgumentNullException(nameof(seiten)); }
		this.seiten = new HashSet<Seite>();
		Add(seiten);
	}
	// ... 
}


public partial class Isbn : ValueObject
{
	public static readonly string ValidNummerRegexPattern = @"(ISBN[-]*(1[03])*[ ]*(: ){0,1})*(([0-9Xx][- ]*){13}|([0-9Xx][- ]*){10})";
	private static readonly Regex ValidNummerRegex = new(ValidNummerRegexPattern, RegexOptions.Compiled);

	private string nummer;

	public Isbn(string nummer)
	{
		if (string.IsNullOrEmpty(nummer)) { throw new ArgumentNullException(nameof(nummer)); }
		if (!ValidNummerRegex.IsMatch(nummer)) { throw new ArgumentException($"{nummer} ist keine gültige ISBN."); }
		this.nummer = nummer;
	}

	public override string ToString()
	{
		return FormattableString.Invariant($"{nameof(Isbn)} '{nummer}'");
	}

	protected override IEnumerable<object> GetMembers()
	{
		yield return nummer;
	}
}

Die Implementierung ist nicht vollständig!

Ein Aggregat sollte in einem Verzeichnis definiert werden: das Aggregat Buch in dem Verzeichnis Buecher. Als Namensraum verwenden wir dabei ebenfalls den Plural um Probleme bei der Implementierung zu vermeiden.

Stärken und Schwächen

Die in diesem Abschnitt beschriebenen Stärken und Schwächen sollen eine Entscheidungshilfe an die Hand geben, wann dieses Muster eingesetzt werden kann.

Stärken
  • Hilft, die oben beschriebenen Probleme (Laden des gesamten Domänenmodells; beliebiges Verweben der Klassen) zu entgehen
  • Gutes Strukturierungsmittel
  • Unterstützt das Verständnis der Domäne optimal
  • Sehr gute Lesbarkeit
Schwächen
  • Die Entscheidung, was ein Aggregat ist, kann nicht anhand scharfer Kriterien definiert werden
  • Konsequentes Arbeiten notwendig

Benutzte Muster

Erweiterungen

Cookie Consent mit Real Cookie Banner