Domain driven design 将域服务注入DDD中的AggregateRoot

Domain driven design 将域服务注入DDD中的AggregateRoot,domain-driven-design,domainservices,Domain Driven Design,Domainservices,DDD中常见的建议是聚合根不使用域服务。域服务是协调两个聚合根以实现行为 当我看到这个由RinatAbdullin写的标题为的博客时,我真的很惊讶。在域服务部分,您将看到域服务被注入到聚合根目录中 聚合根目录是否可以接受域服务?将任何内容注入域对象是非常困难的,而且这样做与技术相关。在java中,需要在编译时将方面编织到域类中。虽然我可能在这一点上弄错了,但我认为大多数DDD领导人认为这通常是一个坏主意。两者都积极地劝阻,我喜欢听他们的。要获得完整的解释,请阅读弗农。中的“是”。如果AR确实需要

DDD中常见的建议是聚合根不使用域服务。域服务是协调两个聚合根以实现行为

当我看到这个由RinatAbdullin写的标题为的博客时,我真的很惊讶。在域服务部分,您将看到域服务被注入到聚合根目录中


聚合根目录是否可以接受域服务?

将任何内容注入域对象是非常困难的,而且这样做与技术相关。在java中,需要在编译时将方面编织到域类中。虽然我可能在这一点上弄错了,但我认为大多数DDD领导人认为这通常是一个坏主意。两者都积极地劝阻,我喜欢听他们的。要获得完整的解释,请阅读弗农。

中的“是”。如果AR确实需要一个服务来完成其部分工作,那么您可以将其作为方法参数注入。如果AR的大部分行为都需要服务,那么很可能是建模错误。

我发现服务非常好。它基于Vaughn Vernon的书,通过实际需要此服务的方法调用在域模型中“注入”域服务

公共类采购订单
{
公共字符串Id{get;private set;}
公共字符串VendorId{get;private set;}
公共字符串PONumber{get;private set;}
公共字符串说明{get;private set;}
公共十进制总数{get;private set;}
公共日期时间提交日期{get;private set;}
公共ICollection发票{get;private set;}
公共十进制发票总额
{
获取{返回this.Invoices.Select(x=>x.Amount.Sum();}
}
公共场所的声音很响
{
获取{返回此.Total x.VendorInvoiceNumber.Equals(
vendorInvoiceNumber,StringComparison.OrdinalIgnoreCase);
}
公共发票(IINVoiceNumber生成器、,
字符串vendorInvoiceNumber、DateTime日期、十进制金额)
{
//这些保安人员维护采购订单的业务完整性。
如果(此.IsFullyInvoiced)
抛出新异常(“采购订单已全部开票”);
if(包含语音(卖方发票号码))
抛出新异常(“重复发票!”);
var发票编号=generator.GenerateInvoiceNumber(
本文件编号:VendorId、vendorInvoiceNumber、日期);
var发票=新发票(发票编号、供应商发票编号、日期、金额);
此.发票.添加(发票);
DomainEvents.Raise(新的PurchaseOrderInvoicedEvent(this.Id,invoice.InvoiceNumber));
退货发票;
}
}
公共类PurchaseOrderService
{
public PurchaseOrderService(IPurchaseOrderRepository存储库,
IINVoiceNumber发电机发票编号发电机)
{
this.repository=存储库;
this.invoiceNumberGenerator=invoiceNumberGenerator;
}
只读IPurchaseOrderRepository存储库;
只读IINVoiceNumber生成器InvoiceNumber生成器;
公共作废发票(字符串purchaseOrderId,
字符串vendorInvoiceNumber、DateTime日期、十进制金额)
{
//事务管理,以及提交工作单元
//可以移动到环境基础设施。
使用(var ts=new TransactionScope())
{
var purchaseOrder=this.repository.Get(purchaseOrderId);
if(purchaseOrder==null)
抛出新异常(“未找到PO!”);
purchaseOrder.Invoice(此.InvoiceNumber发电机,
卖方发票编号、日期、金额);
this.repository.Commit();
ts.完成();
}
}
}

请,忽略这篇文章。这篇文章是很久以前写的,显然是错误的。如果使用AggregateRoot和DomainService模式实现模块,我建议使用更高的逻辑(例如请求处理程序),负责:

  • 装载骨料
  • 在域服务的帮助下执行计算
  • 相应地改变聚合状态

  • 我同意Evans和Vernon不同意Rinat的想法。在本例中,他使用定价服务在LockCustomerForAccountOverdraft方法中找到阈值。这是一个业务规则评估,可以通过发送命令来完成。按照Evan和Verson的方法,应该有一个更高级别的域服务,名为LockCustomer,其中e PricingService和CustomerAggregate的协调可以完成。我认为Evans和Vernon应用SRP,这样就有了更多的组件。Rinat采取了一种简单的方法,这样他就可以在这种情况下为客户打破开放/封闭原则。如果两个服务生活在同一个有界上下文中,聚合根可以通过我相互交谈消息,那么为什么需要注入服务呢?只有当AR需要它来执行某些行为时才需要注入服务。我不认为消息传递在这里会有什么帮助。我可能在这里使用了错误的术语。我指的是域事件。我很理解你,我的答案仍然是正确的same@MikeSW+1.因此,如果我需要一个接口来帮助执行,我会使用它来注入一个服务orm我需要AR做的工作(即ICalculator或IPaymentGateway)。如果服务实际上在聚合中(比如计算器是聚合中的某个类),那么我可以“新建”它(除非它使单元测试更困难)。更好的是,如果我不需要服务的副作用,那么我可以使用无副作用的域事件(我收集AR的域事件,然后应用程序将在事务结束时(使用AR完成时)将它们全部分派)。这是正确的吗?据我所知,PurchaseOrderService是应用程序服务而不是域服务。@wonderfulworld:IINVoicNumber Generator是域服务
    IINVoicNumber Generator
    是域服务,