In diesem Artikel wird erläutert, wie Sie DDD-Prinzipien (Domain-Driven Design) auf die Klassen anwenden, die der Entity Framework Core (EF Core) der Datenbank zuordnet, und warum dies nützlich sein kann.
TLDR
Der DDD-Ansatz bietet viele Vorteile, aber die Hauptsache ist, dass DDD den Code für Erstellungs- / Änderungsvorgänge innerhalb der Entitätsklasse überträgt. Dies verringert die Wahrscheinlichkeit, dass Entwickler die Regeln zum Erstellen, Initialisieren und Verwenden von Klasseninstanzen missverstehen / interpretieren.
- Eric Evans 'Buch und seine Reden enthalten nicht viele Informationen zu diesem Thema:
- Stellen Sie dem Kunden ein einfaches Modell zum Abrufen persistenter Objekte (Klassen) und zum Verwalten seines Lebenszyklus zur Verfügung.
- Ihre Entitätsklassen sollten explizit angeben, ob, wie und nach welchen Regeln sie geändert werden können.
- In DDD gibt es das Konzept eines Aggregats. Aggregat ist ein Baum verwandter Entitäten. Gemäß den DDD-Regeln sollte die Arbeit mit Aggregaten über die „Aggregationswurzel“ (die Wurzelessenz des Baums) erfolgen.
Eric erwähnt Repositories in seinen Reden. Ich empfehle nicht, ein Repository mit EF Core zu implementieren, da EF das Repository und die Arbeitseinheitsmuster bereits per se implementiert. Ich werde Ihnen in einem separaten Artikel mehr darüber erzählen: "
Lohnt es sich, ein Repository mit EF Core zu verwenden ?"
Entitäten im DDD-Stil
Ich werde zunächst den Entitätscode im DDD-Stil anzeigen und ihn dann mit der Art vergleichen, wie Entitäten normalerweise mit EF Core erstellt werden. Buchhandlung (eine sehr vereinfachte Version von Amazon. “Die Datenbankstruktur ist in der Abbildung unten dargestellt.

Die ersten vier Tabellen repräsentieren alles über Bücher: die Bücher selbst, ihre Autoren, Rezensionen. Die beiden folgenden Tabellen werden im Geschäftslogikcode verwendet. Dieses Thema wird in einem separaten Artikel ausführlich beschrieben.
Der gesamte Code für diesen Artikel wurde in das GenericBizRunner- Repository auf GitHub hochgeladen. Neben dem GenericBizRunner-Bibliothekscode gibt es ein weiteres Beispiel für eine ASP.NET Core-Anwendung, die GenericBizRunner für die Arbeit mit Geschäftslogik verwendet. Weitere Informationen hierzu finden Sie im Artikel " Bibliothek für die Arbeit mit Geschäftslogik und Entity Framework Core ".
Und hier ist der Entitätscode, der der Datenbankstruktur entspricht.
public class Book { public const int PromotionalTextLength = 200; public int BookId { get; private set; }
Worauf zu achten ist:
- Zeile 5: Legen Sie den Zugriff auf alle als privat deklarierten Entitätseigenschaften fest. Dies bedeutet, dass die Daten entweder mit dem Konstruktor oder mit den später in diesem Artikel beschriebenen öffentlichen Methoden geändert werden können.
- Zeilen 9 und 10. Verwandte Sammlungen (dieselben Aggregate aus DDD) bieten öffentlichen Zugriff auf IEnumerable <T>, nicht auf ICollection <T>. Dies bedeutet, dass Sie keine Elemente direkt zur Sammlung hinzufügen oder daraus entfernen können. Sie müssen spezielle Methoden aus der Book-Klasse verwenden.
- Zeile 13. EF Core erfordert einen parameterlosen Konstruktor, kann jedoch privaten Zugriff haben. Dies bedeutet, dass anderer Anwendungscode die Initialisierung nicht umgehen und Instanzen von Klassen mit einem parameterlosen Konstruktor erstellen kann (Kommentar eines Übersetzers. Es sei denn, Sie erstellen Entitäten natürlich nur mit Reflektion).
- Zeilen 16-20: Sie können eine Instanz der Book-Klasse nur mit dem öffentlichen Konstruktor erstellen. Dieser Konstruktor enthält alle erforderlichen Informationen zum Initialisieren des Objekts. Somit ist garantiert, dass sich das Objekt in einem gültigen Zustand befindet.
- Zeilen 23-25: Diese Zeilen enthalten Methoden zum Ändern des Status eines Buches.
- Zeilen 28-29: Mit diesen Methoden können Sie verwandte Entitäten (Aggregate) ändern.
Bei den Methoden in den Zeilen 23-39 werde ich weiterhin die "Methoden, die den Zugriff ermöglichen" aufrufen. Diese Methoden sind die einzige Möglichkeit, die Eigenschaften und Beziehungen innerhalb einer Entität zu ändern. Unter dem Strich ist die Buchklasse "geschlossen". Es wird durch einen speziellen Konstruktor erstellt und kann nur teilweise durch spezielle Methoden mit geeigneten Namen geändert werden. Dieser Ansatz bildet einen scharfen Kontrast zum Standardansatz zum Erstellen / Ändern von Entitäten in EF Core, bei dem alle Entitäten einen leeren Standardkonstruktor enthalten und alle Eigenschaften als öffentlich deklariert sind. Die nächste Frage ist, warum der erste Ansatz besser ist.
Vergleich der Entitätserstellung
Vergleichen wir den Code zum Empfangen von Daten zu mehreren Büchern von json und zum Erstellen von Instanzen der Buchklassen auf deren Grundlage.
a. Standardansatz
var price = (decimal) (bookInfoJson.saleInfoListPriceAmount ?? DefaultBookPrice) var book = new Book { Title = bookInfoJson.title, Description = bookInfoJson.description, PublishedOn = DecodePubishDate(bookInfoJson.publishedDate), Publisher = bookInfoJson.publisher, OrgPrice = price, ActualPrice = price, ImageUrl = bookInfoJson.imageLinksThumbnail }; byte i = 0; book.AuthorsLink = new List<BookAuthor>(); foreach (var author in bookInfoJson.authors) { book.AuthorsLink.Add(new BookAuthor { Book = book, Author = authorDict[author], Order = i++ }); }
b. DDD-Stil
var authors = bookInfoJson.authors.Select(x => authorDict[x]).ToList(); var book = new Book(bookInfoJson.title, bookInfoJson.description, DecodePubishDate(bookInfoJson.publishedDate), bookInfoJson.publisher, ((decimal?)bookInfoJson.saleInfoListPriceAmount) ?? DefaultBookPrice, bookInfoJson.imageLinksThumbnail, authors);
Buchklassen-Konstruktorcode
public Book(string title, string description, DateTime publishedOn, string publisher, decimal price, string imageUrl, ICollection<Author> authors) { if (string.IsNullOrWhiteSpace(title)) throw new ArgumentNullException(nameof(title)); Title = title; Description = description; PublishedOn = publishedOn; Publisher = publisher; ActualPrice = price; OrgPrice = price; ImageUrl = imageUrl; _reviews = new HashSet<Review>(); if (authors == null || !authors.Any()) throw new ArgumentException( "You must have at least one Author for a book", nameof(authors)); byte order = 0; _authorsLink = new HashSet<BookAuthor>( authors.Select(a => new BookAuthor(this, a, order++))); }
Worauf zu achten ist:
- Zeilen 1-2: Der Konstruktor zwingt Sie, alle Daten zu übergeben, die für eine ordnungsgemäße Initialisierung erforderlich sind.
- Zeilen 5, 6 und 17-9: Der Code enthält mehrere Überprüfungen für Geschäftsregeln. In diesem speziellen Fall wird ein Verstoß gegen die Regeln als Fehler im Code angesehen. Im Falle eines Verstoßes wird daher eine Ausnahme ausgelöst. Wenn der Benutzer diese Fehler beheben könnte, würde ich möglicherweise eine statische Factory verwenden, die Status <T> zurückgibt (Kommentarübersetzer. Ich würde Option <T> oder Ergebnis <T> als allgemeineren Namen verwenden). Status ist ein Typ, der eine Liste von Fehlern zurückgibt.
- Zeilen 21-23: Die BookAuthor-Bindung wird im Konstruktor erstellt. Der BookAuthor-Konstruktor kann mit der internen Zugriffsebene deklariert werden. Auf diese Weise können wir die Schaffung von Beziehungen außerhalb des DAL verhindern.
Wie Sie vielleicht bemerkt haben, ist die Menge an Code zum Erstellen einer Entität in beiden Fällen ungefähr gleich. Warum ist der DDD-Stil besser? Der DDD-Stil ist darin besser:
- Steuert den Zugriff. Ein versehentlicher Eigentumswechsel ist ausgeschlossen. Jede Änderung erfolgt über den Konstruktor oder die öffentliche Methode mit dem entsprechenden Namen. Offensichtlich was passiert.
- Entspricht DRY (wiederholen Sie sich nicht). Möglicherweise müssen Sie Buchinstanzen an mehreren Stellen erstellen. Der Zuweisungscode befindet sich im Konstruktor und muss nicht an mehreren Stellen wiederholt werden.
- Versteckt Komplexität. Die Book-Klasse hat zwei Eigenschaften: ActualPrice und OrgPrice. Beide Werte sollten beim Erstellen eines neuen Buches gleich sein. In einem Standardansatz sollte sich jeder Entwickler dessen bewusst sein. Beim DDD-Ansatz reicht es für den Entwickler der Buchklasse aus, darüber Bescheid zu wissen. Der Rest lernt diese Regel kennen, da sie explizit im Konstruktor geschrieben ist.
- Blendet die Aggregaterstellung aus. In einem Standardansatz muss der Entwickler manuell eine Instanz von BookAuthor erstellen. Im DDD-Stil ist diese Komplexität für den aufrufenden Code gekapselt.
- Ermöglicht Eigenschaften privaten Schreibzugriff
- Einer der Gründe für die Verwendung von DDD besteht darin, die Entität zu sperren, d.h. Geben Sie nicht die Möglichkeit, Eigenschaften direkt zu ändern. Vergleichen wir den Änderungsvorgang mit und ohne DDD.
Vergleich der Eigenschaftsänderung
Eric Evans, einer der Hauptvorteile von Entitäten im DDD-Stil, nennt Folgendes: „Sie kommunizieren Entwurfsentscheidungen über den Objektzugriff“.
Hinweis Übersetzer. Der ursprüngliche Satz ist schwer ins Russische zu übersetzen. In diesem Fall sind Entwurfsentscheidungen Entscheidungen darüber, wie die Software funktionieren soll. Dies bedeutet, dass Entscheidungen diskutiert und bestätigt wurden. Code mit Konstruktoren, die Entitäten und Methoden mit korrekten Namen, die die Bedeutung von Operationen widerspiegeln, korrekt initialisieren, teilt dem Entwickler ausdrücklich mit, dass die Zuweisungen bestimmter Werte absichtlich und nicht versehentlich vorgenommen wurden und keine Laune eines anderen Entwicklers oder Implementierungsdetails sind.
Ich verstehe diesen Satz wie folgt.
- Machen Sie deutlich, wie Daten innerhalb einer Entität geändert werden und welche Daten sich gemeinsam ändern sollen.
- Machen Sie deutlich, wann Sie bestimmte Daten in der Entität nicht ändern sollten.
Vergleichen wir die beiden Ansätze. Das erste Beispiel ist einfach und das zweite komplizierter.
1. Änderung des Veröffentlichungsdatums
Angenommen, wir möchten zuerst mit einem Entwurf eines Buches arbeiten und ihn erst dann veröffentlichen. Zum Zeitpunkt des Schreibens des Entwurfs wird ein geschätztes Veröffentlichungsdatum festgelegt, das sich sehr wahrscheinlich während des Bearbeitungsprozesses ändert. Zum Speichern des Veröffentlichungsdatums verwenden wir die PublishedOn-Eigenschaft.
a. Entität mit öffentlichen Eigenschaften
var book = context.Find<Book>(dto.BookId); book.PublishedOn = dto.PublishedOn; context.SaveChanges();
b. Entität im DDD-Stil
Im DDD-Stil wird der Setter der Eigenschaft als privat deklariert, daher verwenden wir eine spezielle Zugriffsmethode.
var book = context.Find<Book>(dto.BookId); book.UpdatePublishedOn( dto.PublishedOn); context.SaveChanges();
Diese beiden Fälle sind fast gleich. Die DDD-Version ist noch etwas länger. Aber es gibt immer noch einen Unterschied. Im DDD-Stil wissen Sie sicher, dass das Veröffentlichungsdatum geändert werden kann, da es eine Methode mit einem offensichtlichen Namen gibt. Sie wissen auch, dass Sie den Herausgeber nicht ändern können, da die Publisher-Eigenschaft keine geeignete Methode zum Ändern hat. Diese Informationen sind für jeden Programmierer nützlich, der mit einer Buchklasse arbeitet.
2. Verwalten Sie den Rabatt für das Buch
Eine weitere Anforderung ist, dass wir Rabatte verwalten können müssen. Der Rabatt besteht aus einem neuen Preis und einem Kommentar, zum Beispiel "50% vor Ende dieser Woche!"
Die Implementierung dieser Regel ist einfach, aber nicht zu offensichtlich.
- Die OrgPrice-Eigenschaft ist der Preis ohne Rabatt.
- ActualPrice - Der aktuelle Preis, zu dem das Buch verkauft wird. Wenn der Rabatt gültig ist, unterscheidet sich der aktuelle Preis von OrgPrice durch die Größe des Rabatts. Wenn nicht, ist der Wert der Eigenschaften gleich.
- Die PromotionText-Eigenschaft muss den Rabatttext enthalten, wenn der Rabatt angewendet wird, oder null, wenn der Rabatt derzeit nicht angewendet wird.
Die Regeln sind für die Person, die sie implementiert hat, ziemlich offensichtlich. Für einen anderen Entwickler, der beispielsweise eine Benutzeroberfläche entwickelt, um einen Rabatt hinzuzufügen. Durch Hinzufügen der Methoden AddPromotion und RemovePromotion zur Entitätsklasse werden Implementierungsdetails ausgeblendet. Jetzt hat ein anderer Entwickler öffentliche Methoden mit den entsprechenden Namen. Die Semantik der Verwendung von Methoden liegt auf der Hand.
Sehen Sie sich die Implementierung der Methoden AddPromotion und RemovePromotion an.
public IGenericErrorHandler AddPromotion(decimal newPrice, string promotionalText) { var status = new GenericErrorHandler(); if (string.IsNullOrWhiteSpace(promotionalText)) { status.AddError( "You must provide some text to go with the promotion.", nameof(PromotionalText)); return status; } ActualPrice = newPrice; PromotionalText = promotionalText; return status; }
Worauf zu achten ist:
- Zeilen 4-10: Das Hinzufügen eines PromotionalText-Kommentars ist erforderlich. Die Methode prüft, ob der Text nicht leer ist. Weil Der Benutzer kann diesen Fehler korrigieren. Die Methode gibt eine Liste von Fehlern zur Korrektur zurück.
- Zeilen 12, 13: Die Methode legt die Werte der Eigenschaften gemäß der vom Entwickler ausgewählten Implementierung fest. Der Benutzer der AddPromotion-Methode muss sie nicht kennen. Um einen Rabatt hinzuzufügen, schreiben Sie einfach:
var book = context.Find<Book>(dto.BookId); var status = book.AddPromotion(newPrice, promotionText); if (!status.HasErrors) context.SaveChanges(); return status;
Die RemovePromotion-Methode ist viel einfacher: Sie beinhaltet keine Fehlerbehandlung. Daher ist der Rückgabewert einfach ungültig.
public void RemovePromotion() { ActualPrice = OrgPrice; PromotionalText = null; }
Diese beiden Beispiele unterscheiden sich sehr voneinander. Im ersten Beispiel ist das Ändern der PublishOn-Eigenschaft so einfach, dass die Standardimplementierung in Ordnung ist. Im zweiten Beispiel sind Implementierungsdetails für jemanden, der nicht mit der Book-Klasse gearbeitet hat, nicht offensichtlich. Im zweiten Fall verbirgt der DDD-Stil mit speziellen Zugriffsmethoden Implementierungsdetails und erleichtert das Leben anderer Entwickler. Im zweiten Beispiel enthält der Code auch Geschäftslogik. Obwohl die Menge an Logik gering ist, können wir sie direkt in Zugriffsmethoden speichern und eine Liste von Fehlern zurückgeben, wenn die Methode nicht korrekt verwendet wird.
3. Arbeiten Sie mit den Bewertungen der Aggregat - Eigenschaftssammlung
DDD bietet an, mit dem Gerät nur über die Wurzel zu arbeiten. In unserem Fall verursacht die Reviews-Eigenschaft Probleme. Selbst wenn der Setter als privat deklariert ist, kann der Entwickler Objekte mithilfe der Methoden add und remove hinzufügen oder entfernen oder sogar die Methode clear aufrufen, um die gesamte Sammlung zu löschen. Hier hilft uns die neue EF Core-Funktion,
die Felder unterstützt.
Das Hintergrundfeld ermöglicht es dem Entwickler, die reale Sammlung zu kapseln und öffentlichen Zugriff auf den IEnumerable <T> -Schnittstellenlink bereitzustellen. Die IEnumerable <T> -Schnittstelle bietet keine Methoden zum Hinzufügen, Entfernen oder Löschen. Im folgenden Code finden Sie ein Beispiel für die Verwendung von Sicherungsfeldern.
public class Book { private HashSet<Review> _reviews; public IEnumerable<Review> Reviews => _reviews?.ToList();
Damit dies funktioniert, müssen Sie EF Core mitteilen, dass Sie beim Lesen aus der Datenbank in ein privates Feld schreiben müssen, nicht in eine öffentliche Eigenschaft. Der Konfigurationscode wird unten angezeigt.
protected override void OnModelCreating (ModelBuilder modelBuilder) { modelBuilder.Entity<Book>() .FindNavigation(nameof(Book.Reviews)) .SetPropertyAccessMode(PropertyAccessMode.Field);
Um mit Rezensionen zu arbeiten, habe ich der Buchklasse zwei Methoden hinzugefügt: AddReview und RemoveReview. Die AddReview-Methode ist interessanter. Hier ist sein Code:
public void AddReview(int numStars, string comment, string voterName, DbContext context = null) { if (_reviews != null) { _reviews.Add(new Review(numStars, comment, voterName)); } else if (context == null) { throw new ArgumentNullException(nameof(context), "You must provide a context if the Reviews collection isn't valid."); } else if (context.Entry(this).IsKeySet) { context.Add(new Review(numStars, comment, voterName, BookId)); } else { throw new InvalidOperationException("Could not add a new review."); } }
Worauf zu achten ist:
- Zeilen 4-7: Ich initialisiere das Feld _reviews absichtlich nicht in einem privaten parameterlosen Konstruktor, den EF Core beim Laden von Entitäten aus der Datenbank verwendet. Dadurch kann mein Code mithilfe der .Include-Methode (p => p.Reviews) feststellen, ob die Sammlung geladen wurde. Im öffentlichen Konstruktor initialisiere ich das Feld, sodass bei der Arbeit mit der erstellten Entität keine NRE auftritt.
- Zeilen 8-12: Wenn die Reviews-Auflistung nicht geladen wurde, sollte der Code zum Initialisieren DbContext verwenden.
- Zeilen 13-16: Wenn das Buch erfolgreich erstellt wurde und eine ID enthält, verwende ich eine andere Technik zum Hinzufügen einer Überprüfung: Ich installiere einfach einen Fremdschlüssel in einer Instanz der Überprüfungsklasse und schreibe ihn in die Datenbank. Dies wird in Abschnitt 3.4.5 meines Buches ausführlicher beschrieben.
- Zeile 19: Wenn wir hier sind, gibt es ein Problem mit der Logik des Codes. Also werfe ich eine Ausnahme.
Ich habe alle meine Zugriffsmethoden für umgekehrte Fälle entworfen, in denen nur die Stammentität geladen ist. Die Aktualisierung des Geräts liegt im Ermessen der Methoden. Möglicherweise müssen Sie zusätzliche Entitäten laden.
Fazit
Um Entitäten im DDD-Stil mit EF Core zu erstellen, müssen Sie die folgenden Regeln einhalten:
- Erstellen Sie öffentliche Konstruktoren, um korrekt initialisierte Klasseninstanzen zu erstellen. Wenn während des Erstellungsprozesses Fehler auftreten können, die der Benutzer korrigieren kann, erstellen Sie das Objekt nicht mit dem öffentlichen Konstruktor, sondern mit der Factory-Methode, die Status <T> zurückgibt, wobei T der Typ der zu erstellenden Entität ist
- Alle Eigenschaften sind Eigenschaftssetzer. Das heißt, Alle Eigenschaften sind außerhalb der Klasse schreibgeschützt.
- Deklarieren Sie für Sammlungsnavigationseigenschaften Sicherungsfelder und den öffentlichen Eigenschaftstyp IEnumerable <T>. Dies verhindert, dass andere Entwickler Sammlungen unkontrolliert ändern.
- Erstellen Sie anstelle von öffentlichen Setzern öffentliche Methoden für alle zulässigen Objektänderungsvorgänge. Diese Methoden sollten void zurückgeben, wenn der Vorgang nicht mit einem Fehler fehlschlagen kann, den der Benutzer beheben kann, oder Status <T>, wenn dies möglich ist.
- Der Umfang der Haftung des Unternehmens ist von Bedeutung. Ich denke, dass es am besten ist, Entitäten darauf zu beschränken, die Klasse selbst und andere Klassen innerhalb des Aggregats zu ändern, aber nicht außerhalb. Validierungsregeln sollten sich darauf beschränken, die Richtigkeit der Erstellung und Änderung des Status von Entitäten zu überprüfen. Das heißt, Ich überprüfe keine Geschäftsregeln wie Aktiensalden. Hierfür gibt es einen speziellen Geschäftslogikcode.
- Zustandsänderungsmethoden sollten davon ausgehen, dass nur der Aggregationsstamm geladen ist. Wenn eine Methode andere Daten laden muss, muss sie sich selbst darum kümmern.
- Zustandsänderungsmethoden sollten davon ausgehen, dass nur der Aggregationsstamm geladen ist. Wenn eine Methode andere Daten laden muss, muss sie sich selbst darum kümmern. Dieser Ansatz vereinfacht die Verwendung von Entitäten durch andere Entwickler.
Vor- und Nachteile von DDD-Entitäten bei der Arbeit mit EF Core
Ich mag die kritische Herangehensweise an jedes Muster oder jede Architektur. Folgendes denke ich über die Verwendung von DDD-Entitäten.
Vorteile
- Die Verwendung spezialisierter Methoden zur Änderung des Zustands ist ein sauberer Ansatz. Dies ist definitiv eine gute Lösung, einfach weil die richtig benannten Methoden Code-Absichten viel besser aufdecken und deutlich machen, was geändert werden kann und was nicht. Darüber hinaus können Methoden eine Liste von Fehlern zurückgeben, wenn der Benutzer sie beheben kann.
- Das Ändern von Aggregaten nur über die Wurzel funktioniert ebenfalls gut
- Details der Eins-zu-Viele-Beziehung zwischen den Buch- und Überprüfungsklassen sind dem Benutzer jetzt verborgen. Die Einkapselung ist ein Grundprinzip von OOP.
- Mithilfe spezialisierter Konstruktoren können Sie sicherstellen, dass Entitäten erstellt werden und garantiert korrekt initialisiert werden.
- Durch das Verschieben des Initialisierungscodes in den Konstruktor wird die Wahrscheinlichkeit erheblich verringert, dass der Entwickler die Initialisierung der Klasse nicht richtig interpretiert.
Nachteile
- Mein Ansatz enthält Abhängigkeiten von der Implementierung von EF Core.
- Einige Leute nennen es sogar ein Anti-Muster. Das Problem ist, dass jetzt die Entitäten des Subjektmodells vom Datenbankzugriffscode abhängen. In Bezug auf DDD ist dies schlecht. Wenn ich das nicht getan hätte, hätte ich mich darauf verlassen müssen, dass der Anrufer weiß, was geladen werden soll. Dieser Ansatz verstößt gegen das Prinzip der Trennung von Bedenken.
- DDD zwingt Sie, mehr Code zu schreiben.
Lohnt es sich wirklich in einfachen Fällen, wie der Aktualisierung des Veröffentlichungsdatums eines Buches?
Wie Sie sehen können, gefällt mir der DDD-Ansatz. Es hat jedoch eine Weile gedauert, bis ich es richtig strukturiert habe, aber im Moment ist der Ansatz bereits festgelegt und ich wende ihn in den Projekten an, an denen ich arbeite. Ich habe es bereits geschafft, diesen Stil in kleinen Projekten auszuprobieren und bin zufrieden, aber alle Vor- und Nachteile müssen noch herausgefunden werden, wenn ich ihn in großen Projekten verwende.
Meine Entscheidung, die Verwendung von EFCore-spezifischem Code in den Argumenten der Entitätsmodell-Entitätsmethoden zuzulassen, war nicht einfach. Ich habe versucht, dies zu verhindern, bin aber am Ende zu dem Schluss gekommen, dass der aufrufende Code viele Navigationseigenschaften laden musste. Und wenn dies nicht erfolgt, wird die Änderung einfach nicht fehlerfrei angewendet (insbesondere in einer Eins-zu-Eins-Beziehung). Dies war für mich nicht akzeptabel, daher erlaubte ich die Verwendung von EF Core in einigen Methoden (aber nicht in Konstruktoren).
Eine andere schlechte Seite ist, dass DDD Sie zwingt, wesentlich mehr Code für CRUD-Operationen zu schreiben. Ich bin mir immer noch nicht sicher, ob ich weiterhin einen Kaktus essen und separate Methoden für alle Eigenschaften schreiben soll, oder in einigen Fällen lohnt es sich, von einem solch radikalen Puritanismus abzuweichen. Ich weiß, dass es nur einen Wagen und einen kleinen Lastwagen mit langweiligem CRUD gibt, der einfacher direkt zu schreiben ist. Nur die Arbeit an realen Projekten wird zeigen, was besser ist.
Andere DDD-Aspekte, die in diesem Artikel nicht behandelt werden
Der Artikel hat sich als zu lang herausgestellt, deshalb werde ich hier enden. Dies bedeutet jedoch, dass noch viel nicht bekannt gegebenes Material vorhanden ist. Ich habe bereits über etwas geschrieben, über etwas, das ich in naher Zukunft schreiben werde. Folgendes bleibt über Bord:
- Geschäftslogik und DDD. Ich verwende DDD-Konzepte seit mehreren Jahren im Geschäftslogikcode und erwarte mithilfe der neuen Funktionen von EF Core, dass ich einen Teil der Logik auf Entitätscode übertragen kann. Lesen Sie den Artikel „Nochmals zur Architektur der Business Logic Layer mit Entity Framework (Core und v6)“.
- DDD und das Repository-Muster. Eric Evans empfiehlt die Verwendung eines Repositorys, um den Datenzugriff zu abstrahieren. , «» EF Core – . Warum? - .
- DBContext' / (bounded contexts). DbContext'. , BookContext Book OrderContext, . , « » , . , .
Der gesamte Code für diesen Artikel ist im GenericBizRunner-Repository auf GitHub verfügbar . Dieses Repository enthält eine Beispielanwendung für ASP.NET Core mit speziellen Zugriffsmethoden zum Ändern der Book-Klasse. Sie können das Repository klonen und die Anwendung lokal ausführen. Es verwendet In-Memory-SQLite als Datenbank, daher sollte es auf jeder Infrastruktur ausgeführt werden.Fröhliche Entwicklung!