DDD - Dépendances entre le modèle de domaine, les services et les référentiels
Voulais juste savoir comment d'autres ont des couches de leur architecture. Dire que j'ai mes couches comme suit:
Couche Domaine
--Produit
--ProductService (si l'imp aller dans cette couche?)
--IProductService
--IProductRepository
La Couche De L'Infrastructure
--ProductRepository (Imp de IProductRepository dans mon domaine)
Maintenant, quand un nouveau produit est créé, j'ai une obligation d'attribuer un id de produit d'appel de la ProductService.GetNextProductId() la méthode.
Parce que le service a une dépendance sur le référentiel-je configurer la ProductService ctor avec une interface de IProductRepository qui peut être injecté plus tard. quelque chose comme ceci:
public class ProductService : IProductService
{
private IProductRepository _repository;
public ProductService(IProductRepository repository)
{
_repository = repository;
}
public long GetNextProductId()
{
return _repository.GetNextProductId();
}
}
Mon problème est que lorsque j'utilise le service dans la Classe de Produit, je fais référence à l'espace de stockage dans le ctor lors de l'instanciation d'un nouveau ProductService classe. En DDD son une grande ne pas avoir une telle référence. Je' ne suis même pas sûr si mon domaine, la classe est mis en place correctement pour appeler le service, quelqu'un peut-pls conseiller:
public class Product : Entity
{
private ProductService _svc;
private IProductRepository _repository;
public Product(string name, Address address) //It doesnt seem right to put parm for IProductRepository in the ctor?
: base(_svc.GetNextProductId) //This is where i pass the id
{
//where to create an instance of IProductRepository?
}
}
Comment puis-je élégamment résoudre ce problème de conception? Je suis ouvert aux suggestions de l'expérience DDD'ers
EDIT:
Merci pour vous commentaires. J'ai aussi demandé si le service doit être appelée à partir de la Catégorie de Produit. Je n'ai pas utilisé un modèle de fabrique (encore) que la construction de l'objet est toujours simple. Je ne me sens pas que cela justifie une méthode de fabrique encore?
Je suis confus...Mettre le ProductId de côté, si mon Produit de classe a besoin de certaines autres données à partir d'un Service électronique.g GetSystemDateTime() (je sais, mauvais exemple, mais en essayant de démontrer une non db appel) où serait ce service appelée méthode?
Services de DDD sont logique décharges où la logique n'est pas natrual à l'objet du domaine, droit? Alors, Comment est-il colle ensemble?
source d'informationauteur
Vous devez vous connecter pour publier un commentaire.
À votre dernier point, les services de DDD sont un endroit pour mettre ce que je décris comme "maladroite" de la logique. Si vous avez un certain type de logique ou de flux de travail qui a des dépendances sur d'autres entités, c'est le type de logique qui généralement n'est pas "apte" à l'intérieur d'un domaine de l'objet lui-même. Exemple: Si j'ai une méthode sur mon objet de l'entreprise à effectuer un certain type de validation, la classe de service peut exécuter cette méthode (toujours en gardant le réel de la logique de validation liés à l'entité à l'intérieur de sa classe)
Un autre très bon exemple, j'ai toujours mentionner le transfert de fonds de la méthode. Tu n'aurais pas un compte de transfert de l'objet à partir d'un objet à un autre, mais au lieu de cela vous avez un service qui prend les "pour" et le compte d' "à partir de" compte. Ensuite, à l'intérieur du service, vous invoque la méthode de retrait sur votre "" du compte et la méthode de dépôt sur votre "compte". Si vous avez essayé de mettre cela à l'intérieur de l'entité de compte lui-même, il allait se sentir maladroit.
Un excellent podcast qui parle en profondeur sur ce sujet peuvent être trouvées ici. David Laribee fait un très bon travail en expliquant maintenant seulement le "comment" mais le "pourquoi" de DDD.
Votre modèle de domaine ne doit pas avoir de référence à ProductService, ni à IProductRepository. Si vous créez un nouveau Produit, il doit être créé par le biais d'une usine, l'Usine peut utiliser ProductService pour obtenir un id de produit.
En fait j'avais envelopper ProductService avec une interface appropriée, comme IProductIdGeneratorService de sorte que vous pouvez injecter dans l'usine à l'aide de votre conteneur IoC.
Ici est de savoir comment je structure de votre problème. Je crois que c'est également recommandée DDD façon de faire.
Fondamentalement, ProductService est le cadre de votre Demande de Service, qui est, l'entrée et la sortie de tout ce qui a besoin d'utiliser votre nom de domaine ou de la croix, à sa frontière. Il est responsable de la délégation de chaque cas d'utilisation de composants qui peuvent traiter avec elle, et la coordination entre tous ces composants, si un grand nombre est nécessaire pour réaliser le cas d'utilisation.
Produit est votre AggregateRoot et une Entité dans votre Domaine. Il est responsable de dicter le contrat de la UbiquitousLanguage que la capture du nom de Domaine de votre entreprise. Donc, en soi, cela signifie que votre domaine a un concept de Produit, qui contient les Données et les Comportements, selon les données et les comportements de vous exposer publiquement devez avoir un concept de la UbiquitousLanguage. C'est le terrain ne devrait pas avoir de dépendances externes à l'extérieur du modèle de domaine, donc pas de services. Mais, de méthodes peuvent prendre les Services de Domaine en tant que paramètres pour l'aider à exécuter le comportement logique.
ProductIdGenerator est un exemple d'un tel Service de Domaine. Les Services de domaine encapsuler le comportement logique qui traverse en dehors d'une Entité propre limite. Donc, si vous avez la logique qui nécessite d'autres agrégats racines, ou des services externes comme Référentiel, le Système de Fichiers, Cryptographie, etc. Fondamentalement, toute la logique que vous ne pouvez pas séance d'entraînement à partir de l'intérieur de votre Entité, sans avoir besoin de rien d'autre, vous pourriez avoir besoin d'un Service de Domaine. Si la logique est à l'englobe et semble conceptuellement peut pas vraiment leur place en tant que méthode de votre Entité, c'est un signe que vous pourriez avoir besoin d'un tout nouveau Service d'Application des cas d'utilisation, juste pour lui, ou vous avez manqué une Entité dans votre conception. Il est également possible d'utiliser le Service de Domaine de l'Application de Service directement, dans un double envoi. C'est un peu similaire à C# les méthodes d'extension par rapport à la normale méthode statique.
=========== Pour Répondre à votre Modifier les questions ===============
Services de domaine peut être appelée à partir de la classe de produit si ils sont passés comme une référence temporaire par le biais d'un paramètre de méthode. Les Services d'Application ne doit jamais être appelée à partir de la catégorie de Produit.
Cela dépend de ce que vous comptez vous prendre plus de temps, faire une usine aujourd'hui, même si vous n'avez pas de multiples construction logique, ou refactoring plus tard, quand vous le faites. Je pense que c'est pas la peine pour les entités qui n'ont pas besoin d'être construit à plus d'un titre. Comme wikipedia explique, l'usine est utilisé pour faire ce que chaque constructeur de faire plus explicite et dérivable. Dans mon exemple, le MakeNew usine explique ce que cette construction particulière de l'Entité qui sert de but: créer un nouveau Produit. Vous pourriez avoir plus d'usine comme MakeExisting, MakeSample, MakeDeprecated, etc. Chacun de ces usine serait de créer un Produit, mais à des fins différentes et des manières légèrement différentes. Sans une Usine, tous ces constructeurs serait Produit nommé() et qu'il serait difficile de savoir qui l'on est pour ce qui et fait quoi. L'inconvénient est que l'Usine sont plus difficile à travailler lorsque vous étendez votre entité, l'entité enfant ne peut pas utiliser le parent de l'Usine pour créer un enfant, c'est pourquoi j'ai tendance à faire toute la construction de la logique à l'intérieur de l'constructeurs de toute façon, et la seule utilisation de l'Usine pour avoir un joli nom pour eux.
Dites-vous pensé de la Date de la mise en œuvre était un détail de l'infrastructure. Vous devez créer une abstraction autour d'elle pour l'utiliser dans votre application. Il serait de commencer avec une interface, peut-être quelque chose comme IDateTimeProvider. Cette interface aurait une méthode GetSystemDateTime().
Vos Services d'Application serait gratuite pour instancier un IDateTimeProvider et appeler ses méthodes, à tout moment, qu'il ne pouvait transmettre le résultat à des Agrégats, des Entités, des Services de Domaine, ou tout autre chose qui en auraient besoin.
De vos Services de Domaine serait libre de maintenir une référence à IDateTimeProvider comme un champ de classe, mais il ne doit pas créer l'instance elle-même. Soit il la reçoit par le biais de l'injection de dépendance, ou il le demande par le Service de localisation.
Enfin, votre Entités et d'Agrégation des Racines et des Objets de Valeur serait d'appel gratuit GetSystemDateTime() et d'autres méthodes de IDateTimeProvider, mais pas directement. Il aurait besoin d'aller à travers la double expédition, où vous donner un Service de Domaine en tant que paramètre de l'un de ses méthodes, et il serait de Domaine Service à la requête de l'info qu'il veut, ou d'exécuter le comportement dont il a besoin. Il peut également passer à nouveau dans le Domaine du Service, là où le service de domaine serait de faire de l'interrogation et de la configuration.
Si vous considérez votre IDateTimeProvider pour être réellement un Domaine de Service, comme une partie de l'omniprésence de la Langue, que vos Entités et d'Agrégation des Racines pouvez simplement appeler des méthodes sur lui directement, il ne peut pas contenir une référence à elle comme un champ de classe, mais les variables locales de paramètres de la méthode sont beaux.
Je pense que toute ma réponse a fait ce assez clair déjà. Fondamentalement, vous avez 3 possibilités pour le collage de tout cela (que je pense de l'instant au moins).
1) Une Demande de Service instancie un Service de Domaine, appelle une méthode sur elle, et passe le retour des valeurs à quelque chose d'autre qui en avait bien besoin (repo, entité racine d'agrégat, objet de valeur, un autre domaine de service, les usines, etc.).
2) Un Service de Domaine est instancié par le Domaine d'Application et transmis comme paramètre à une méthode de quelque chose qui va l'utiliser. Quelle que soit utilise, ne garde pas une référence permanente pour elle, c'est une variable locale seulement.
3) Un Service de Domaine est instancié par le Domaine d'Application et transmis comme paramètre à une méthode de quelque chose qui va l'utiliser. Quelle que soit utilise, utilise double expédition à utiliser le Domaine Service à un non dépendant. Cela signifie qu'il transmet à la méthode de Service de Domaine une référence à lui-même, comme dans DomainService.DoSomething(ce, nom, Adresse).
Espère que cette aide.
Vos commentaires sont les bienvenus si j'ai fait quelque chose de mal ou qui va à l'encontre de DDD meilleures pratiques.
Si je comprends votre question correctement, vous dites que votre classe de Produit est l'appel de la ProductService classe. Il ne devrait pas. Vous devez faire cela dans une usine de classe qui est responsable de la création et la configuration d'un Produit. Lorsque vous appelez cette méthode peut également dépendre de la date de l'émission le ProductId: Nous avons ce que peut être un cas similaire en qui nous avons besoin pour obtenir un certain nombre de notre héritage système de comptabilité de projet. - Je différer d'obtenir le nombre jusqu'à ce que le projet est maintenu, de sorte que nous ne perdez pas de numéros ou d'avoir des lacunes. Si vous êtes dans une situation similaire, vous souhaitez peut-être problème de la référence du produit dans un référentiel méthode save au lieu d'lorsque l'objet est créé.
Comme un de côté, pensez-vous vraiment que vous aurez jamais plus d'un ProductService ou ProductRepository? Si non, alors je ne vous embêtez pas avec les interfaces.
Modifiées afin d'ajouter:
Je recommande de commencer petit et de le garder simple, en commençant par les deux classes, de Produits et de ProductServices. ProductServices permettrait d'assurer tous les services, y compris l'usine et le référentiel, puisque vous pouvez penser à ceux que les services spécialisés.
Pourquoi avez-vous besoin de l'id de produit lorsque vous créez le produit dans la mémoire? Habituellement, l'id de produit est sertie lorsque vous créez le produit dans votre référentiel.
Prendre un coup d'oeil au code suivant:
var id1 = _repository.GetNextProductId();
var id2 = _repository.GetNextProductId();
Il sera de retour deux différents id de produit?
Si la réponse est oui, alors il est sûr (mais encore maladroit);
Si la réponse est non, alors vous aurez un énorme problème;
En accord avec Marcelo, vous devriez probablement d'abord déterminer si l'ID de produit est vraiment un modèle du domaine de concept. Si l'entreprise les utilisateurs ne jamais utiliser ou d'avoir une quelconque idée de l'ID de produit et font généralement référence du produit, le nom, le numéro de référence ou un produit naturel ID composé de nom + dimensions, alors c'est quoi le modèle de domaine doit être conscient de.
Cela étant dit, voici comment je structure mon DDD projets en supposant que l'ID de produit est un champ de numérotation automatique dans la base de données:
Projet.Entreprise (Modèle Du Domaine)
Pas de références et, par conséquent, aucune dépendance à quoi que ce soit.
Projet.La mise en œuvre
Projet.ApplicationName (Couche Présentation)
Si nécessaire, vous pouvez avoir:
Projet.Services (Application Couche de Services qui utilise des Otd entre lui-même et la Couche de Présentation)