我一直在阅读使用Command对象来表示我们的域公开的用例,以及Command Handler对象来处理这些命令。
例如:
RegisterUserCommand
RegisterUserCommandHandler
但它看起来与RegisterUserService
完全相同,其中命令对象代表registerUser()
方法的参数。
当然,如果方法参数太多,我最终会创建一个包装它们的对象,该对象与RegisterUserCommand
相同。
那么为什么有不同的模式来表示同样的事情呢?服务很普遍,而不是命令(根据我的经验);我错过了什么区别?简而言之,为什么我会使用一个而不是另一个?
答案 0 :(得分:18)
拥有命令可以获得良好的旧命令模式的好处:
如果您的服务很大,每个都有很多复杂的方法(如果方法不复杂,您可能不应该使用DDD或CQRS),那么将每个方法移动到命令处理程序中可能会改善您的服务通过使其更易组合,更容易测试等应用程序。毫无疑问,直接从大服务重构到命令/命令处理程序的人将这视为后一种模式的好处是很常见的。但是,通过将大型服务分解为较小的服务(如您的示例中的特定服务所示),您可以获得相同的好处,因此严格来说,这不是服务和命令/命令处理程序之间的区别。
答案 1 :(得分:14)
我认为您完全有权质疑这两个概念在上下文中似乎相似。 它可能值得回去并且实际上考虑它们的用途。
在域驱动设计中,有不同类型的服务 例如应用程序服务(通常是UI服务),基础结构服务和域服务。
Jimmy Bogard does an excellent job of explaining these
简而言之:
域名服务
域服务的工作是执行通常不适合一个实体的功能。当您拥有需要各种功能的功能时,请考虑使用域服务 实体(聚合/值对象)。一个例子可能是:要计算抵押贷款可能花费多少的估计,您需要有关买方收入/就业的详细信息。您可能需要买方的信用记录,最后您可能需要有关抵押贷款正在考虑的建筑物的信息。
pricingService.CalculateMortageEstimate(BuyerIncomingDetails bid, BuyerCreditHistory bch, BuildingOverview bo)
申请服务
一个示例可能是用作UI的一部分的服务。
基础设施服务
倾向于与外部资源通信的服务(电子邮件发件人,文件系统,xml文件,ftp等......)
命令查询责任隔离。正如它在锡上所说;分离:
使用CQRS并不总是正确的选择,但根据我的经验,人们倾向于在数据分布在多个数据源时使用它。
因此,使用这些命令,您明确要求执行一个工作单元(不要与UnitOfWork模式混淆),例如AddFraudRecordCommand或UpdateNoteCommand。
对DDD服务和CQRS命令之间的差异进行了很少的更新。 我会注意到以下几点:
我甚至需要Command / CommandHandlers吗?我得到了什么,我应该直接去服务吗?
我的命令处理程序的工作是处理我的命令的逻辑(命令是一个非常具体的请求)。 DDD服务有不同的工作(域服务:协调多个实体的功能,基础设施服务:与外部服务协作,例如电子邮件)
也许可以这样想: CommandHandler作业 - 执行代码以运行特定命令(这可能包括使用多个服务)。 服务工作 - 取决于服务类型。
不是最好的例子,但我希望它能说明我想说的话:
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));
}
}
}