将域服务注入DDD中的AggregateRoots

时间:2013-06-08 03:16:44

标签: domain-driven-design domainservices

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

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

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

4 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:8)

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

  1. 加载聚合
  2. 借助域名服务执行计算
  3. 相应地改变聚合状态。

答案 2 :(得分:4)

我发现following explanation相当不错。它基于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();
        }
    }
}

答案 3 :(得分:3)

很难任何内容注入域对象,并且这样做非常注重技术。在java中,它需要将方面编译时间编织到域类中。虽然我可能会误解这一点,但我认为大多数DDD领导人认为这通常是一个坏主意。 EvansVernon都会积极地阻止它,我喜欢听它们。有关完整说明,请阅读Vernon。