Laden...

DDD Aggregate + Repository Pattern in EF Core

Erstellt von munzili vor 4 Jahren Letzter Beitrag vor 4 Jahren 1.268 Views
M
munzili Themenstarter:in
1 Beiträge seit 2019
vor 4 Jahren
DDD Aggregate + Repository Pattern in EF Core

Hallo!

Ich versuche gerade ein Projekt in DDD und Clean Architecture umzusetzen bzw. sind das meine ersten Versuche 😉

Ich arbeite auf .Net Core 3.0 und möchte eine API bereitstellen. Dabei bin ich auf eine kleine Hürde gestoßen, wo ich jetzt nicht genau weis, wie man das am besten löst. Folgender Code ist vereinfacht um auf meine allgemeine Hürde hinzuweisen, value objects entfernt bzw vereinfacht.

Ich hab ein Domain Aggregate DistributionPackage:


public class DistributionPackage
{
      private readonly List<DistributionPackageItem> _distributionPackageItems = new List<DistributionPackageItem>();

        private DistributionPackage() { }

        public DistributionPackage(/*...*/)
        {
            Id = Guid.NewGuid();
            // ...
            Date = DateTime.Now;
        }

        public Guid Id { get; private set; }

        public DateTime Date { get; private set; }

        public IReadOnlyCollection<DistributionPackageItem> DistributionPackageItems => _distributionPackageItems.AsReadOnly();

        public DistributionPackageItem AddItem(string name, string description, int amount)
        {
            var newItem = new DistributionPackageItem(name, description, amount);
            _distributionPackageItems.Add(newItem);
            return newItem;
        }
}

public class DistributionPackageItem 
{
        private DistributionPackageItem() { }

        internal DistributionPackageItem(string name, string description, int amount)
        {
            Id = Guid.NewGuid();
            Name = name;
            Description = description;
            Amount = amount;
        }

        public Guid Id { get; private set; }

        public string Name { get; private set; }

        public string Description { get; private set; }

        public int Amount { get; private set; }
}

Von EF Core sieht meine Config so aus:


public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext (DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

        public DbSet<DistributionPackage> DistributionPackage { get; set; }

        // ...

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            // Customer Aggreagte
            modelBuilder.Entity<DistributionPackage>(ConfigureDistributionPackage);
            modelBuilder.Entity<DistributionPackageItem>(ConfigureDistributionPackageItem);
    
            // ....
        }

        private void ConfigureDistributionPackage(EntityTypeBuilder<DistributionPackage> builder)
        {
            builder.HasKey(distributionPackage => distributionPackage.Id);
            builder.HasMany(distributionPackage => distributionPackage.DistributionPackageItems).WithOne().Metadata.PrincipalToDependent.SetPropertyAccessMode(PropertyAccessMode.Field);            
        }

        private void ConfigureDistributionPackageItem(EntityTypeBuilder<DistributionPackageItem> builder)
        {
            builder.HasKey(distributionPackageItem => distributionPackageItem.Id);
        }
}

Wenn ich jetzt versuche ein DistributionPackage zu erstellen in meinen UseCases, items hinzufüge und diese in der DB mittels einer Repository classe speichere, funktioniert alles wunderbar.

repository.Add(distributionPackage());}

repository.Add macht nichts anderes als

            _dbContext.Set<DistributionPackage>().Add(entity);

Das ganze klappt auch ohne Probleme. Wenn ich aber jetzt das Objekt mit weiteren Items fülle und Updaten will via _dbContext.SaveChangesAsync(); bekomme ich einen Fehler, das ein Objekt versucht wird zu speichern, das nicht in der Db existiert (weil ja das Item in AddItem() erstellt wird und nicht registriert im EF)

Meine Frage nun: Wie löss ich das am besten ohne jetzt das Aggregate aufzubohren?

  • In jedem UseCase via Repository Interface die items manuel registrieren lassen? Hört sich für mich sehr unsauber an
  • Irgentwie im Infrastrucuture.Data Layer versuchen AddItem via Domain Events abzufangen und registrieren zu lassen?
  • Im Infrastrucuture.Data Layer versuchen die AddItem zu overloaden und im Repository by Add() abzufangen?
  • Besser vorschlgäe oder elegantere Lösungsansätze?

Besten Danke!

16.806 Beiträge seit 2008
vor 4 Jahren

Das Domain Model stellt keine Datenbank-Entität dar (was für alle Datenbank-Konzepte gilt, nicht nur EF).
Verwendest Du das DDD Modell als Entität ist das - zumindest in der strikten Auslegung - eine Verletzung der Schichtentrennung.
Basics: [Artikel] Drei-Schichten-Architektur

Konzepte in EF (vor allem wenns Richtung Optimierung dessen geht) machen dann eine reine DDD Umsetzung schwierig bzw. komplex - und umgekehrt.
Im Endeffekt die Gegenfrage: wie ernst und streng willst Du Dich an den Konzepten orientieren - und mit welchen Kompromissen kannst Du leben.

Im Infrastrucuture.Data Layer

Da solltest Du evtl nochmal schauen, wie man Namespaces schneidet 😃

Besser vorschlgäe oder elegantere Lösungsansätze?

Dafür gibt es zu wenig Infos von Deiner Seite. Ein Repository Ansatz ist weit verbreitet - es gibt aber auch viele weitere Ansätze (zB Event Sourcing).

A
764 Beiträge seit 2007
vor 4 Jahren

Meine Frage nun: Wie löss ich das am besten ohne jetzt das Aggregate aufzubohren?

  • In jedem UseCase via Repository Interface die items manuel registrieren lassen? Hört sich für mich sehr unsauber an
  • Irgentwie im Infrastrucuture.Data Layer versuchen AddItem via Domain Events abzufangen und registrieren zu lassen?
  • Im Infrastrucuture.Data Layer versuchen die AddItem zu overloaden und im Repository by Add() abzufangen?
  • Besser vorschlgäe oder elegantere Lösungsansätze?

Alles wesentliche hat Abt schon gesagt.

Ich habe festgestellt, dass die Auswahl des richtigen Weges zu stark von der Anforderung abhängig ist, als dass man sich auf einen Festlegen könnte.
Grade Beispiel-Implementierungen und Test-Projekte geben die nötige Komplexität für tiefergehendes Verständnis nicht her.