8

DDD 中众所周知的建议是聚合根不使用域服务。域服务是协调两个聚合根来实现一个行为。

当我看到 Rinat Abdullin 写的标题为Building Blocks Of CQRS的博客时,我真的很惊讶。在域服务部分下,您将看到域服务被注入到聚合根。

聚合根可以接受域服务吗?

4

4 回答 4

11

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

  1. 加载聚合
  2. 在域服务的帮助下执行计算
  3. 相应地改变聚合状态。
于 2015-02-04T07:57:47.240 回答
10

在某种程度上是的。如果 AR 确实需要服务来完成它的某些工作,那么您可以将其作为方法参数注入。如果 AR 的大部分行为都需要服务,那么它的建模可能不正确。

于 2013-06-17T08:30:55.627 回答
5

我觉得下面的解释很好。它基于 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();
        }
    }
}
于 2014-03-12T06:46:40.960 回答
3

将任何东西注入域对象是非常困难的,并且这样做是非常特定于技术的。在java中,它需要在编译时将方面编织到您的域类中。尽管我可能会误会,但我认为大多数 DDD 领导者认为这通常是一个坏主意。埃文斯弗农都积极劝阻,我喜欢听他们的话。有关完整的解释,请阅读弗农。

于 2013-06-08T04:29:05.860 回答