通过聚合根 (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 到另一个聚合,例如在 OrderLineassociative 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 可能比上述任何一个都要好。