38

Ich habe ein Domänenmodell, das das Konzept eines Editors und eines Projekts hat.Repository-Muster: wie zur Lazy Load? oder, Sollte ich dieses Aggregat teilen?

Ein Editor besitzt eine Reihe von Projekten, und ein Projekt hat nicht nur einen Editorbesitzer, sondern auch eine Reihe von Editormitgliedern. Daher hat ein Editor auch eine Anzahl von "verbundenen" Projekten.

Ich nehme einen DDD-Ansatz, um dies zu modellieren und das Repository-Muster für die Persistenz zu verwenden. Allerdings bin ich nicht gut genug, um zu bestimmen, wie ich das machen soll.

Ich arbeite unter der Annahme, dass Editor und Projekt möglicherweise in der gleichen Aggregat sind, mit der Wurzel Editor. Ich kann also einen Editor bekommen und dann seine Projekte aufzählen und von dort aus die Mitglieder des Projects-Members aufzählen.

Wenn ich jedoch nur Editoren aus meinem Repository abrufen darf, heißt das nicht, dass ich alle Projekte aus dem Repository laden muss, wenn ich den Editor erhalte, der sie besitzt? Und wenn ich die Member-Editoren lazy laden möchte, benötigt das Projekt auch einen Verweis auf das Repository?

Alternativ, wenn ich das Aggregat aufteilen und ein Editor-Repository und ein Projekt-Repository habe, wie sollte ich eine Transaktion zwischen den beiden behandeln, etwa wenn ein neues Projekt einem Editor hinzugefügt wird? Zum Beispiel:

Editor e = new Editor("Editor Name"); 
editorRepository.Add(e); 

Project p = e.CreateProject("Project Name"); 
projectRepository.Add(p); // These two lines 
editorRepository.Save(e); // should be atomic 

Am falsch interpretiere ich die Absicht der Repository-Muster?

+1

Vielleicht möchten Sie einen Blick auf meine verwandte Frage werfen: http://StackOverflow.com/q/20820302/253098 – SystematicFrank

Antwort

30

Fehlinterpretation der Absicht des Repository-Musters?

Ich werde „ja“ sagen, aber wissen, dass Sie mich und jede Person, mit denen ich gearbeitet habe, die gleiche Sache aus dem gleichen Grund gefragt ... „Du bist nicht der 4. dimensions denken, Marty ".

Sagen wir es ein wenig vereinfachen und mit Konstrukteuren Stick statt Methoden erstellen zuerst:

Editor e = new Editor("Editor Name"); 
e = editorRepository.Add(e); 

Project p = new Project("Project Name", e); 
p = projectRepository.Add(p); 

Darunter Ihr Projekt-Repository immer einen gültigen Eigentümer Speichern (p.EditorId) in die Projektdaten, wie es erstellt wird, und Wenn Sie jedoch die Projekte eines Redakteurs neu füllen, wird es da sein. Aus diesem Grund empfiehlt es sich, alle erforderlichen Eigenschaften in Konstruktoren zu integrieren. Wenn Sie nicht das gesamte Objekt übergeben möchten, genügt die e.Id.

Und wenn ich die Member-Editoren lazy laden möchte, benötigt das Projekt auch einen Verweis auf das Repository?

Nun, wie Sie die Projekte eines Editors nach Bedarf neu füllen können, haben Sie eine Reihe von Möglichkeiten, je nachdem, was Sie anstreben. Straight Repository sagt, Sie wollen:

IEnumerable<Project> list = projectRepository.GetAllProjects() 
           .Where(x => x.editorId == e.Id); 

Aber wo soll ich es setzen? Nicht in Project oder Editor, du hast Recht, oder sie müssen Zugang zu Repositories bekommen, und das ist nicht gut. Das obige Snippet ist lose gekoppelt, aber nicht wiederverwendbar. Sie haben gerade die Grenzen des Repository-Patterns erreicht.

Als nächstes ist ein Adapter Layer für Ihre Anwendung, mit einer gemeinsamen Quelle von Repositories (StaticServiceWrapper) und entweder eine Art EditorAdapter Objekt (oder Aggregate oder was auch immer Sie sie nennen) oder jetzt können Sie in Erweiterung Methoden mischen kann fließend mit allen notwendigen Repositorien sprechen. Ich habe es nicht genau diese Art und Weise in einem Produktionssystem getan, aber Ihnen ein prägnantes Beispiel zeigen:

public static class Aggregators 
{ 
    // one to one, easy 
    public static Editor GetOwner(this Project p) 
    { 
     return StaticServiceWrapper.editorRep.GetEditorById(p.editorId); 
    } 

