将域服务注入DDD中的AggregateRoots

won*_*rld 7 domain-driven-design domainservices

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

当我看到由Rinat Abdullin撰写的题为Building Blocks of CQRS的博客时,我真的很惊讶.在"域服务"部分下,您将读到域服务注入聚合根.

Aggregate Root可以接受域服务吗?

Rin*_*lin 9

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

  1. 加载聚合
  2. 在域服务的帮助下执行计算
  3. 相应地改变聚合状态.

  • Rinat,难道你不想在你的这些旧文章上放置横幅,这会重复你在答案中所写的内容吗?人们经常会在你的博客中提到想法和最佳实践,但你肯定知道一些描述的做法并不是最好的......没有冒犯,只是一个建议. (4认同)

Mik*_*eSW 8

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

  • 仅在AR需要它执行某些行为时才需要它.我不知道消息传递在这方面会有什么帮助. (2认同)

Git*_*ter 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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @wonderfulworld:IInvoiceNumberGenerator是域服务 (3认同)