通过聚合根 (DDD) 创建嵌套实体
Creating nested Entities through an Aggregate Root (DDD)
我有一个关于处理聚合根中 child 实体的多层嵌套的问题。
到目前为止,我只处理没有 children 或最多 "one level" 嵌套实体的聚合根。
创建和修改 child 时,我通过 AR ie 进行了管理。使用传统的 Order / OrderLines 示例:
class Order:
public void addOrderLine(product, price)
public void adjustPriceForOrderLine(percentage_change, line_id)
我刚刚设计了一个具有 2 层嵌套(均为 1-to-many)的 AR,并且很难确定通过 AR 处理交互的方法:
class Root:
public void addLevelOneChild(...)
public void adjustLevelOneChild(...)
但是当谈到使用嵌套在 LevelOne child 下的 child 时,我一直采用的方法变得更加冗长。
public void addLevelTwoChildToLevelOneChild(..., levelOneChild_id)
public void adjustLevelTwoChildInLevelOneChild(..., levelOneChild_id)
有效。但是在对二级 Child 采取任何操作之前,总是需要努力确定一级 child 的本地标识符。
此外,当使用工厂方法创建新的 levelOneChild 时,我需要 return levelOneChild 的本地 ID,然后创建 levelTwoChild, 或跳过一些圈以获取新 levelOneChild.
的本地标识
public local_id root.addLevelOneChild(...)
public root.addLevelTwoChildtoLevelOneChild(..., local_id)
或
public void root.addLevelOneChild(...)
public local_id getIdforLevelOneChild(some natural identifier(s))
public root.addLevelTwoChildtoLevelOneChild(..., local_id)
这看起来是正确的方法吗?或者任何更优雅的解决方案的建议。
我一直在考虑为 local_id 使用自然 ID(我目前使用 guid 只是为了保持一致性),这将有助于减少 return 或查询生成的密钥的需要。尽管这让持久性实现细节泄露了。
谢谢
我试图将设计保持在一个层次上:具有一个或多个值对象集合的聚合根。这些值对象可能是 link 到另一个聚合,例如在 OrderLine
是 associative entity 的情况下,用 DB 的说法,是 Product
聚合。
需要了解域,看看第一级子级是否不应该自己聚合,或者这些第一级子级是否向关联的 AR 提供 link,这反过来会在您的设计中包含二级值对象。
但是,在您的示例中的 abstract/conceptual 级别上,无法判断 :)
我建议做一个思想实验,在你的设计中,你不能下降超过一个层次:你会如何改变你的设计?
聚合的嵌套应该只由真正的业务不变量强制执行,除非是这种情况我喜欢将我的聚合限制在它自己的属性和值对象内。
请记住,聚合的强制嵌套会对性能和可伸缩性产生负面影响。
我强烈推荐您阅读 Vaughn Vernon 的三部分系列文章 Effective aggregate design。
DDD 快乐!
你首先要问问自己,你是否应该拥有这么大的集群AR,并探索分解它。您要保护哪些不变量?这些规则最终能否一致?
如果你一定要有这个大集群AR,这里有几个想法:
1.通用层次结构处理:
addNode(node, parentId?)
removeNode(childId, parentId?)
请注意,对于异构树,这可能会更加困难。此外,如果将 Node
传递给 addNode
,封装会稍微中断,因为实体可能会被调用者修改。您可以改为传递不可变节点描述符。
2。 Children 通知更改 parent:
您可以例外地允许直接与实体交互,但让它们将任何更改通知 AR。例如,考虑文档 Object 模型 (DOM) 和事件冒泡。
root.childOfId(childId).addChild(...)
addChild(...) {
parent.notifyAddChild(...);
//add child
}
3。层级编辑器:
editor = root.newHierarchyEditor();
editor
.firstChild()
.addChild(...)
.applyChanges(root); //could call on #1-like methods or root.editHierarchy(mutations)
请注意,无论您选择哪种方法,您都必须尽可能地努力使 API 与您的通用语言保持一致。另请注意,如果您只有 2 个深度级别,那么像您建议的那样进行明确的 API 可能比上述任何一个都要好。
我有一个关于处理聚合根中 child 实体的多层嵌套的问题。
到目前为止,我只处理没有 children 或最多 "one level" 嵌套实体的聚合根。
创建和修改 child 时,我通过 AR ie 进行了管理。使用传统的 Order / OrderLines 示例:
class Order:
public void addOrderLine(product, price)
public void adjustPriceForOrderLine(percentage_change, line_id)
我刚刚设计了一个具有 2 层嵌套(均为 1-to-many)的 AR,并且很难确定通过 AR 处理交互的方法:
class Root:
public void addLevelOneChild(...)
public void adjustLevelOneChild(...)
但是当谈到使用嵌套在 LevelOne child 下的 child 时,我一直采用的方法变得更加冗长。
public void addLevelTwoChildToLevelOneChild(..., levelOneChild_id)
public void adjustLevelTwoChildInLevelOneChild(..., levelOneChild_id)
有效。但是在对二级 Child 采取任何操作之前,总是需要努力确定一级 child 的本地标识符。
此外,当使用工厂方法创建新的 levelOneChild 时,我需要 return levelOneChild 的本地 ID,然后创建 levelTwoChild, 或跳过一些圈以获取新 levelOneChild.
的本地标识public local_id root.addLevelOneChild(...)
public root.addLevelTwoChildtoLevelOneChild(..., local_id)
或
public void root.addLevelOneChild(...)
public local_id getIdforLevelOneChild(some natural identifier(s))
public root.addLevelTwoChildtoLevelOneChild(..., local_id)
这看起来是正确的方法吗?或者任何更优雅的解决方案的建议。
我一直在考虑为 local_id 使用自然 ID(我目前使用 guid 只是为了保持一致性),这将有助于减少 return 或查询生成的密钥的需要。尽管这让持久性实现细节泄露了。
谢谢
我试图将设计保持在一个层次上:具有一个或多个值对象集合的聚合根。这些值对象可能是 link 到另一个聚合,例如在 OrderLine
是 associative entity 的情况下,用 DB 的说法,是 Product
聚合。
需要了解域,看看第一级子级是否不应该自己聚合,或者这些第一级子级是否向关联的 AR 提供 link,这反过来会在您的设计中包含二级值对象。
但是,在您的示例中的 abstract/conceptual 级别上,无法判断 :)
我建议做一个思想实验,在你的设计中,你不能下降超过一个层次:你会如何改变你的设计?
聚合的嵌套应该只由真正的业务不变量强制执行,除非是这种情况我喜欢将我的聚合限制在它自己的属性和值对象内。
请记住,聚合的强制嵌套会对性能和可伸缩性产生负面影响。
我强烈推荐您阅读 Vaughn Vernon 的三部分系列文章 Effective aggregate design。
DDD 快乐!
你首先要问问自己,你是否应该拥有这么大的集群AR,并探索分解它。您要保护哪些不变量?这些规则最终能否一致?
如果你一定要有这个大集群AR,这里有几个想法:
1.通用层次结构处理:
addNode(node, parentId?)
removeNode(childId, parentId?)
请注意,对于异构树,这可能会更加困难。此外,如果将 Node
传递给 addNode
,封装会稍微中断,因为实体可能会被调用者修改。您可以改为传递不可变节点描述符。
2。 Children 通知更改 parent:
您可以例外地允许直接与实体交互,但让它们将任何更改通知 AR。例如,考虑文档 Object 模型 (DOM) 和事件冒泡。
root.childOfId(childId).addChild(...)
addChild(...) {
parent.notifyAddChild(...);
//add child
}
3。层级编辑器:
editor = root.newHierarchyEditor();
editor
.firstChild()
.addChild(...)
.applyChanges(root); //could call on #1-like methods or root.editHierarchy(mutations)
请注意,无论您选择哪种方法,您都必须尽可能地努力使 API 与您的通用语言保持一致。另请注意,如果您只有 2 个深度级别,那么像您建议的那样进行明确的 API 可能比上述任何一个都要好。