    // one to many, medium 
    public static IEnumerable<Project> GetProjects(this Editor e) 
    { 
     return StaticServiceWrapper.projectRep.GetAllProjects() 
       .Where(x => x.editorId == e.Id); 
    } 

    // many to many, harder 
    public static IEnumerable<Editor> GetMembers(this Project p) 
    { 
     var list = StaticServiceWrapper.projectMemberMap.GetAllMemberMaps() 
         .Where(x => x.projectId == p.projectId); 

     foreach (var item in list) 
      yield return StaticServiceWrapper.editorRep.GetEditorById(item.editorId); 
    } 
} 

Grundsätzlich einmal Ihr GetAll, GetById, Hinzufügen, Aktualisieren, Entfernen Object Repository durchgeführt wird, die Sie haben Ich muss die Assoziationen in Ruhe lassen und die Objekt-/Ebenenhierarchie in die spaßigen Teile wie Adapter und Caches und Geschäftslogik ("Oh, mein!") hochziehen.

+0

Wenn die zugrunde liegende Implementierung von IRepository eine Datenbank ist, und Sie eine sehr große Menge an X-Entität haben. Ist es immer noch in Ordnung, GetAll() für das Repository zu verwenden und dann einen LINQ auszuführen, in dem die gesamte Menge abgefragt wird? Was ist, wenn das zu langsam ist, und Sie lieber die Abfrage auf DB-Ebene durchführen möchten, wie würden Sie das mit dem Repository machen? –

+0

Die Verwendung von FindAll() führt nicht dazu, dass die SQL-Abfrage sofort ausgelöst wird. Dies geschieht nur, wenn Sie es in eine Liste, ein einzelnes Element (zum Beispiel mit '.FirstOrDefault() 'oder du machst eine foreach-Schleife darauf. So können Sie alle Arten von Linq-Zeug damit tun, bevor Sie Ihre Abfrage ausführen und die Ergebnisse aus Ihrer DB abgerufen werden. –

3

Es hängt von den Anforderungen Ihrer Anwendung ab. Wenn es ein großes Problem ist, alle Projekte für einen bestimmten Editor zu laden, versuchen Sie es mit einem Lazy Loading-Muster wie Virtual Proxy.

In Bezug auf das Laden der Member-Editoren eines Projekts, wenn Sie Virtual Proxy verwenden, sehe ich kein Problem, den Proxy mit dem EditorRepository zu injizieren, da ich den Proxy nicht als Teil der Domäne betrachte.

Wenn Sie das Aggregat aufteilen, können Sie das Unit of Work Muster als eine Lösung für die Atomarität untersuchen. Dieses Problem ist jedoch nicht nur DDD und ich bin mir sicher, dass es andere Lösungen für das Transaktionsverhalten gibt.

4

Wie wäre es, Verantwortlichkeiten in einen EditorOwner und einen EditorMember aufzuteilen?

Ohne zu wissen, Ihre Domäne, würde ich mir vorstellen, dass sie unterschiedliche Verantwortlichkeiten hätten - zum Beispiel könnte der EditorOwner ziemlich reich sein (und könnte der aggregierte Stamm sein), aber das Projekt muss möglicherweise nur eine begrenzte Menge darüber wissen seine Mitglieder, so kann das EditorMember-Objekt ziemlich leicht sein.

Diese Domänenobjekte können sich auch auf Benutzer beziehen, aber das wäre in einem anderen Kontext.

Hilft das, oder macht es einfach komplizierter?

0

Hier haben Sie zwei verschiedene Beziehungen, eine für Besitz und eine für Mitgliedschaft.

Die Eigentumsbeziehung ist eine einfache für viele (ein Besitzer für jedes Projekt). Die Mitgliederbeziehung ist viele zu viele (viele Redakteure nach Projekt, viele Projekte von Redakteur).

Sie könnten eine Owner-Eigenschaft für die Project-Klasse bereitstellen und eine Methode für das ProjectRepository bereitstellen, um alle Projekte zu erhalten, die einem bestimmten Editor gehören.

Geben Sie für die viele Beziehung eine Members-Eigenschaft für die Project-Klasse und eine Methode für das ProjectRepository ein, um alle Projekte mit dem angegebenen Editor als Mitglied abzurufen.

Es scheint auch, dass Editoren und Projekte Entitäten sind, würde ich wahrscheinlich das Aggregat teilen, aber vielleicht haben diese Begriffe eine bestimmte Bedeutung in Ihrem Kontext, die es Untereinheiten eines Aggregats machen.