Command + CommandHandler 和 Service 有什么区别?

问题描述 投票:0回答:3

我一直在阅读有关使用 Command 对象来表示我们的域公开的用例,以及使用 Command Handler 对象来处理这些命令的内容。

例如:

  • RegisterUserCommand
  • RegisterUserCommandHandler

但它看起来与

RegisterUserService
完全相同,其中命令对象代表
registerUser()
方法的参数。

当然,如果该方法有太多参数,我最终会创建一个对象来包装它们,并且该对象将与

RegisterUserCommand
相同。

那么为什么要用不同的模式来表示同一事物呢?服务是广泛存在的,而不是命令(根据我的经验);我所缺少的有什么区别?简而言之,为什么我要使用其中一种而不是另一种?

design-patterns domain-driven-design cqrs command-pattern
3个回答
42
投票

拥有命令可以让您享受到旧式命令模式的好处:

  • 您可以参数化一个对象,例如一个 UI 元素,带有要执行的命令
  • 您可以存储命令并稍后执行,例如在队列或事务日志中
  • 您可以跟踪您执行的命令,为您实现撤消奠定基础

如果您的服务很大,每个服务都有许多复杂的方法(如果这些方法不复杂,您可能不应该使用 DDD 或 CQRS),那么将每个方法移动到命令处理程序中可能会通过使其更具可组合性来改进您的应用程序,更容易测试等等。毫无疑问,直接从大型服务重构为命令/命令处理程序的人们通常会认为这是后一种模式的好处。但是,您可以通过将大型服务分解为较小的服务来获得相同的好处(如示例中非常具体的服务所建议的那样),因此严格来说,这在服务和命令/命令处理程序之间没有区别。


39
投票

我认为你的质疑是完全正确的,这两个概念在上下文中似乎很相似。 实际上,可能值得回去考虑一下它们的用途。

国内直拨服务

在领域驱动设计中,有不同类型的服务 例如应用程序服务(通常是 UI 服务)、基础设施服务和领域服务。

Jimmy Bogard 出色地解释了这些

简而言之:

域名服务

领域服务的工作是执行通常不适合某个实体的功能。当您有一项需要多种功能的功能时,请考虑使用域服务
实体(聚合/值对象)。举个例子:要计算抵押贷款的费用估计,您需要买家收入/就业的详细信息。您可能需要买家的信用记录,最后您可能需要有关正在考虑抵押贷款的建筑物的信息。

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

应用服务

示例可能是用作 UI 一部分的服务。

基础设施服务

倾向于与外部资源通信的服务(电子邮件发件人、文件系统、xml 文件、ftp 等...)

命令/命令处理程序 (CQRS)

命令查询职责分离。正如罐头上所说;分离:

  1. 针对您的数据源运行查询
  2. 修改(通过命令)您的数据

使用 CQRS 并不总是正确的选择,但根据我的经验,当人们的数据分布在多个数据源中时,人们倾向于使用它。

因此,使用这些命令,您明确要求执行一个工作单元(不要与 UnitOfWork 模式混淆),例如添加FraudRecordCommand 或UpdateNoteCommand。


稍微回顾一下 DDD 服务和 CQRS 命令之间的差异。 我会注意以下几点:

  1. 我还需要命令/命令处理程序吗?我能得到什么,我应该直接使用服务吗?

  2. 我的命令处理程序的工作是处理我的命令的逻辑(命令是一个非常具体的请求)。而 DDD 服务有不同的工作(领域服务:协调多个实体的功能,基础设施服务:与外部服务(例如电子邮件)协作)

  3. 也许可以这样想: CommandHandler Job – 执行代码以运行特定命令(这可能包括使用多个服务)。 服务工作 – 取决于服务类型。

不是最好的例子,但我希望它能让我明白我想说的:

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));
      }     

   }

}

0
投票

这是否意味着命令/查询是事件,即 DTO,需要由命令或查询处理程序处理,而命令或查询处理程序是一种仅处理一个用例的服务?

© www.soinside.com 2019 - 2024. All rights reserved.