web-dev-qa-db-fra.com

Quelle est la différence entre Command + CommandHandler et Service?

J'ai lu sur l'utilisation d'objets Command pour représenter des cas d'utilisation que notre domaine expose et des objets Gestionnaire de commandes pour traiter ces commandes.

Par exemple:

  • RegisterUserCommand
  • RegisterUserCommandHandler

Mais cela ressemble exactement à un RegisterUserService, où l'objet de commande représenterait les paramètres de la méthode registerUser().

Et bien sûr, si la méthode avait trop de paramètres, je finirais par créer un objet pour les envelopper et cet objet serait le même que le RegisterUserCommand.

Alors pourquoi avoir un motif différent pour représenter la même chose? Les services sont répandus, pas les commandes (d'après mon expérience); quelle est la différence ici qui me manque? En bref, pourquoi devrais-je utiliser l'un plutôt que l'autre?

38
Matthieu Napoli

Avoir des commandes vous donne les avantages du bon vieux modèle de commande:

  • vous pouvez paramétrer un objet, par ex. un élément d'interface utilisateur, avec une commande à effectuer
  • vous pouvez stocker une commande et l'exécuter plus tard, par exemple dans une file d'attente ou un journal des transactions
  • vous pouvez suivre les commandes que vous avez exécutées, vous donnant une base pour implémenter l'annulation

Si vos services étaient volumineux, chacun avec de nombreuses méthodes complexes (et si les méthodes n'étaient pas complexes, vous ne devriez probablement pas utiliser DDD ou CQRS), le déplacement de chaque méthode dans un gestionnaire de commandes pourrait améliorer votre application en la rendant plus composable, plus facile à tester, etc. Sans aucun doute, il est courant pour les personnes qui refactorisent directement des gros services aux gestionnaires de commandes/commandes de considérer cela comme un avantage de ce dernier modèle. Mais vous pourriez obtenir le même avantage en décomposant les grands services en plus petits (comme suggéré par le service très spécifique dans votre exemple), donc à proprement parler ce n'est pas une différence entre les services et les commandes/gestionnaires de commandes.

25
Dave Schweisguth

Je pense que vous avez tout à fait raison de remettre en question le fait que ces deux concepts semblent similaires dans leur contexte. Cela vaut probablement la peine de revenir en arrière et de considérer, pratiquement, à quoi ils sont destinés.

Services DDD

Dans Domain Driven Design, il existe différents types de services, par exemple Services d'application (généralement les services d'interface utilisateur), services d'infrastructure et services de domaine.

Jimmy Bogard fait un excellent travail pour les expliquer

En un mot:

Services de domaine

Le travail des services de domaine consiste à exécuter des fonctionnalités qui ne conviennent généralement pas à une entité. Envisagez d'utiliser un service de domaine lorsque vous disposez d'un élément de fonctionnalité qui nécessite une variété de
entités (objets agrégés/valeur). Un exemple peut-être: pour calculer une estimation du coût d'une hypothèque, vous avez besoin des détails sur le revenu/l'emploi de l'acheteur. Vous pouvez avoir besoin des antécédents de crédit de l'acheteur et, enfin, vous pourriez avoir besoin d'informations sur le bâtiment pour lequel l'hypothèque est envisagée.

pricingService.CalculateMortageEstimate(BuyerIncomingDetails bid, BuyerCreditHistory bch, BuildingOverview bo)

Services d'application

Un exemple peut être des services utilisés dans le cadre de l'interface utilisateur.

Services d'infrastructure

Services qui ont tendance à communiquer avec des ressources externes (expéditeurs d'emails, systèmes de fichiers, fichiers xml, ftp etc ...)

Command/CommandHandlers (CQRS)

Ségrégation de responsabilité de requête de commande. Comme il est dit sur l'étain; une séparation de:

  1. exécution de requêtes sur votre source de données
  2. Modifier (via des commandes) vos données

utiliser CQRS n'est pas toujours la bonne option, mais d'après mon expérience, les gens ont tendance à l'utiliser lorsque leurs données sont réparties sur plusieurs sources de données.

Ainsi, avec les commandes, vous demandez explicitement qu'une unité de travail (à ne pas confondre avec le modèle UnitOfWork) soit exécutée, par ex. AddFraudRecordCommand ou UpdateNoteCommand.


Avec ce petit rafraîchissement sur les différences entre les services DDD et les commandes CQRS. Je voudrais noter les choses suivantes:

  1. Ai-je même besoin de Command/CommandHandlers? Qu'est-ce que je gagne, dois-je aller directement aux services?

  2. Le travail de mon gestionnaire de commandes est de gérer la logique de ma commande (une commande étant une demande très spécifique). Alors que les services DDD ont des tâches différentes (Services de domaine: coordonner les fonctionnalités de plusieurs entités, Services d'infrastructure: collaborer avec des services externes, par exemple la messagerie électronique)

  3. Pensez-y peut-être comme ceci: CommandHandler Job - exécutez le code pour exécuter la commande spécifique (cela peut inclure l'utilisation de plusieurs services). Travail de service - Selon le type de service dont il s'agit.

Pas le meilleur exemple, mais j'espère que cela éclaire ce que j'essaie de dire:

public class CalculateFraudProbabilityCommandHandler : CommandHandler<CalculateFraudProbabilityCommand>
{
         IFraudService _fraudService;
         IEmailNotifier _notifier;
         ICustomerRepository _customerRepo;


  public CalculateFraudProbabilityCommandHandler(ICustomerRepository customerRepo, IFraudService fraudService, IEmailNotifier notifier) 
  {     
        _fraudService = fraudService; //Domain Service  
        _notifier = notifier;         //Infrastructure Service  
        _customerRepo = customerRepo; //Repository
  }

 //Execute Command
 public void Execute(CalculateFraudProbabilityCommand command) {

     Customer customer = _customerRepository.GetById(command.CustomerId);
     FraudHistory fraudHistory = _fraudService.RetrieveFraudHistory(customer);

     //any fraud recently? if so, let someone know!
      if(fraudHistory.FraudSince(DateTime.Now.AddYear(-1)) {
           _notifier.SendEmail(_fraudService.BuildFraudWarningEmail(customer,      fraudHistory));
      }     

   }

}
23
Mike