Soweit ich weiß, ist es in DDD angebracht, ein Repository-Muster mit einem aggregierten Stamm zu verwenden. Meine Frage ist, sollte ich die Daten als Entität oder Domänenobjekte / DTO zurückgeben?
Vielleicht erklärt ein Code meine Frage weiter:
Entität
public class Customer
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Soll ich so etwas machen?
public Customer GetCustomerByName(string name) { /*some code*/ }
Oder sowas?
public class CustomerDTO
{
public Guid Id { get; set; }
public FullName { get; set; }
}
public CustomerDTO GetCustomerByName(string name) { /*some code*/ }
Zusätzliche Frage:
- Soll ich in einem Repository IQueryable oder IEnumerable zurückgeben?
- In einem Dienst oder Repository, sollte ich so etwas tun ..
GetCustomerByLastName
,GetCustomerByFirstName
,GetCustomerByEmail
? oder einfach eine Methode machen, die so etwas wie istGetCustomerBy(Func<string, bool> predicate)
?
c#
domain-driven-design
repository-pattern
Codefisch
quelle
quelle
GetCustomerByName('John Smith')
zurückgegeben, wenn Sie zwanzig John Smiths in Ihrer Datenbank haben? Es sieht so aus, als würden Sie davon ausgehen, dass keine zwei Personen denselben Namen haben.Antworten:
Nun, das hängt ganz von Ihren Anwendungsfällen ab. Der einzige Grund, warum ich daran denken kann, ein DTO anstelle einer vollständigen Entität zurückzugeben, ist, wenn Ihre Entität riesig ist und Sie nur an einer Teilmenge davon arbeiten müssen.
Wenn dies der Fall ist, sollten Sie möglicherweise Ihr Domain-Modell überdenken und Ihre große Entität in verwandte kleinere Entitäten aufteilen.
Eine gute Faustregel ist, immer den einfachsten (höchsten in der Vererbungshierarchie) Typ zurückzugeben.
IEnumerable
Kehren Sie also zurück, es sei denn, Sie möchten, dass der Repository-Konsument mit einem arbeitetIQueryable
.Persönlich denke ich, dass die Rückgabe
IQueryable
eine undichte Abstraktion ist, aber ich habe mehrere Entwickler getroffen, die leidenschaftlich argumentieren, dass dies nicht der Fall ist. Meiner Meinung nach sollte die gesamte Abfragelogik im Repository enthalten und verborgen sein. Wenn Sie zulassen, dass aufrufender Code die Abfrage anpasst, wozu dient das Repository dann?Aus dem gleichen Grund, den ich in Punkt 1 erwähnt habe, definitiv nicht verwenden
GetCustomerBy(Func<string, bool> predicate)
. Es mag zunächst verlockend erscheinen, aber genau deshalb haben die Leute gelernt, generische Repositories zu hassen. Es ist undicht.Dinge wie
GetByPredicate(Func<T, bool> predicate)
sind nur dann nützlich, wenn sie sich hinter konkreten Klassen verstecken. Wenn Sie also eine abstrakte Basisklasse namens "RepositoryBase<T>
Belichtet" hätten,protected T GetByPredicate(Func<T, bool> predicate)
die nur von konkreten Repositorys (z. B.public class CustomerRepository : RepositoryBase<Customer>
) verwendet wurde, wäre das in Ordnung.quelle
Es gibt eine beträchtliche Community von Leuten, die CQRS verwenden, um ihre Domänen zu implementieren. Ich bin der Meinung, dass Sie nicht zu weit in die Irre gehen werden, wenn die Schnittstelle Ihres Repositorys den von ihnen verwendeten Best Practices entspricht.
Basierend auf dem, was ich gesehen habe ...
1) Befehlshandler verwenden normalerweise das Repository, um das Aggregat über ein Repository zu laden. Befehle zielen auf eine einzelne bestimmte Instanz des Aggregats ab. Das Repository lädt den Stamm nach ID. Wie ich sehen kann, gibt es keinen Fall, in dem die Befehle für eine Sammlung von Aggregaten ausgeführt werden (stattdessen würden Sie zuerst eine Abfrage ausführen, um die Sammlung von Aggregaten abzurufen, dann die Sammlung aufzählen und jeweils einen Befehl ausgeben.
In Kontexten, in denen Sie das Aggregat ändern möchten, würde ich daher erwarten, dass das Repository die Entität (auch als Aggregatstamm bezeichnet) zurückgibt.
2) Abfragehandler berühren die Aggregate überhaupt nicht. Stattdessen arbeiten sie mit Projektionen der Aggregate - Wertobjekte, die den Status des Aggregats / der Aggregate zu einem bestimmten Zeitpunkt beschreiben. Denken Sie also eher an ProjectionDTO als an AggregateDTO, und Sie haben die richtige Idee.
In Kontexten, in denen Sie Abfragen für das Aggregat ausführen, es für die Anzeige vorbereiten usw., würde ich erwarten, dass ein DTO oder eine DTO-Sammlung anstelle einer Entität zurückgegeben wird.
Alle Ihre
getCustomerByProperty
Anrufe sehen für mich wie Anfragen aus, sodass sie in die letztere Kategorie fallen würden. Ich würde wahrscheinlich einen einzelnen Einstiegspunkt verwenden wollen, um die Sammlung zu generieren, also würde ich nachsehen, obist eine vernünftige Wahl; Die Abfragehandler würden dann die entsprechende Spezifikation aus den angegebenen Parametern erstellen und diese Spezifikation an das Repository übergeben. Der Nachteil ist, dass die Signatur wirklich darauf hindeutet, dass das Repository eine Sammlung im Speicher ist. Mir ist nicht klar, dass das Prädikat Ihnen viel kostet, wenn das Repository nur eine Abstraktion zum Ausführen einer SQL-Anweisung für eine relationale Datenbank ist.
Es gibt jedoch einige Muster, die helfen können. Anstatt die Spezifikation manuell zu erstellen, übergeben Sie beispielsweise eine Beschreibung der Einschränkungen an das Repository und lassen Sie die Implementierung des Repositorys entscheiden, was zu tun ist.
Warnung: Java-ähnliche Eingabe erkannt
Fazit: Die Wahl zwischen der Bereitstellung eines Aggregats und der Bereitstellung eines DTO hängt davon ab, was Sie vom Verbraucher erwarten. Meine Vermutung wäre eine konkrete Implementierung, die eine Schnittstelle für jeden Kontext unterstützt.
quelle
getCustomersThatSatisfy(Specification spec)
liste ich einfach die Eigenschaften auf, die ich für die Suchoptionen benötigte. Verstehe ich es richtig?Basierend auf meinem Wissen über DDD sollten Sie dies haben,
Es kommt darauf an, dass Sie für diese Frage gemischte Ansichten von verschiedenen Völkern finden. Ich persönlich bin jedoch der Meinung, dass Sie IQueryable verwenden können, wenn Ihr Repository von einem Dienst wie CustomerService verwendet wird, andernfalls bleiben Sie bei IEnumerable.
Sollten Repositories IQueryable zurückgeben?
In Ihrem Repository sollten Sie eine generische Funktion haben, wie Sie gesagt haben,
GetCustomer(Func predicate)
aber in Ihrer Service-Schicht drei verschiedene Methoden hinzufügen. Dies liegt daran, dass Ihr Service möglicherweise von verschiedenen Clients aufgerufen wird und diese unterschiedliche DTOs benötigen.Sie können auch das generische Repository-Muster für die allgemeine CRUD verwenden, wenn Sie dies noch nicht wissen.
quelle