【问题标题】:Accessing Subclassed Aggregate Members访问子类聚合成员
【发布时间】:2011-05-03 10:27:09
【问题描述】:

我知道我们不应该直接更改聚合根的子节点,而应该通过聚合根上的方法来执行它们。 例如。 order.SetOrderLineQty(product, qty);

但是如果聚合根的子节点是抽象的呢? 假设您有 Car 聚合根,其中包含 IWheel 列表作为聚合的一部分。您将如何通过其聚合根添加/更改轮子的属性(谁对它们可能是什么具体类型的轮子一无所知)?

一个更真实的例子是这样的: 医生可以创建一个 MedicalRerport(聚合根),其中包含 IMedicalNote 列表(作为 MedicalReport 聚合的一部分)。 IMedicalNote 是一个基类/接口,它被子类化为几个具体的子类,例如BloodCheckNote、TemperatureNote、MineralConcentrationNote等。

每个子类都有不同的属性,它们都是可编辑的。 MedicalReport 聚合可能包含一个或多个这些注释中的任何一个。 (每个笔记子类都有一个特定的用户控件供用户输入/更新详细信息,在 MedicalReport 大屏幕下显示为面板/选项卡)

我的问题是,如何严格通过其聚合根 (MedicalReport) 添加/编辑这些注释的属性?由于我不允许直接更改这些注释属性,一个丑陋的选择是在聚合根 (MedicalReport) 上公开所有可能的注释属性,即:

report.SetWhiteBloodCellCount(cellCount);
report.SetBloodCheckComment(comment);
report.SetTemperature(bodyPart, temperature);
report.AddMineral(mineral, concentration);

这些方法中的每一个都将更新(或创建新)其内部子集合中的笔记项。这有两个明显的问题:

  1. 我们必须预先定义聚合根上所有可能的 IMedicalNote 子类的所有可用属性。这是不可接受的,因为子类的数量肯定会增长,这取决于我们想要捕获的医疗数据类型,而这首先是继承的重点。
  2. 列表中可以有多个相同笔记类型的实例。这个 API 会失败,因为我们不能只说 report.SetBloodCheckComment(comment) 并期望它会更新列表中的 BloodCheckNote 项目,因为我们允许列表中有多个 BloodCheckNote 项目。

我仍然希望通过它的聚合根来维护与这些注释的所有交互,因为它必须控制整个 MedicalReport 聚合是否有效以保存,聚合是否不可修改,粗粒度的乐观并发检查,等等。但是我该怎么做呢?

【问题讨论】:

    标签: domain-driven-design aggregateroot


    【解决方案1】:

    想知道您是否误解了有关聚合根的指南(或者我可能是...)。

    我从来没有读过这样的指导:“聚合必须为其所有聚合对象的每个可能的属性提供代理方法”。相反,我认为它说:“聚合控制其聚合对象的生命周期、身份和关系”。

    因此,客户端向聚合请求对其其中一个对象的(瞬态)引用并对其进行处理是完全有效的。我没有我的 DDD 副本来确认措辞,但这似乎与 DDD summary ebook (p53) 一致,它说:

    根可以传递内部的瞬态引用 反对外部的,条件是外部的 操作完成后对象不持有引用。

    因此,在您的情况下,客户会询问 MedicalReportIMedicalNote 的实例,取回子类型,对它们进行适当的操作,并在适用时传回根目录。

    正如我所说:不能肯定这与 DDD 一致,但常识表明它比试图在聚合根中反映每个子类型的每个属性/方法更具可扩展性和灵活性。

    第一次。

    【讨论】:

    • 我的理解是您可以检索内部对象(IMedicalNote),但仅用于只读目的。您不应该对其进行任何修改。所有修改都必须通过根进行,以便它可以控制完整性。另外,将它传递回根是什么意思?一旦我们在外部修改了孩子的属性,就是这样,它被改变了(通过引用),绕过了根,不是吗? “回根”到底是做什么的?
    • 抱歉,回答有点模棱两可。根传递回对象的副本,然后客户端更新并传递回根。然后根负责检查不变量和建立关系。提供合理的责任划分:root 知道聚合对象之间的关系和约束,但不需要知道所有子类型的详细信息。客户端可以根据需要对特定的子类型进行操作。注意 Root 可能需要知道一些子类型的详细信息(例如,如果它必须只包含一个 BloodCheckNote 但可能有多个 TemperatureNotes)。
    • 您的意思是创建一个克隆机制,以便在通过 root 的公共 getter 访问时,root 将始终传递回每个子注释的克隆?然后,一旦客户端将这些子项返回到根目录,就可以将这些值复制回其实际对象。只是想澄清一下这是否是您的意思,因为这听起来需要做很多工作。干杯
    • 请注意。对于 Value 对象,返回一个克隆。无论如何,客户都必须创建另一个(不变性)。对于实体,您可以将引用传递给客户端。然后,您需要决定何时完成验证:在实体方法中或在传回时汇总。这就是我想要达到的目的:将特定于实体的验证放在实体中,与聚合中的关系有关。即使添加新的子类型,聚合界面也能保持相对稳定。
    • 如果客户端直接修改子实体引用,然后将其传递回 AR,这有点没有意义,因为(由客户端)直接在实际子实体上进行了更改,不是吗?考虑如果您通过 AR 更改订单项目数量,AR 可以决定是否允许数量(例如,基于最大订单量)。但是,如果您让客户端直接在 OrderItem 对象上更改数量,并将其传递回 AR,那么实际的订单项 已经 已经更改,此时,AR 无能为力关于它。你能扩大你的看法吗?谢谢
    猜你喜欢
    • 2018-03-19
    • 1970-01-01
    • 2018-10-05
    • 1970-01-01
    • 2012-10-08
    • 1970-01-01
    • 2011-11-03
    • 1970-01-01
    • 2014-07-13
    相关资源
    最近更新 更多