Si je comprends bien, en DDD, il convient d'utiliser un modèle de référentiel avec une racine agrégée. Ma question est, dois-je renvoyer les données en tant qu'entités ou objets de domaine / DTO?
Peut-être qu'un code expliquera ma question plus en détail:
Entité
public class Customer
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Dois-je faire quelque chose comme ça?
public Customer GetCustomerByName(string name) { /*some code*/ }
Ou quelque chose comme ça?
public class CustomerDTO
{
public Guid Id { get; set; }
public FullName { get; set; }
}
public CustomerDTO GetCustomerByName(string name) { /*some code*/ }
Question supplémentaire:
- Dans un référentiel, dois-je retourner IQueryable ou IEnumerable?
- Dans un service ou un dépôt, dois - je faire quelque chose comme ..
GetCustomerByLastName
,GetCustomerByFirstName
,GetCustomerByEmail
? ou tout simplement faire une méthode qui ressemble à quelque choseGetCustomerBy(Func<string, bool> predicate)
?
c#
domain-driven-design
repository-pattern
poisson-code
la source
la source
GetCustomerByName('John Smith')
reviendra si vous avez vingt John Smith dans votre base de données? On dirait que vous supposez que deux personnes n'ont pas le même nom.Réponses:
Eh bien, cela dépend entièrement de vos cas d'utilisation. La seule raison pour laquelle je peux penser à retourner un DTO au lieu d'une entité complète est si votre entité est énorme et que vous n'avez besoin que de travailler sur un sous-ensemble de celui-ci.
Si tel est le cas, vous devriez peut-être reconsidérer votre modèle de domaine et diviser votre grande entité en entités plus petites connexes.
Une bonne règle est de toujours renvoyer le type le plus simple (le plus élevé dans la hiérarchie d'héritage) possible. Donc, revenez
IEnumerable
sauf si vous voulez laisser le consommateur du référentiel travailler avec unIQueryable
.Personnellement, je pense que le retour d'un
IQueryable
est une abstraction qui fuit, mais j'ai rencontré plusieurs développeurs qui soutiennent avec passion que ce n'est pas le cas. Dans mon esprit, toute logique d'interrogation devrait être contenue et cachée par le référentiel. Si vous autorisez le code appelant à personnaliser leur requête, quel est l'intérêt du référentiel?Pour la même raison que je l'ai mentionné au point 1, ne l' utilisez certainement pas
GetCustomerBy(Func<string, bool> predicate)
. Cela peut sembler tentant au premier abord, mais c'est exactement pourquoi les gens ont appris à détester les référentiels génériques. C'est fuyant.Des choses comme
GetByPredicate(Func<T, bool> predicate)
ne sont utiles que lorsqu'elles sont cachées derrière des classes concrètes. Donc, si vous aviez une classe de base abstraite appeléeRepositoryBase<T>
qui exposaitprotected T GetByPredicate(Func<T, bool> predicate)
qui n'était utilisée que par des référentiels concrets (par exemple,public class CustomerRepository : RepositoryBase<Customer>
) alors ce serait bien.la source
Il existe une importante communauté de personnes qui utilisent CQRS pour implémenter leurs domaines. Mon sentiment est que, si l'interface de votre référentiel est analogue aux meilleures pratiques utilisées par eux, vous ne vous égarerez pas trop.
D'après ce que j'ai vu ...
1) Les gestionnaires de commandes utilisent généralement le référentiel pour charger l'agrégat via un référentiel. Les commandes ciblent une seule instance spécifique de l'agrégat; le référentiel charge la racine par ID. Il n'y a pas, comme je peux le voir, un cas où les commandes sont exécutées sur une collection d'agrégats (à la place, vous devez d'abord exécuter une requête pour obtenir la collection d'agrégats, puis énumérer la collection et émettre une commande pour chacun.
Par conséquent, dans des contextes où vous allez modifier l'agrégat, je m'attendrais à ce que le référentiel retourne l'entité (aka la racine d'agrégat).
2) Les gestionnaires de requêtes ne touchent pas du tout aux agrégats; au lieu de cela, ils fonctionnent avec des projections des agrégats - objets de valeur qui décrivent l'état de l'agrégat / agrégats à un moment donné. Pensez donc à ProjectionDTO, plutôt qu'à AggregateDTO, et vous avez la bonne idée.
Dans des contextes où vous allez exécuter des requêtes sur l'agrégat, le préparer pour l'affichage, etc., je m'attends à voir un DTO, ou une collection DTO, renvoyé, plutôt qu'une entité.
Tous vos
getCustomerByProperty
appels me ressemblent à des requêtes, donc ils entrent dans cette dernière catégorie. Je voudrais probablement utiliser un seul point d'entrée pour générer la collection, donc je chercherais à voir siest un choix raisonnable; les gestionnaires de requêtes construiraient alors la spécification appropriée à partir des paramètres donnés et passeraient cette spécification au référentiel. L'inconvénient est que la signature suggère vraiment que le référentiel est une collection en mémoire; il n'est pas clair pour moi que le prédicat vous achète beaucoup si le référentiel n'est qu'une abstraction de l'exécution d'une instruction SQL sur une base de données relationnelle.
Cependant, il existe certains modèles qui peuvent aider. Par exemple, au lieu de construire la spécification à la main, transmettez au référentiel une description des contraintes et laissez l'implémentation du référentiel décider quoi faire.
Avertissement: Java comme la frappe détectée
En conclusion: le choix entre fournir un agrégat et fournir un DTO dépend de ce que vous attendez du consommateur. Je suppose qu'une implémentation concrète prend en charge une interface pour chaque contexte.
la source
getCustomersThatSatisfy(Specification spec)
je vais simplement énumérer les propriétés dont j'avais besoin pour les options de recherche. Suis-je bien compris?Sur la base de ma connaissance de DDD, vous devriez avoir ceci,
Cela dépend, vous trouverez des vues mixtes de différents peuples pour cette question. Mais je crois personnellement que si votre référentiel sera utilisé par un service comme CustomerService, vous pouvez utiliser IQueryable, sinon restez avec IEnumerable.
Les référentiels doivent-ils renvoyer IQueryable?
Dans votre référentiel, vous devriez avoir une fonction générique comme vous l'avez dit,
GetCustomer(Func predicate)
mais dans votre couche de service, ajoutez trois méthodes différentes, car il est possible que votre service soit appelé à partir de différents clients et qu'ils nécessitent des DTO différents.Vous pouvez également utiliser le modèle de référentiel générique pour les CRUD courants, si vous n'êtes pas déjà au courant.
la source