Work in Progress
Diese Seite ist aktuell im Review! Die Seite wurde noch nicht qualitätsgesichert und kann Fehler enthalten.
Die verlinkten Seiten sind ggf. nur für Schleupen-Mitarbeiter sichtbar.

Aggregat-Verknüpfung

Dieses Muster beschreibt das Zusammenspiel von Aggregaten. Es wird gezeigt, wie Aggregate technisch möglichst lose verknüpft werden können. Hierzu sind die in Muster Aggregat beschriebenen Regeln zu berücksichtigen.

Design

Verschiedene Aggregate werden im Code durch typisierte Ids verbunden. Hierbei ist die Verwendung von typisierten Ids anstelle von Guids wichtig, um eine gute Codenavigation zu erhalten und zum Anderen das Verwechseln von Ids zu vermeiden wichtig. Ferner ist zu beachten, dass natürlich die in Aggregat beschriebnenen Regeln nicht verletzt werden.

Die BuchId gehört formal zum Aggregat Buch!

  • Definiere einen Typ als Entity-Id und verwende diese sowohl in der eigentlichen Klasse als auch im referenzierenden Aggregat. Namenskonvention: <Klassenname>Id. Beispiel: BuchId. Siehe hierzu auch EntityId.
  • Erstelle ggf. eine Tabelle für die Ziel-Id (notwendig bei 1..n-Beziehungen).
  • Für das Verknüpfungsziel wird eine neue ClassMap wie in Objektpersistierung mit NHibernate - Mapping beschrieben erstellt. Dabei wird die Entity-Id gemappt. Das Mapping kann direkt in den bestehenden ClassMaps per Map() oder Component angebunden werden.

Implementierung

Aggregat "Benutzerkonto" (1..n-Beziehung)

Gegeben seien die Aggregate Benutzerkonto und Ausleihe. Diese sollen per Id verknüpft werden.

public class Benutzerkonto : IBenutzerkontoData // Aggregatwurzel, Zugriff auf die Eigenschaften mit Hilfe von [[mab_soa_fagento]], Fagento generiert IBenutzerkontoData
{
    private ISet<AusleiheId> ausleihen;

    public Benutzerkonto(...)
    {
        ausleihen = new HashSet<AusleiheId>(); // Beziehung zu Aggregat "Ausleihe"
    }

    //...
}
Aggregatentkopplung und Mappings

Die Aggregate werden über Entity-Ids verknüpft. Hier wie folgt für Listen, in denen Aggregatgrenzen enthalten sind. Hier kann per Component gemappt werden:

public class BenutzerkontoMap : ClassMap<Benutzerkonto>
{
    public BenutzerkontoMap()
    {
        // ...
        Id(x => ((IBenutzerkontoMapData)x).Id).CustomType<EntityIdUserType<AusleiheId>>();
        // ...
        HasMany(x => ((IBenutzerkontoMapData)x).Ausleihen)
            .Table("[mt_bib].BenutzerkontoAusleihe") // Eigene Zwischentabelle zur Entkopplung
            .Component(c =>
                {
                    c.Map(y => ((IAusleiheIdData)y).Value);
                })
            .Not.KeyUpdate()
            .Not.KeyNullable()
            .Cascade.AllDeleteOrphan();
        }
}

Hierzu muss die Datenbanktabelle BenutzerkontoAusleihe erstellt werden!

Im Repository werden die Mappings per Konfigurator wie gewohnt angeschlossen:

public sealed class BuechersammlungRepositoryConfigurator : NHibernateConfigurator, IBuechersammlungRepositoryConfigurator
{
    protected override IEnumerable<Type> MappingTypes
    {
        get
        {
            yield return typeof(BuechersammlungMap);
            // ...
        }
    }
}
Aggregat "Buch" (1..1-Beziehung)

Gegeben seien die Aggregate Buch und Verlag. Diese sollen per Id gekoppelt werden.

[AggregateRoot]
public class Buch : IBuchData // Aggregatwurzel
{
    public virtual VerlagId verlag; // Verknüpfung zum Aggregat Verlag
    // ...
}
Aggregatentkopplung und Mappings

Die Aggregate werden über Entity-Ids verknüpft. Die Mappings für den OR-Mapper NHibernate werden dabei wie folgt erstellt.

internal sealed class BuchMap : ClassMap<BuchId>
{
    public BuchMap()
    {
        // ...
        Id(x => x.Id).CustomType<EntityIdUserType<BuchId>>();
        // ...

        Map(x => ((IBuchData)x).Verlag)
           .Column("Verlag_Id") // hierdurch wird stark entkoppelt
           .CustomType<EntityIdUserType<VerlagId>>();
    }
}

Hier wird also nur die Id des Ziels der Assoziation gemappt.

Im Repository werden die Mappings per Konfigurator wie gewohnt angeschlossen:

public sealed class BuechersammlungRepositoryConfigurator : NHibernateConfigurator, IBuechersammlungRepositoryConfigurator
{
    protected override IEnumerable<Type> MappingTypes
    {
        get
        {
            yield return typeof(BuechersammlungMap);
            // ...
        }
    }
}
Optimistic Locking

Lesenderweise gibt es keine Einschränkung bzgl. des Optimistic Locking. Ein schreibender Zugriff über Aggregatsgrenzen wird nicht unterstützt. Es muss immer sichergestellt werden, das jeder Typ eindeutig einem Aggregat für den schreibenden Zugriff zugeordnet ist.

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
  • Aggregatgrenze können direkt eingesehen werden
  • Standardweg gemäß Literatur
  • Typisierung der Ids
  • Die Lösung ist einfach
Schwächen
  • Technische Lösung erhält Einzug in das Domänenmodell
  • Flexibilität nicht gegeben, insbesondere hinsichtlich CQRS
    • Erklärung: Es ist so nicht einfach möglich, eine erste Stufe von CQRS zu implementieren: Man erstellt ein einfaches QueryRepository, auf dem Aggregat-übergreifend Abfragen formuliert werden können. Somit kann dem Problem des Schneidens von kleinen Aggregaten für die Persistierung vermieden werden. Aggregate und das Schneiden von Aggregaten ist allein der Persistierung geschuldet.

Benutzte Muster

Cookie Consent mit Real Cookie Banner