【问题标题】:DDD - child entity encapsulationDDD - 子实体封装
【发布时间】:2014-02-20 20:26:26
【问题描述】:

给定以下示例聚合:

public class Order
{
    private readonly IList<OrderLine> _orderLines;
    public IEnumerable<OrderLine> Lines { get { return _orderLines; } }

    public Order()
    {
       _orderLines = new List<OrderLine>();
    }

    public void AddOrderLine(string sku, int qty)
    {
        _orderLines.Add(new OrderLine(sku, qty));
    }

    public void CancelOrderLine(string sku)
    {
         OrderLine line = _orderLines.FirstOrDefault(l => l.Sku == sku);
         if (line == null)
            return;

         line.Cancel();
    }
}

public class OrderLine
{
    public string Sku { get; private set; }
    public int Qty { get; private set; }

    public OrderLine(string sku, int qty)
    {
       Sku = sku;
       Qty = qty;
    }

    public void Cancel()
    {
        // do some cancellation stuff
    }
}

什么是防止有人绕过聚合根并直接修改 OrderLine?例如:

foreach(OrderLine line in currentOrder.Lines)
{
   line.Cancel();
}

到底有没有真正封装的聚合根?唯一的解决方案是创建一组并行的订单行值对象来代替吗?

感谢您的任何见解。

【问题讨论】:

    标签: c# domain-driven-design aggregate encapsulation


    【解决方案1】:

    您不能先将 Cancel 方法设置为内部方法,以便它仅在您的 Order 程序集中可见吗? 当然,这个程序集中的其他类仍然可以访问取消方法。

    也许另一种方法是将 OrderLines 集合公开为 IOrderLine 接口的集合,并以这种方式隐藏 Cancel 方法。

    【讨论】:

    • 是的,我考虑过在内部制作方法以减少曝光,并且可能会这样做。也考虑了接口路由,但有人可以将结果转换回 OrderLine 对象并直接调用取消方法。我知道这近乎偏执,但我想我想知道是否有一种“正确”的方法可以提供真正的封装,还是我必须在这里妥协?如果我没有得到更好的答案,我会接受你的。感谢您的建议。
    【解决方案2】:

    为什么要公开 Lines?公开一些快照 (DTO),其中包含您在外部需要的 OrderLines 信息,而不是 OrderLines 本身。这是已知的解决方案。而使用 CQRS,您可能根本不需要公开它。

    【讨论】:

    • 据我所知,DTO 在域本身中没有位置。为我的所有子实体创建快照对象也会非常乏味。在旁注中,我发现返回列表是这样的: public IEnumerable Lines { get { return _orderLines.AsEnumerable(); } } 解决了这个问题。修改返回的行不会影响聚合中存储的行。似乎是复制了一份。
    猜你喜欢
    • 1970-01-01
    • 2010-11-11
    • 1970-01-01
    • 2010-11-11
    • 2010-11-23
    • 1970-01-01
    • 1970-01-01
    • 2021-10-31
    • 1970-01-01
    相关资源
    最近更新 更多