Domain driven design 通过聚合根(DDD)创建嵌套实体

Domain driven design 通过聚合根(DDD)创建嵌套实体,domain-driven-design,entities,aggregateroot,Domain Driven Design,Entities,Aggregateroot,我有一个关于在聚合根中处理子实体的多层嵌套的问题 到目前为止,我只处理没有子元素的聚合根,或者最多一层嵌套实体 在创建和修改子级时,我通过AR ie进行了管理。使用传统的订单/订单行示例: class Order: public void addOrderLine(product, price) public void adjustPriceForOrderLine(percentage_change, line_id) 我刚刚设计了一个AR,它有两个嵌套级别,都是1对多,我很难确定通过AR处

我有一个关于在聚合根中处理子实体的多层嵌套的问题

到目前为止,我只处理没有子元素的聚合根,或者最多一层嵌套实体

在创建和修改子级时,我通过AR ie进行了管理。使用传统的订单/订单行示例:

class Order:
public void addOrderLine(product, price)
public void adjustPriceForOrderLine(percentage_change, line_id)
我刚刚设计了一个AR,它有两个嵌套级别,都是1对多,我很难确定通过AR处理交互的方法:

class Root:
public void addLevelOneChild(...)
public void adjustLevelOneChild(...)
但是当涉及到嵌套在LevelOne子级下的子级时,我所采用的方法变得更加冗长

public void addLevelTwoChildToLevelOneChild(..., levelOneChild_id)
public void adjustLevelTwoChildInLevelOneChild(..., levelOneChild_id)
它起作用了。但在对级别2的子级执行任何操作之前,始终需要努力确定级别1子级的本地标识符

另外,当使用工厂方法创建新的levelOneChild时,我需要返回levelOneChild的本地id,然后创建levelTwoChild,或者跳过一些环来获取新levelOneChild的本地标识

public local_id root.addLevelOneChild(...)
public root.addLevelTwoChildtoLevelOneChild(..., local_id)

这似乎是正确的方法吗?或任何关于更优雅解决方案的建议

我一直在考虑使用自然id我现在使用guid只是为了本地id的一致性,这将有助于减少返回或查询生成的密钥的需要。尽管这让持久性实现的细节泄露了出来


谢谢

我尝试将设计保持在一个级别:包含一个或多个值对象集合的聚合根。这些值对象可以是指向另一个聚合的链接,例如,用DB的说法,订单行是产品聚合的链接

您需要了解域,以了解第一级子对象是否不应自行聚合,或者这些第一级子对象是否提供到关联AR的链接,而关联AR又将包含设计中的第二级值对象

但是,在您的示例中,在抽象/概念层面上,无法判断:


我建议你做一个思想实验,在你的设计中,你不被允许进入一个以上的层次:你将如何改变你的设计?

我试图将设计保持在一个层次:一个包含一个或多个值对象集合的聚合根。这些值对象可以是指向另一个聚合的链接,例如,用DB的说法,订单行是产品聚合的链接

您需要了解域,以了解第一级子对象是否不应自行聚合,或者这些第一级子对象是否提供到关联AR的链接,而关联AR又将包含设计中的第二级值对象

但是,在您的示例中,在抽象/概念层面上,无法判断:


我建议你做一个思想实验,在你的设计中,你不允许进入一个以上的层次:你将如何改变你的设计?

聚合的嵌套应该只由真正的业务不变量来强制,除非是这样的情况,我想让我的聚合很小,只限于它自己的属性和值对象

请记住,强制嵌套聚合可能会对性能和可伸缩性产生负面影响

我完全推荐你阅读沃恩·弗农的三集丛书


祝你快乐

聚合的嵌套应该仅由真正的业务不变量强制执行,除非在这种情况下,我喜欢将聚合保持在较小的范围内,仅限于它自己的属性和值对象

请记住,强制嵌套聚合可能会对性能和可伸缩性产生负面影响

我完全推荐你阅读沃恩·弗农的三集丛书

祝你快乐

首先你应该问问自己,你是否应该拥有这么大的集群AR,并探索如何将其分解。你想保护什么不变量?这些规则最终能否保持一致

如果您必须拥有这个大型集群AR,以下是一些想法:

一,。通用层次结构处理:

请注意,对于异构树,这可能更加困难。此外,如果将一个节点传递给addNode,则封装会稍微中断,因为该实体可能会被调用方修改。您可以传递一个不可变的节点描述符

二,。子项通知对父项的更改:

您可以例外地允许直接与实体交互,但让它们将任何更改通知AR。例如,考虑文档对象模型DOM和事件冒泡

    root.childOfId(childId).addChild(...)

    addChild(...) {
        parent.notifyAddChild(...);

        //add child
    }
三,。层次结构编辑器:

请注意,无论您选择哪种方法,都必须尽可能使API与您的通用语言保持一致。还要注意的是,如果您只有两个深度级别,那么使用您建议的显式API可能比任何API都要好 您首先应该问自己是否应该拥有如此大的集群AR,并探索如何将其分解。你想保护什么不变量?这些规则最终能否保持一致

如果您必须拥有这个大型集群AR,以下是一些想法:

一,。通用层次结构处理:

请注意,对于异构树,这可能更加困难。此外,如果将一个节点传递给addNode,则封装会稍微中断,因为该实体可能会被调用方修改。您可以传递一个不可变的节点描述符

二,。子项通知对父项的更改:

您可以例外地允许直接与实体交互,但让它们将任何更改通知AR。例如,考虑文档对象模型DOM和事件冒泡

    root.childOfId(childId).addChild(...)

    addChild(...) {
        parent.notifyAddChild(...);

        //add child
    }
三,。层次结构编辑器:


请注意,无论您选择哪种方法,都必须尽可能使API与您的通用语言保持一致。另外请注意,如果您只有两个深度级别,那么使用您建议的显式API可能比上述任何一种都要好。

刚刚意识到我最后的想法可能是采取的方法。完全忽略ID意味着一个方法:addLevelTwoChild。。。其中,参数还包括LevelOneChild的本地标识符。AR将在内部处理将LevelTwo子项添加到正确的LevelTwo子项。我刚刚意识到我最后的想法可能是采取的方法。完全忽略ID意味着一个方法:addLevelTwoChild。。。其中,参数还包括LevelOneChild的本地标识符。AR将在内部处理将LevelTwo子级添加到正确的LevelOne子级的操作。
    root.childOfId(childId).addChild(...)

    addChild(...) {
        parent.notifyAddChild(...);

        //add child
    }
editor = root.newHierarchyEditor();

editor
    .firstChild()
    .addChild(...)
    .applyChanges(root); //could call on #1-like methods or root.editHierarchy(mutations)