访问子类聚合成员
我知道我们不应该直接更改聚合根的子项,而是应该通过聚合根上的方法执行它们。 例如 order.SetOrderLineQty(product, qty);
但是如果聚合根的子项是抽象的东西怎么办? 假设您有 Car 聚合根,其中包含 IWheel 列表作为聚合的一部分。您将如何通过其聚合根添加/更改轮子的属性(谁不知道它们可能是什么具体类型的轮子)?
一个更现实的例子是这样的: 医生可以创建一个 MedicalRerport(聚合根),其中包含 IMedicalNote 列表(作为 MedicalReport 聚合的一部分)。 IMedicalNote 是一个基类/接口,它被子类化为几个具体的子类,例如 BloodCheckNote、TemperatureNote、MineralConcentrationNote 等。
每个子类都有不同的属性,并且它们都是可编辑的。 MedicalReport 聚合可能包含这些注释中的一项或多项。 (每个注释子类都有一个特定的用户控件,供用户输入/更新详细信息,在大医疗报告屏幕下显示为面板/选项卡)
我的问题是,如何通过其聚合严格添加/编辑这些注释的属性-根(医疗报告)?由于不允许我直接更改这些注释属性,因此一个丑陋的选择是在聚合根(MedicalReport)上公开所有可能的注释属性,即:
report.SetWhiteBloodCellCount(cellCount);
report.SetBloodCheckComment(comment);
report.SetTemperature(bodyPart, temperature);
report.AddMineral(mineral, concentration);
这些方法中的每一个都将更新(或创建新的)其内部子集合中的注释项。这有两个明显的问题:
- 我们必须预先定义聚合根上所有可能的 IMedicalNote 子类的所有可用属性。这是不可接受的,因为子类的数量肯定会增长,这取决于我们想要捕获的医疗数据的类型,这首先是继承的全部要点。
- 列表中可以存在同一注释类型的多个实例。该 API 将会失败,因为我们不能只说
report.SetBloodCheckComment(comment)
并期望它会更新列表中的 BloodCheckNote 项目,因为我们允许列表中存在多个 BloodCheckNote 项目。
我仍然想通过其聚合根维护与这些注释的所有交互,因为它必须控制整个 MedicalReport 聚合是否可以有效保存、聚合是否不可修改、粗粒度乐观并发检查等。但是我怎样才能做到这一点?
I understand that we should not change children of an aggregate-root directly, but instead they should be performed via methods on the aggregate-root.
E.g. order.SetOrderLineQty(product, qty);
But what if the children of the aggregate-root are something abstract?
Imagine you have Car aggregate root, which contains a list of IWheel as part of the aggregate. How would you add/change the properties of the wheel via its aggregate-root (who knows nothing about what the concrete type of wheel they might be)?
A more real world example is this:
A doctor can create a MedicalRerport (aggregate-root), which contains a list of IMedicalNote (as part of MedicalReport aggregate).
IMedicalNote is a base-class/interface, which is subclassed into a few concrete subclasses, e.g. BloodCheckNote, TemperatureNote, MineralConcentrationNote, etc etc.
Each subclass has different properties, and they're all editable.
A MedicalReport aggregate may contain one or more of any of these notes.
(Each note subclass has a specific user-control for the user to enter/update the details, shown as panels/tabs under the big MedicalReport screen)
My question is, how can I add/edit the properties of these notes strictly via its aggregate-root (MedicalReport)? Since I am not allowed to change these notes properties directly, one ugly option is by exposing all possible note properties on the aggregate root (MedicalReport), i.e.:
report.SetWhiteBloodCellCount(cellCount);
report.SetBloodCheckComment(comment);
report.SetTemperature(bodyPart, temperature);
report.AddMineral(mineral, concentration);
Each of these methods will update (or create new) note items in its internal children collection. There are 2 obvious problems with this:
- We have to define upfront all available properties of all possible IMedicalNote subclasses on the aggregate-root. That's not acceptable as the number of subclasses is guaranteed to grow, depends on the type of medical data we want to capture, which is the whole point of the inheritance in the first-place.
- There can be multiple instances of the same note-type within the list. This API will fail, since we can't just say
report.SetBloodCheckComment(comment)
and expect it will update a BloodCheckNote item in the list, because we allow more than one BloodCheckNote items in the list.
I still want to maintain all interactions to these notes via its aggregate-root, since it has to control whether the whole MedicalReport aggregate is valid to be saved, whether the aggregate is not modifiable, coarse-grained optimistic-concurrency check, etc. But how can I do that?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
想知道您是否误解了有关聚合根的指导(或者也许是我做的......)。
我从未读过指南中所说的:“聚合必须为其所有聚合对象的每个可想象的属性提供代理方法”。相反,我认为它说:“聚合控制其聚合对象的生命周期、身份和关系”。
因此,客户端向聚合请求对其对象之一的(瞬时)引用并对其执行某些操作是完全有效的。我这里没有 DDD 副本来确认措辞,但这似乎与 DDD 摘要电子书 (p53) 内容如下:
因此,在您的情况下,客户会向
MedicalReport
询问IMedicalNote
的实例,获取子类型,对它们进行适当的操作,并在适用的情况下传回根。正如我所说:不能肯定地说这与 DDD 一致,但常识表明,与试图反映聚合根中每个子类型的每个属性/方法相比,这是一个更具可扩展性和灵活性的解决方案。
嗯。
Wonder if you're mis-interpreting the guidance around aggregate roots (Or maybe I did...).
I never read the guidance as saying: "the aggregate must provide proxy methods for every conceivable property of all its aggregated objects". Rather, I think it says: "the aggregate controls the lifecycle, identity and relationships of its aggregated objects".
So it's perfectly valid for a client to ask the aggregate for a (transient) reference to one of its objects and do something with it. I don't have my copy of DDD here to confirm the wording, but that would seem consistent with the DDD summary ebook (p53) which says:
So, in your case clients would ask
MedicalReport
for instance(s) ofIMedicalNote
, get back subtypes, operate on them as appropriate and pass back to the root if applicable.As I say: can't say for sure that's in line with DDD, but common sense says it's a more scalable and flexible solution than trying to reflect every property/method of every subtype in the aggregate root.
hth.