Domain-Driven-Design - dépendances externes dans le problème Entity

23

Je voudrais démarrer Domain-Driven-Design, mais il y a plusieurs problèmes que je voudrais résoudre avant de commencer :)

Imaginons que j'ai un groupe et des utilisateurs et lorsque l'utilisateur souhaite rejoindre un groupe, j'appelle une groupsService.AddUserToGroup(group, user)méthode. En DDD, je devrais faire group.JoinUser(user), ce qui semble assez bon.

Le problème apparaît s'il existe des règles de validation pour l'ajout d'un utilisateur ou si certaines tâches externes doivent être démarrées lorsque l'utilisateur est ajouté au groupe. Le fait d'avoir ces tâches conduira l'entité à avoir des dépendances externes.

Un exemple pourrait être - une restriction selon laquelle l'utilisateur ne peut participer qu'à 3 groupes max. Cela nécessitera des appels DB de la méthode group.JoinUser interne pour valider cela.

Mais le fait qu'une entité dépende de certains services / classes externes ne me semble pas si bon et "naturel".

Quelle est la bonne façon de gérer cela dans DDD?

Shaddix
la source

Réponses:

15

Imaginons que j'ai un groupe et des utilisateurs et lorsque l'utilisateur souhaite rejoindre un groupe, j'appelle la méthode groupsService.AddUserToGroup (groupe, utilisateur). Dans DDD, je devrais faire group.JoinUser (user), qui a l'air plutôt bien.

Mais DDD vous encourage également à utiliser des services (sans état) pour effectuer des tâches, si la tâche en cours est trop complexe ou ne rentre pas dans un modèle d'entité. C'est ok d'avoir des services dans la couche domaine. Mais les services de la couche domaine ne doivent inclure que la logique métier. Les tâches externes et la logique d'application (comme l'envoi d'un e-mail), d'autre part, devraient utiliser le service de domaine dans la couche application, dans laquelle vous pourriez avoir un service distinct (application) l'enveloppant par exemple.

Le problème apparaît s'il existe des règles de validation pour l'ajout d'un utilisateur ...

Les règles de validation appartiennent au modèle de domaine! Ils doivent être encapsulés à l'intérieur des objets de domaine (entités, etc.).

... ou certaines tâches externes doivent être démarrées lorsque l'utilisateur est ajouté au groupe. La possession de ces tâches entraînera l'entité ayant des dépendances externes.

Bien que je ne sache pas de quel type de tâche externe vous parlez, je suppose que c'est quelque chose comme l'envoi d'un e-mail, etc. Mais cela ne fait pas vraiment partie de votre modèle de domaine. Il devrait vivre dans la couche application et y être suspendu à mon humble avis. Vous pouvez avoir un service dans votre couche d'application qui fonctionne sur des services de domaine et des entités pour effectuer ces tâches.

Mais le fait qu'une entité dépende de certains services / classes externes ne me semble pas si bon et "naturel".

Ce n'est pas naturel et ne devrait pas se produire. L'entité ne doit pas être au courant de choses qui ne relèvent pas de sa responsabilité. Les services doivent être utilisés pour orchestrer les interactions d'entité.

Quelle est la bonne façon de gérer cela dans DDD?

Dans votre cas, la relation devrait probablement être bidirectionnelle. Que l'utilisateur rejoigne le groupe ou que le groupe le prenne dépend de votre domaine. L'utilisateur rejoint-il le groupe? Ou l'utilisateur est-il ajouté à un groupe? Comment ça marche dans votre domaine?

Quoi qu'il en soit, vous avez une relation bidirectionnelle et pouvez ainsi déterminer la quantité de groupes auxquels l'utilisateur appartient déjà dans le groupe d'utilisateurs. Que vous passiez l'utilisateur au groupe ou le groupe à l'utilisateur est techniquement trivial une fois que vous avez déterminé la classe responsable.

La validation doit ensuite être effectuée par l'entité. Le tout est appelé à partir d'un service de la couche application qui peut également faire des choses techniques, comme envoyer des e-mails, etc.

Cependant, si la logique de validation est vraiment complexe, un service de domaine pourrait être une meilleure solution. Dans ce cas, encapsulez les règles métier et appelez-les à partir de votre couche d'application.

Faucon
la source
Mais si nous déplaçons tant de logique en dehors de l'entité, que faut-il garder à l'intérieur?
SiberianGuy
Les responsabilités directes de l'entité! Si vous pouvez dire "l'utilisateur peut rejoindre un groupe" par exemple, alors c'est la responsabilité de l'entité utilisateur. Parfois, vous devez prendre des décisions de compromis pour des raisons techniques. Je ne suis pas non plus un grand fan des relations bidirectionnelles, mais parfois cela correspond le mieux au modèle. Écoutez donc attentivement lorsque vous parlez du domaine. "Une entité fait ..." "L'entité peut ..." Lorsque vous entendez de telles phrases, ces opérations appartiennent très probablement à l'entité.
Falcon
De plus, vous savez que vous avez besoin d'un service lorsque deux ou plusieurs objets non liés par ailleurs doivent participer à une tâche pour accomplir quelque chose.
Falcon
1
Merci pour ta réponse, Falcon! Btw, j'ai toujours essayé d'utiliser des services sans état, donc je suis un peu plus près de DDD :) Disons que dans un domaine, cette opération UserJoinsToGroup appartient au groupe. Le problème est que pour valider cette opération, j'ai besoin de savoir dans combien de groupes cet utilisateur participe déjà (pour refuser une opération si elle est déjà> 3). Pour savoir que je dois interroger la base de données. Comment puis-je le faire depuis l'entité du Groupe? J'ai d'autres exemples, lorsque j'ai besoin de toucher la base de données dans les opérations qui devraient naturellement appartenir à l'entité (je les posterai si nécessaire :))
Shaddix
2
Eh bien, si j'y pense: qu'en est-il d'une entité GroupMembership? Il peut être construit par une usine et cette usine peut accéder aux dépôts. Ce serait une bonne DDD et résume la création d'adhésions. L'usine peut accéder aux référentiels, crée une adhésion et l'ajoute à l'utilisateur et au groupe respectivement. Cette nouvelle entité pourrait également encapsuler des privilèges. C'est peut-être une bonne idée.
Falcon
3

La façon dont j'aborderais le problème de la validation est la suivante: Créez un service de domaine appelé MembershipService:

class MembershipService : IMembershipService
{
   public MembershipService(IGroupRepository groupRepository)
   { 
     _groupRepository = groupRepository;
   }
   public int NumberOfGroupsAssignedTo(UserId userId)
   {
        return _groupsRepository.NumberOfGroupsAssignedTo(userId);
   }
}

Il faut injecter l'entité du Groupe IMemberShipService. Cela peut être fait au niveau de la classe ou au niveau de la méthode. Supposons que nous le faisons au niveau de la méthode.

class Group{

   public void JoinUser(User user, IMembershipService membershipService)
   {
       if(membershipService.NumberOfGroupsAssignedTo(user.UserId) >= 3)
         throw new BusinessException("User assigned to more than 3 groups. Cannot proceed");

       // do some more stuff
   }
}

Le service Application: GroupServicepeut être injecté à l' IMemberShipServiceaide de l'injection de constructeur, qu'il peut ensuite passer à la JoinUserméthode de la Groupclasse.

Eklavya Gupta
la source
1
Vous voudrez peut-être envisager formater le code source dans votre article pour plus de lisibilité
Benni