5

我知道我们不应该直接更改聚合根的子节点,而应该通过聚合根上的方法来执行它们。例如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 聚合是否有效以保存、聚合是否不可修改、粗粒度的乐观并发检查等。但是我怎样才能做到这一点?

4

1 回答 1

4

想知道您是否误解了关于聚合根的指导(或者也许我做了......)。

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

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

根可以将内部对象的临时引用传递给外部对象,条件是外部对象在操作完成后不持有引用。

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

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

hth。

于 2010-11-02T08:41:54.273 回答