2010-11-01 4 views
5

Я понимаю, что мы не должны напрямую изменять дочерние элементы агрегатного корня, но вместо этого они должны выполняться с помощью методов в агрегированном корне. . order.SetOrderLineQty(product, qty);Доступ к субъектам с подклассами

Но что, если дети совокупного корня являются чем-то абстрактным? Представьте, что у вас есть корень агрегата автомобиля, который содержит список IWheel как часть совокупности. Как бы вы добавили/изменили свойства колеса через его совокупный корень (который ничего не знает о том, каков конкретный тип колеса, которым они могли бы быть)?

Более реальный пример мира: Врач может создать MedicalRerport (aggregate-root), который содержит список 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, будь то агрегат не изменяемый, крупнозернистый optimistic- проверка параллелизма и т. д. Но как я могу это сделать?

ответ

4

Удивительно, если вы неправильно интерпретируете руководство по совокупным корням (или, может быть, я это сделал ...).

Я никогда не читал руководство, говоря: «агрегат должен предоставлять прокси-методы для каждого мыслимого свойства всех его агрегированных объектов». Скорее, я думаю, что он говорит: «совокупность управляет жизненным циклом, идентичностью и отношениями его агрегированных объектов».

Таким образом, клиент может запросить совокупность для (переходной) ссылки на один из своих объектов и что-то сделать с ней.Я не моя копии DDD здесь, чтобы подтвердить формулировку, но это, казалось бы, в соответствии с DDD summary ebook (р53), который говорит:

Вполне возможно корень передать временные ссылки внутренних объектов в внешние, при условии, что внешние объекты не будут содержать ссылку после завершения операции.

Таким образом, в вашем случае клиенты будут просить MedicalReport к примеру (ы) IMedicalNote, вернись подтипы, действуют на них в случае необходимости и передать обратно в корень, если это применимо.

Как я уже сказал: не могу точно сказать, что это соответствует DDD, но здравый смысл говорит, что это более масштабируемое и гибкое решение, чем попытка отразить каждое свойство/метод каждого подтипа в сводном корне.

hth.

+0

Я понимаю, что вы можете получить внутренний объект (IMedicalNote), но только для цели только для чтения. Вы не должны делать никаких изменений. Все изменения должны быть сделаны через корень, чтобы он мог контролировать целостность. Кроме того, что вы подразумеваете, передавая его обратно в корень? Как только мы модифицируем свойство дочернего элемента извне, то это все, оно изменено (по ссылке), минуя корень, не так ли? Что делает «переход к корню» точно? – Sheepy

+0

Извинения, ответ несколько неоднозначный. Корень передает обратно копии объектов, которые клиент затем обновляет и передает обратно root. Корень затем отвечает за проверку инвариантов и установление отношений. Обеспечивает разумное разделение ответственности: корень знает о связях и ограничениях между агрегированными объектами, но не нуждается в подробностях всех подтипов. Клиенты могут работать по определенным подтипам по мере необходимости. Примечание. Корневой элемент, возможно, должен знать некоторые сведения о подтипе (например, если он должен содержать только один «BloodCheckNote», но может иметь много «TemperatureNote's»). – sfinnie

+0

Вы хотите создать механизм клонирования, чтобы корень всегда возвращал клон каждой дочерней заметки при доступе через публичный getter root? И затем способ скопировать эти значения обратно в его фактический объект, как только клиент вернет эти дети обратно в корень. Просто хотел прояснить, понимал ли это то, что вы имели в виду, потому что это звучит довольно много. Cheers – Sheepy

Смежные вопросы