带孙子的 .NET DDD 域模式
.NET DDD Domain Pattern with Grandchildren
我想在我的 .NET Core 域模型中实现 DDD 模式。
以下是具有聚合子实体和聚合孙实体的聚合根示例:
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);
}
}
和聚合孙子:
public class CatalogItem
: Entity
{
private string _name;
protected CatalogItem() { }
public CatalogItem(string name)
{
_name = name;
}
}
这是完成 DDD 模式的正确方法吗?
或者这是否违反了我不知道的 DDD 规则?
它本身并没有违反任何 DDD 规则 - 但它是一种经常被劝阻的方法,原因如下:
- DDD 鼓励您根据系统需要支持的操作、流程和规则对域进行建模,同时管理状态的更改。
- 您建模的关系可能存在于现实世界中 - 它们可能有助于可视化数据 - 但它们对于您的模型真的是必需的吗?
- DDD 中的良好做法是设计小型 聚合。聚合实际上是一个 'consistency' 边界——你把你的一致性边界设置得越大,你就会有一些后果:
- 大多数操作不涉及所有数据 - 但所有数据在每个操作期间都必须事务锁定
- 在协作的多用户域中,这可能导致并发问题,其中任一用户由于他们想要操作的数据被不必要地锁定而被阻止
- 为什么 可能 这种关系对建模有用?
- 如果您有一个规则(不变的)必须在所有子聚合中具有事务一致的数据,例如:
- 每个供应商必须最多有 4 个目录 - 要执行此规则,您必须在供应商中具有交易一致的目录列表
- 目录中目录商品的价格总和不得超过 $X - 同样,您需要所有商品以交易一致的方式
备选方案
那么 - 如果您没有任何此类规则,并且您已被说服创建 'small' 聚合,您可以做什么?
- 每个实体都成为它自己的集合体
- Link 个实体,来自 reference。在这种情况下:
- 供应商不会有目录列表
- 目录会有一个'SupplierId'到link的目录给供应商,方便供应商获取目录列表
- Catalog 不会 有 CatalogItem
的列表
- CatalogItem 将有一个 'CatalogId' 到 link 到目录 - 同样,有助于按目录获得项目列表
这种方法的含义是您将不会在聚合之间具有事务一致性 - 但除非您的规则要求它,否则您不需要它,那么为什么要强制执行它?
您真的需要事务一致性吗?
DDD 鼓励的另一条建议是挑战对事务一致性的需求——这真的有必要吗?
使用上面的简单示例 - 如果供应商最终有 5 个目录会怎样?世界会崩溃吗?即使您确实需要该规则,它是否需要在事务上保持一致?使用较小的聚合,您可以:
- 如果供应商已经有 4 个目录,则包括客户端验证以防止操作
- 创建目录时发布事件,触发检查和补偿操作 - 如果处理程序确定供应商现在有 5 个目录,则发出警报或更新供应商状态,直到用户删除目录,或自动使最旧的目录过期 - 在您的域中最有意义的任何内容
总结
建议是:
- 设计小骨料
- 拥抱最终一致性
我想在我的 .NET Core 域模型中实现 DDD 模式。 以下是具有聚合子实体和聚合孙实体的聚合根示例:
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);
}
}
和聚合孙子:
public class CatalogItem
: Entity
{
private string _name;
protected CatalogItem() { }
public CatalogItem(string name)
{
_name = name;
}
}
这是完成 DDD 模式的正确方法吗?
或者这是否违反了我不知道的 DDD 规则?
它本身并没有违反任何 DDD 规则 - 但它是一种经常被劝阻的方法,原因如下:
- DDD 鼓励您根据系统需要支持的操作、流程和规则对域进行建模,同时管理状态的更改。
- 您建模的关系可能存在于现实世界中 - 它们可能有助于可视化数据 - 但它们对于您的模型真的是必需的吗?
- DDD 中的良好做法是设计小型 聚合。聚合实际上是一个 'consistency' 边界——你把你的一致性边界设置得越大,你就会有一些后果:
- 大多数操作不涉及所有数据 - 但所有数据在每个操作期间都必须事务锁定
- 在协作的多用户域中,这可能导致并发问题,其中任一用户由于他们想要操作的数据被不必要地锁定而被阻止
- 为什么 可能 这种关系对建模有用?
- 如果您有一个规则(不变的)必须在所有子聚合中具有事务一致的数据,例如:
- 每个供应商必须最多有 4 个目录 - 要执行此规则,您必须在供应商中具有交易一致的目录列表
- 目录中目录商品的价格总和不得超过 $X - 同样,您需要所有商品以交易一致的方式
- 如果您有一个规则(不变的)必须在所有子聚合中具有事务一致的数据,例如:
备选方案
那么 - 如果您没有任何此类规则,并且您已被说服创建 'small' 聚合,您可以做什么?
- 每个实体都成为它自己的集合体
- Link 个实体,来自 reference。在这种情况下:
- 供应商不会有目录列表
- 目录会有一个'SupplierId'到link的目录给供应商,方便供应商获取目录列表
- Catalog 不会 有 CatalogItem 的列表
- CatalogItem 将有一个 'CatalogId' 到 link 到目录 - 同样,有助于按目录获得项目列表
这种方法的含义是您将不会在聚合之间具有事务一致性 - 但除非您的规则要求它,否则您不需要它,那么为什么要强制执行它?
您真的需要事务一致性吗?
DDD 鼓励的另一条建议是挑战对事务一致性的需求——这真的有必要吗?
使用上面的简单示例 - 如果供应商最终有 5 个目录会怎样?世界会崩溃吗?即使您确实需要该规则,它是否需要在事务上保持一致?使用较小的聚合,您可以:
- 如果供应商已经有 4 个目录,则包括客户端验证以防止操作
- 创建目录时发布事件,触发检查和补偿操作 - 如果处理程序确定供应商现在有 5 个目录,则发出警报或更新供应商状态,直到用户删除目录,或自动使最旧的目录过期 - 在您的域中最有意义的任何内容
总结
建议是:
- 设计小骨料
- 拥抱最终一致性