Dans le débat sur les modèles de domaine Rich vs. Anemic, Internet regorge de conseils philosophiques, mais ne contient que des exemples faisant autorité. L’objectif de cette question est de trouver des directives définitives et des exemples concrets de modèles de conception pilotés par domaine appropriés. (Idéalement en C #.)
Pour un exemple concret, cette implémentation de DDD semble être fausse:
Les modèles de domaine WorkItem ci-dessous ne sont que des sacs de propriétés, utilisés par Entity Framework pour une base de données code-first. Per Fowler, c'est anémique .
La couche WorkItemService est apparemment une perception erronée courante des services de domaine; il contient toute la logique comportementale / métier du workItem. Per Yemelyanov et d’autres, c’est une procédure . (p. 6)
Donc, si le dessous est faux, comment puis-je le corriger?
Le comportement, à savoir AddStatusUpdate ou Checkout , devrait appartenir à la classe WorkItem correcte?
Quelles dépendances le modèle WorkItem devrait-il avoir?
public class WorkItemService : IWorkItemService {
private IUnitOfWorkFactory _unitOfWorkFactory;
//using Unity for dependency injection
public WorkItemService(IUnitOfWorkFactory unitOfWorkFactory) {
_unitOfWorkFactory = unitOfWorkFactory;
}
public void AddStatusUpdate(int workItemId, int statusId) {
using (var unitOfWork = _unitOfWorkFactory.GetUnitOfWork<IWorkItemUnitOfWork>()) {
var workItemRepo = unitOfWork.WorkItemRepository;
var workItemStatusRepo = unitOfWork.WorkItemStatusRepository;
var workItem = workItemRepo.Read(wi => wi.Id == workItemId).FirstOrDefault();
if (workItem == null)
throw new ArgumentException(string.Format(@"The provided WorkItem Id '{0}' is not recognized", workItemId), "workItemId");
var status = workItemStatusRepo.Read(s => s.Id == statusId).FirstOrDefault();
if (status == null)
throw new ArgumentException(string.Format(@"The provided Status Id '{0}' is not recognized", statusId), "statusId");
workItem.StatusHistory.Add(status);
workItemRepo.Update(workItem);
unitOfWork.Save();
}
}
}
(Cet exemple a été simplifié pour être plus lisible. Le code est certainement toujours maladroit, car c'est une tentative confuse, mais le comportement du domaine était le suivant: statut de la mise à jour en ajoutant le nouveau statut à l'historique des archives. En fin de compte, je suis d'accord avec les autres pourrait juste être manipulé par CRUD.)
Mise à jour
@AlexeyZimarev a donné la meilleure réponse, une vidéo parfaite sur le sujet en C # de Jimmy Bogard, mais elle a apparemment été déplacée dans un commentaire ci-dessous car elle ne donnait pas suffisamment d'informations au-delà du lien. J'ai un brouillon de mes notes résumant la vidéo dans la réponse ci-dessous. N'hésitez pas à commenter la réponse avec des corrections. La vidéo dure une heure mais vaut vraiment la peine d'être visionnée.
Mise à jour - 2 ans plus tard
Je pense que c'est un signe de la maturité naissante de DDD: même après l'avoir étudié pendant 2 ans, je ne peux toujours pas promettre que je connais la "bonne façon" de le faire. Le langage omniprésent, les racines globales et son approche de la conception axée sur le comportement constituent les contributions précieuses de DDD au secteur. L'ignorance de la persistance et le repérage des événements créent de la confusion, et je pense qu'une telle philosophie l'empêche d'adopter plus largement. Mais si je devais refaire ce code, avec ce que j'ai appris, je pense que cela ressemblerait à ceci:
Je suis toujours heureux de recevoir des réponses à ce message (très actif) fournissant un code de bonne pratique pour un modèle de domaine valide.
"I don't want to duplicate all my entities into DTOs simply because I don't need it and it violates DRY, and I also don't want my client application to take a dependency on EntityFramework.dll"
. "Entités" dans le jargon du Entity Framework ne sont pas les mêmes que "Entités" mais dans "Modèle de domaine"Réponses:
La réponse la plus utile a été donnée par Alexey Zimarev et a obtenu au moins 7 votes positifs avant qu'un modérateur ne l'ait déplacée dans un commentaire situé sous ma question initiale ....
Sa réponse:
J'ai pris quelques notes pour résumer la vidéo à la fois pour le bénéfice de mon équipe et pour fournir un peu plus de détail dans ce post. (La vidéo dure une heure, mais chaque minute vaut vraiment la peine si vous en avez le temps. Jimmy Bogard mérite beaucoup de mérite pour son explication.)
N'hésitez pas à commenter tout autre point qui, à votre avis, devrait être inclus, ou si vous pensez que l'une de ces notes n'est pas conforme aux attentes. J'ai essayé de citer directement ou de paraphraser autant que possible.
la source
Votre question ne peut pas être répondue, parce que votre exemple est faux. Plus précisément, parce qu'il n'y a pas de comportement. Du moins pas dans la zone de votre domaine. L'exemple de
AddStatusUpdate
méthode n'est pas une logique de domaine, mais une logique qui utilise ce domaine. Ce type de logique a du sens d'être dans un type de service, qui gère les demandes externes.Par exemple, s'il était exigé qu'un élément de travail spécifique ne puisse avoir que des statuts spécifiques ou qu'il ne puisse avoir que des statuts N, il s'agit alors d'une logique de domaine qui doit faire partie de l'une
WorkItem
ou de l'autreStatusHistory
méthode.La raison de votre confusion est que vous essayez d’appliquer une directive à un code qui n’en a pas besoin. Les modèles de domaine ne sont pertinents que si vous avez beaucoup de logique de domaine complexe. Par exemple. logique qui fonctionne sur les entités elles-mêmes et découle d'exigences. Si le code concerne la manipulation d'entités à partir de données externes, il ne s'agit probablement pas d'une logique de domaine. Mais au moment où vous obtenez beaucoup de
if
données en fonction des données et des entités avec lesquelles vous travaillez, alors c'est la logique de domaine.L'un des problèmes de la modélisation de domaine véritable est qu'il s'agit de gérer des exigences complexes. Et en tant que tel, son véritable pouvoir et ses avantages ne peuvent être présentés sur un simple code. Vous avez besoin de dizaines d'entités ayant des tonnes d'exigences autour d'elles pour vraiment en voir les avantages. Encore une fois, votre exemple est trop simple pour qu'un modèle de domaine puisse vraiment briller.
Enfin, certains aspects de l'ergothérapie que je voudrais mentionner, c'est qu'un véritable modèle de domaine avec une conception POO réelle serait vraiment difficile à conserver avec Entity Framework. Alors que les ORM ont été conçus avec la mise en correspondance de la vraie structure POO avec des structures relationnelles, de nombreux problèmes subsistent et le modèle relationnel se retrouvera souvent dans le modèle POO. Même avec nHibernate, que je considère beaucoup plus puissant que EF, cela peut poser problème.
la source
Votre hypothèse selon laquelle l’encapsulation de votre logique d’entreprise associée à WorkItem dans un «gros service» est un anti-modèle inhérent, ce qui, à mon avis, ne l’est pas nécessairement.
Indépendamment de vos réflexions sur le modèle de domaine anémique, les schémas et pratiques standard typiques d’une application .NET sectorielle encouragent une approche en couches transactionnelle comprenant divers composants. Ils encouragent la séparation de la logique métier du modèle de domaine afin de faciliter la communication d'un modèle de domaine commun entre d'autres composants .NET ainsi que des composants sur différentes piles de technologies ou sur des niveaux physiques.
Un exemple de ceci serait un service Web SOAP basé sur .NET qui communique avec une application cliente Silverlight ayant une DLL contenant des types de données simples. Ce projet d'entité de domaine peut être intégré à un assemblage .NET ou à un assemblage Silverlight, dans lequel les composants Silverlight intéressés qui possèdent cette DLL ne seront pas exposés à des comportements d'objet pouvant dépendre de composants uniquement disponibles pour le service.
Indépendamment de votre position sur ce débat, il s'agit du modèle adopté et accepté présenté par Microsoft et, selon mon opinion professionnelle, ce n'est pas une mauvaise approche, mais un modèle objet qui définit son propre comportement n'est pas nécessairement un anti-modèle non plus. Si vous continuez avec cette conception, il est préférable de comprendre et de comprendre certaines des limitations et des difficultés que vous pourriez rencontrer si vous devez intégrer d'autres composants qui doivent voir votre modèle de domaine. Dans ce cas particulier, vous voudrez peut-être qu'un traducteur convertisse votre modèle de domaine de style orienté objet en objets de données simples qui n'exposent pas certaines méthodes de comportement.
la source
Je me rends compte que cette question est assez ancienne, donc cette réponse est pour la postérité. Je veux répondre avec un exemple concret au lieu d'un exemple basé sur la théorie.
Encapsulez le "changement d'état de l'élément de travail" sur la
WorkItem
classe comme suit:Maintenant, votre
WorkItem
classe est responsable de se maintenir dans un état légal. La mise en œuvre est assez faible, cependant. Le propriétaire du produit souhaite qu'un historique de toutes les mises à jour de statut soit effectué sur leWorkItem
.Nous changeons cela en quelque chose comme ceci:
L'implémentation a radicalement changé, mais l'appelant de la
ChangeStatus
méthode ignore les détails de l'implémentation sous-jacente et n'a aucune raison de se modifier lui-même.Ceci est un exemple d'entité modèle de domaine riche, IMHO.
la source