DDD 中众所周知的建议是聚合根不使用域服务。域服务是协调两个聚合根来实现一个行为。
当我看到 Rinat Abdullin 写的标题为Building Blocks Of CQRS的博客时,我真的很惊讶。在域服务部分下,您将看到域服务被注入到聚合根。
聚合根可以接受域服务吗?
DDD 中众所周知的建议是聚合根不使用域服务。域服务是协调两个聚合根来实现一个行为。
当我看到 Rinat Abdullin 写的标题为Building Blocks Of CQRS的博客时,我真的很惊讶。在域服务部分下,您将看到域服务被注入到聚合根。
聚合根可以接受域服务吗?
请忽略那篇文章。这是很久以前写的,完全是错误的。如果使用AggregateRoot和DomainService模式实现模块,我建议使用更高的逻辑(例如请求处理程序),它负责:
在某种程度上是的。如果 AR 确实需要服务来完成它的某些工作,那么您可以将其作为方法参数注入。如果 AR 的大部分行为都需要服务,那么它的建模可能不正确。
我觉得下面的解释很好。它基于 Vaughn Vernon 的书,并通过实际需要该服务的方法调用将域服务“注入”到域模型中。
public class PurchaseOrder
{
public string Id { get; private set; }
public string VendorId { get; private set; }
public string PONumber { get; private set; }
public string Description { get; private set; }
public decimal Total { get; private set; }
public DateTime SubmissionDate { get; private set; }
public ICollection<Invoice> Invoices { get; private set; }
public decimal InvoiceTotal
{
get { return this.Invoices.Select(x => x.Amount).Sum(); }
}
public bool IsFullyInvoiced
{
get { return this.Total <= this.InvoiceTotal; }
}
bool ContainsInvoice(string vendorInvoiceNumber)
{
return this.Invoices.Any(x => x.VendorInvoiceNumber.Equals(
vendorInvoiceNumber, StringComparison.OrdinalIgnoreCase));
}
public Invoice Invoice(IInvoiceNumberGenerator generator,
string vendorInvoiceNumber, DateTime date, decimal amount)
{
// These guards maintain business integrity of the PO.
if (this.IsFullyInvoiced)
throw new Exception("The PO is fully invoiced.");
if (ContainsInvoice(vendorInvoiceNumber))
throw new Exception("Duplicate invoice!");
var invoiceNumber = generator.GenerateInvoiceNumber(
this.VendorId, vendorInvoiceNumber, date);
var invoice = new Invoice(invoiceNumber, vendorInvoiceNumber, date, amount);
this.Invoices.Add(invoice);
DomainEvents.Raise(new PurchaseOrderInvoicedEvent(this.Id, invoice.InvoiceNumber));
return invoice;
}
}
public class PurchaseOrderService
{
public PurchaseOrderService(IPurchaseOrderRepository repository,
IInvoiceNumberGenerator invoiceNumberGenerator)
{
this.repository = repository;
this.invoiceNumberGenerator = invoiceNumberGenerator;
}
readonly IPurchaseOrderRepository repository;
readonly IInvoiceNumberGenerator invoiceNumberGenerator;
public void Invoice(string purchaseOrderId,
string vendorInvoiceNumber, DateTime date, decimal amount)
{
// Transaction management, along with committing the unit of work
// can be moved to ambient infrastructure.
using (var ts = new TransactionScope())
{
var purchaseOrder = this.repository.Get(purchaseOrderId);
if (purchaseOrder == null)
throw new Exception("PO not found!");
purchaseOrder.Invoice(this.invoiceNumberGenerator,
vendorInvoiceNumber, date, amount);
this.repository.Commit();
ts.Complete();
}
}
}