我有一个Invoice聚合根,在某些时候可以发送到会计外部Web服务,并通过持久保存从该服务获得的一些ID /号码来标记为发送。
在DDD中执行此操作的正确方法是什么?
以下是我的想法:
首先:
拥有带功能SendToAccounting
的发票AggregateRoot,并注入域服务/接口,它将发票发送到会计,并检索一些" id / code"在会计软件中,并设置AccountingSoftwareId
属性
Invoice.SendToAccounting(IInvoiceDomain service)
{
var accountingSoftwareID = service.getAccountingSoftwareId(this);
this.AccountingSoftwareId = accountingSoftwareId;
}
///Implementation in the application service
var invoice = _invoiceRepository.GetInvoiceById(id);
invoice.SendToAccounting(someDomainService);
_invoiceRepository.Update(invoice);
_unitOfWork.Save();
第二种方法:
与第一种方法类似,但域名服务应负责持久保存:
var invoice = _invoiceRepository.GetInvoiceById(id);
///unit of work save will be called inside this function
invoice.SendToAccounting(someDomainService);
第三个方法:
域服务将完全重新负责封装此行为
///Code inside domain service
public void SendInvoiceToAccounting(int invoiceId)
{
var invoice = _invoiceRepository.GetInvoiceById(invoiceId);
string invoiceAccountingId = _accountingService.GetAccountingSoftwareId(invoice);
invoice.SetAsSentToAccounting(invoiceAccountingId);
_invoiceRepository.Update(invoice);
_unitOfWork.Save();
}
答案 0 :(得分:1)
在DDD中执行此操作的正确方法是什么?
你的第一种方法是最接近的。域服务上的签名应该接受状态作为参数,而不是聚合根本身。
Invoice.SendToAccounting(IInvoiceDomain service)
{
var accountingSoftwareID = service.getAccountingSoftwareId(this.Id, ...);
this.AccountingSoftwareId = accountingSoftwareId;
}
传递的所有参数都应该是值类型 - 域服务不应该通过操纵其参数副本来更改聚合的状态,并且它当然不应该能够在其上运行其他命令聚合
在代码审查中,我会拒绝您提供的第二种方法;从域模型的角度来看,域服务接口应该只提供查询,而不是命令(在CQS意义上)。
在代码审查中,我会完全拒绝第三种方法 - 聚合的设置者是代码气味;重点是用更新规则封装状态。
设计有点令人担忧,因为您在同一事务中的两个不同位置进行写入。在快乐的道路上,这不是什么大问题,但是如果在会计服务上运行的命令成功,你应该做什么,但更新发票的保存失败了?
假设分布式交易没有吸引力,您可能想要查看Udi Dahan对reliable messaging的评论。
答案 1 :(得分:0)
我的第一个想法是“不是会计部分的发票?” :)
选项1是我过去倾向于使用的域名对象具有行为的内容。
我不喜欢选项2,因为Invoice需要对存储库的私有引用。
更普遍的观察是,这里的域中似乎没有太多行为 - 它似乎只是设置了一个id。选项3似乎捕获了这一点。我想知道一个应用程序服务是否足够,只是协调以下
上面几乎是选项3。我很想传递存储库和服务,但这确实只是一个更实用的风格 - 上面的内容也适用于私有字段。
答案 2 :(得分:0)
对于给定的invoiceId,会计BC应始终返回相同的accountingSoftwareId。
如果在第一轮中,呼叫是在会计BC上进行的,但发票更新失败,则帐户BC中的状态为t1,发票BC中的状态为t0。当您重试该命令时,它将执行相同的调用并返回相同的ID,如果更新成功,则您在每个BC中处于t1状态。在最坏的情况下,即使必须手动解析命令,对于给定的发票ID,结果帐户ID也始终相同。
因此,要解决特定发票的会计ID,您可以直接询问会计BC。