Ich habe derzeit ein Repository für nahezu jede Tabelle in der Datenbank und möchte mich weiter an DDD ausrichten, indem ich sie auf aggregierte Roots reduziere.
Nehmen wir an, ich habe die folgenden Tabellen User
und Phone
. Jeder Benutzer kann ein oder mehrere Telefone haben. Ohne den Begriff der aggregierten Wurzel könnte ich so etwas tun:
//assuming I have the userId in session for example and I want to update a phone number
List<Phone> phones = PhoneRepository.GetPhoneNumberByUserId(userId);
phones[0].Number = “911”;
PhoneRepository.Update(phones[0]);
Das Konzept der aggregierten Wurzeln ist auf dem Papier leichter zu verstehen als in der Praxis. Ich werde niemals Telefonnummern haben, die keinem Benutzer gehören. Wäre es also sinnvoll, das PhoneRepository abzuschaffen und telefonbezogene Methoden in das UserRepository aufzunehmen? Angenommen, die Antwort lautet "Ja", dann schreibe ich das vorherige Codebeispiel neu.
Darf ich im UserRepository eine Methode haben, die Telefonnummern zurückgibt? Oder sollte es immer einen Verweis auf einen Benutzer zurückgeben und dann die Beziehung durch den Benutzer durchlaufen, um zu den Telefonnummern zu gelangen:
List<Phone> phones = UserRepository.GetPhoneNumbers(userId);
// Or
User user = UserRepository.GetUserWithPhoneNumbers(userId); //this method will join to Phone
Unabhängig davon, auf welche Weise ich die Telefone erwerbe, gehe ich davon aus, dass ich eines davon geändert habe. Mein begrenztes Verständnis ist, dass Objekte unter dem Stamm über den Stamm aktualisiert werden sollten, was mich zu Auswahl 1 unten führen würde. Obwohl dies mit Entity Framework sehr gut funktioniert, scheint dies äußerst unbeschreiblich zu sein, da ich beim Lesen des Codes keine Ahnung habe, was ich tatsächlich aktualisiere, obwohl Entity Framework geänderte Objekte im Diagramm im Auge behält.
UserRepository.Update(user);
// Or
UserRepository.UpdatePhone(phone);
Schließlich vorausgesetzt , ich mehrere Lookup - Tabellen haben, die nicht wirklich etwas gebunden sind, wie CountryCodes
, ColorsCodes
, SomethingElseCodes
. Ich könnte sie verwenden, um Dropdowns zu füllen oder aus irgendeinem anderen Grund. Sind das eigenständige Repositorys? Können sie zu einer logischen Gruppierung / einem logischen Repository kombiniert werden, z CodesRepository
. Oder ist das gegen Best Practices?
Antworten:
Sie dürfen jede gewünschte Methode in Ihrem Repository haben :) In beiden von Ihnen genannten Fällen ist es sinnvoll, den Benutzer mit der ausgefüllten Telefonliste zurückzugeben. Normalerweise wird das Benutzerobjekt nicht vollständig mit allen Unterinformationen (z. B. allen Adressen, Telefonnummern) gefüllt, und wir haben möglicherweise unterschiedliche Methoden, um das Benutzerobjekt mit verschiedenen Arten von Informationen zu füllen. Dies wird als verzögertes Laden bezeichnet.
Zum Aktualisieren wird in diesem Fall der Benutzer aktualisiert, nicht die Telefonnummer selbst. Das Speichermodell speichert die Telefone möglicherweise in einer anderen Tabelle. Auf diese Weise denken Sie möglicherweise, dass nur die Telefone aktualisiert werden. Dies ist jedoch aus DDD-Sicht nicht der Fall. Soweit Lesbarkeit betroffen ist, während die Linie
allein vermittelt nicht, was aktualisiert wird, der Code darüber würde deutlich machen, was aktualisiert wird. Außerdem wäre es höchstwahrscheinlich Teil eines Front-End-Methodenaufrufs, der möglicherweise anzeigt, was aktualisiert wird.
Für die Nachschlagetabellen und sogar sonst ist es nützlich, GenericRepository zu haben und dieses zu verwenden. Das benutzerdefinierte Repository kann vom GenericRepository erben.
Suchen Sie nach Generic Repository Entity Framework und Sie würden viele nette Implementierung gut. Verwenden Sie eine davon oder schreiben Sie Ihre eigene.
quelle
Ihr Beispiel für das Aggregate Root-Repository ist vollkommen in Ordnung, dh jede Entität, die ohne Abhängigkeit von einer anderen nicht vernünftigerweise existieren kann, sollte kein eigenes Repository haben (in Ihrem Fall Phone). Ohne diese Überlegung können Sie schnell eine Explosion von Repositorys in einer 1-1-Zuordnung zu DB-Tabellen feststellen.
Sie sollten sich die Verwendung des Arbeitseinheitsmusters für Datenänderungen und nicht für die Repositorys selbst ansehen, da diese Ihrer Meinung nach zu Verwirrung hinsichtlich der Absicht führen, wenn Änderungen an der Datenbank beibehalten werden. In einer EF-Lösung ist die Arbeitseinheit im Wesentlichen ein Schnittstellen-Wrapper um Ihren EF-Kontext.
In Bezug auf Ihr Repository für Suchdaten erstellen wir einfach ein ReferenceDataRepository, das für Daten verantwortlich wird, die nicht speziell zu einer Domänenentität gehören (Länder, Farben usw.).
quelle
Wenn das Telefon ohne Benutzer keinen Sinn ergibt, handelt es sich um eine Entität (wenn Sie sich für die Identität interessieren) oder ein Wertobjekt und sollte immer über den Benutzer geändert und gemeinsam abgerufen / aktualisiert werden.
Stellen Sie sich aggregierte Wurzeln als Kontextdefinierer vor - sie zeichnen lokale Kontexte, befinden sich jedoch selbst im globalen Kontext (Ihrer Anwendung).
Wenn Sie dem domänengesteuerten Design folgen, sollten Repositorys 1: 1 pro Aggregatwurzel sein.
Keine Ausreden.
Ich wette, das sind Probleme, mit denen Sie konfrontiert sind:
Ich bin nicht sicher, wie ich das erste Problem beheben soll, aber ich habe festgestellt, dass das Beheben des zweiten Problems das erste Problem gut genug behebt. Probieren Sie dieses Papier aus , um zu verstehen, was ich mit verhaltensorientiert meine .
Ps Das Reduzieren des Repositorys auf das Aggregieren von Root macht keinen Sinn.
Pps vermeiden
"CodeRepositories"
. Das führt zu datenzentriertem -> prozeduralem Code.Ppps Arbeitseinheitsmuster vermeiden. Aggregierte Roots sollten Transaktionsgrenzen definieren.
quelle
Dies ist eine alte Frage, aber es lohnt sich, eine einfache Lösung zu veröffentlichen.
statische Leere updateNumber (int userId, Zeichenfolge oldNumber, Zeichenfolge newNumber)
quelle
Wenn eine Telefonentität nur zusammen mit einem aggregierten Stammbenutzer sinnvoll ist, ist es meiner Meinung nach auch sinnvoll, dass der Vorgang zum Hinzufügen eines neuen Telefondatensatzes in der Verantwortung des Benutzerdomänenobjekts durch eine bestimmte Methode (DDD-Verhalten) liegt Dies ist aus mehreren Gründen durchaus sinnvoll. Der unmittelbare Grund ist, dass wir überprüfen sollten, ob das Benutzerobjekt vorhanden ist, da die Telefonentität von ihrer Existenz abhängt, und möglicherweise eine Transaktionssperre beibehalten, während weitere Validierungsprüfungen durchgeführt werden, um sicherzustellen, dass kein anderer Prozess das Stammaggregat zuvor gelöscht hat Wir sind mit der Validierung der Operation fertig. In anderen Fällen möchten Sie bei anderen Arten von Root-Aggregaten möglicherweise einen Wert aggregieren oder berechnen und ihn in den Spalteneigenschaften des Root-Aggregats beibehalten, um später eine effizientere Verarbeitung durch andere Vorgänge zu ermöglichen.
Wenn Sie Methoden verwenden möchten, mit denen alle Telefone abgerufen werden, unabhängig davon, welche Benutzer sie besitzen, können Sie dennoch über das Benutzer-Repository nur eine Methode verwenden, die alle Benutzer als IQueryable zurückgibt. Anschließend können Sie sie zuordnen, um alle Benutzer-Telefone abzurufen und eine Verfeinerung durchzuführen frage damit ab. In diesem Fall benötigen Sie also nicht einmal ein PhoneRepository. Außerdem würde ich für IQueryable lieber eine Klasse mit Erweiterungsmethode verwenden, die ich überall verwenden kann, nicht nur aus einer Repository-Klasse, wenn ich Abfragen hinter Methoden abstrahieren möchte.
Nur eine Einschränkung, um Telefonentitäten nur mithilfe des Domänenobjekts und nicht eines Telefonrepositorys löschen zu können. Sie müssen sicherstellen, dass die Benutzer-ID Teil des Telefonprimärschlüssels ist. Mit anderen Worten, der Primärschlüssel eines Telefondatensatzes ist ein zusammengesetzter Schlüssel besteht aus UserId und einer anderen Eigenschaft (ich schlage eine automatisch generierte Identität vor) in der Phone-Entität. Dies ist intuitiv sinnvoll, da der Telefondatensatz dem Benutzerdatensatz "gehört" und das Entfernen aus der Benutzer-Navigationssammlung dem vollständigen Entfernen aus der Datenbank entspricht.
quelle