Accessing Subclassed Aggregate Members
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?
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:
It is possible for the root to pass transient references of internal objects to external ones, with the condition that the external objects do not hold the reference after the operation is finished.
So, in your case clients would ask MedicalReport
for instance(s) of IMedicalNote
, 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.
精彩评论