与孙子孙女的.NET DDD域模式

时间:2018-02-11 17:34:33

标签: c# .net .net-core domain-driven-design domain-model

我想在我的.NET核心域模型中实现DDD模式。 以下是具有聚合子实体的聚合根以及聚合孙实体的示例:

public List<String> generateParenthesis(int n) {
   List<String> result = new LinkedList<String>();
   Generate("", 0, 0, n, result);
   return result;
}

private void Generate(String s, int l, int r, int n, List<String> result){
   if(l == n && r == n){
       result.add(s);
       return;
   }

   if(l<n){
       Generate(s+"(", l+1, r, n, result);    
   }

   if(r < l)
       Generate(s+")", l , r+1, n, result);
 }}

这里是Aggregate Child的代码:

public class Supplier
    : Entity, IAggregateRoot
{
    private string _name;

    private readonly List<Catalog> _catalogs;
    public IReadOnlyCollection<Catalog> Catalogs => _catalogs;

    protected Supplier() { _catalogs = new List<Catalog>(); }

    public Supplier(string name)
    {
        _catalogs = new List<Catalog>();
        _name = name;
    }

    public void AddCatalog(string name)
    {
        var catalog = new Catalog(name);
        _catalogs.Add(catalog);
    }

    public void AddCatalogItem(int catalogId, string name)
    {
        var catalogItem = new CatalogItem(name);
        _catalogs.Single(c => c.Id == catalogId).AddCatalogItem(catalogItem);
    }
}

聚合孙子:

public class Catalog
    : Entity
{
    private string _name;

    private readonly List<CatalogItem> _catalogItems;
    public IReadOnlyCollection<CatalogItem> CatalogItems => _catalogItems;

    protected Catalog() { _catalogItems = new List<CatalogItem>(); }

    public Catalog(string name)
    {
        _catalogItems = new List<CatalogItem>();
        _name = name;
    }

    public void AddCatalogItem(CatalogItem catalogItem)
    {
        _catalogItems.Add(catalogItem);
    }
}

这是完成DDD模式的正确方法吗? 或者这会破坏我不知道的DDD规则吗?

1 个答案:

答案 0 :(得分:4)

它本身并没有破坏任何DDD 规则 - 但这种方法通常是不鼓励的,原因如下:

  • DDD鼓励您根据操作,流程和范围对域进行建模。管理更改到状态时系统需要支持的规则。
    • 您建模的关系可能存在于现实世界中 - 它们可能有助于可视化数据 - 但它们对您的模型是否真的有必要?
  • DDD的良好做法是设计小型聚合。汇总是一种真正的一致性&#39;边界 - 你的一致性边界越大,你就会产生一些后果:
    • 大多数操作与所有数据无关 - 但所有数据必须在每次操作期间以事务方式锁定
    • 在协作式多用户域中,这可能会导致并发问题,即由于他们想要操作的数据被不必要地锁定而导致用户被阻止
  • 为什么可能这种关系对模型有用?
    • 如果您有一个规则(不变量),必须在所有子聚合中具有事务一致的数据,例如:
      • 必须在每个供应商的MOST 4目录中 - 为了执行此规则,您必须在供应商中具有交易一致的目录列表
      • 目录中目录项目的价格总和不得超过$ X - 再次,您需要以事务一致的方式处理所有项目

替代

所以 - 如果你没有任何这样的规则,并且你已经被说服创造了小小的&#39;聚合,你能做什么?

  • 每个实体都成为它自己的聚合
  • 通过引用链接实体。在这种情况下:
    • 供应商有目录列表
    • 目录中有一个&#39; SupplierId&#39;将目录链接到供应商,并便于按供应商获取目录清单
    • 目录有一个CatalogItem列表
    • CatalogItem会有一个&#39; CatalogId&#39;链接到目录 - 再次,便于按目录获取项目列表

这种方法的含义是,在整个聚合中具有事务一致性 - 但除非您的规则要求它,否则您不需要它,那么为什么要强制执行呢?

您真的需要事务一致性吗?

DDD鼓励的另一条建议是挑战对交易一致性的要求 - 它真的有必要吗?

使用上面的简单示例 - 如果供应商最终获得了5个目录,会发生什么?世界会破裂吗?即使您确实需要该规则,它是否需要在事务上保持一致?使用较小的聚合,您可以:

  • 如果供应商已有4个目录,则包括客户端验证以防止操作
  • 在创建目录时发布事件,触发检查和补偿操作 - 如果处理程序确定供应商现在有5个目录,发出警报或更新供应商状态,直到用户删除目录,或者自动过期最旧的目录 - 在您的域中最有意义的

摘要

建议如下:

  • 设计小聚合
  • 拥抱最终的一致性