У меня есть ограниченный контекст, в котором две агрегаты являются центральными для бизнеса в этой конкретной модели.Модель начинает проявлять себя достаточно хорошо, но связь между этими требует, чтобы был двунаправленным (я изучаю и создаю альтернативные проекты, поэтому, вероятно, я в конечном итоге нашел лучший подход).
Допустим, A
и B
являются корнями совокупности каждого агрегата.A
и B
имеет ассоциацию n:m
.Инвариантам вокруг A
необходимо отслеживать как минимум число экземпляров B
, и существует высокий уровень конкуренции за одновременный доступ к A
(многие пользователи изменяют экземпляры A
в одно и то же время, весьма вероятно, что это же A
)и бизнес говорит, что инвариант здесь должен быть принудительным (никакая возможная последовательность не допускается).Если пользователь пытается «прикрепить» экземпляр B
к экземпляру A
(это, конечно, имеет понятие в UL), правила требуют проверки текущего состояния B
, включая экземплярыдругих A
с, к которым это относится.Это очень важно, потому что в зависимости от анализа операция может быть отклонена, поэтому инварианты агрегата сохраняются.Низкая конкуренция за параллелизм на B
(очень и очень маловероятно, что разные пользователи попытаются изменить один и тот же B
, и, вероятно, это ошибка пользователя).
Поскольку конкуренция на низком уровнеB
, есть небольшой урон при сохранении обоих агрегатов в одной транзакции.Однако операция «присоединить» к A
требует только текущего состояния B
, поэтому их можно не синхронизировать в текущей транзакции, но в следующей (которая может быть запущена)немедленно) нужно увидеть изменения последнего.
Из чтения Синей книги и Красной книги я знаю, что хранилищу разрешено возвращать, в некоторых случаях, объект-значение вместо целого агрегата,но как насчет того, чтобы позволить хранилищу гидратировать часть состояния B
, полученную из состояния A
на уровне базы данных?
Текущая транзакция может изменяться и сохранять состояниеA
без изменения B
.Затем следующая транзакция снова получает B
, но на этот раз изменения видны, поскольку B
регенерируется с проверкой состояния A
в хранилище.
Мой вопрос не о самой реализации, этоо модели: при таком подходе модель выражает, что A
относится ко многим B
s, а A
имеет правильную операцию для изменения этой коллекции (операция «присоединить»).Но даже если B
относится ко многим A
с, сценарии использования требуют перехода от B
к A
с, и поэтому метод A
разрешает эту навигацию, этот набор A
внутри B
явно не управляет операция «присоединить» , так как теперь A
не имеет метода для изменения его коллекции; это реализовано в инфраструктуре внутри репозитория посредством поиска и восстановления базы данных .Да, модель кода все еще выражает существование отношения от B
до A
, но неясно, откуда оно и как рассчитывается.
Что следуетЯ предпочитаю?Более явная модель кода, которая поддерживает двунаправленную ассоциацию в коде, управляется с помощью операции «присоединить», даже если она включает в себя поддержание синхронизации ссылок и изменение двух разных агрегатов в одной транзакции;или позволить хранилищу воссоздать один из этих агрегатов, исследуя состояние постоянного состояния, но оставляя часть семантики ассоциации внутри реализации хранилища?
ОБНОВЛЕНИЕ: больше контекста
Область, упрощенная, очень похожа на это: образовательный центр, где есть классы, курсы и студенты.Студенты в основном управляются в другом БК (личные данные и т. Д.), Но здесь они также могут изменяться, поскольку им разрешено изменять договор / соглашение практически в любое время (это также выполняется в другом БЦ), и это определяет тип курсовони могут быть зарегистрированы. Уникальный номер этого контракта является релевантным, и организация Student
должна отслеживать их.Студент начинает посещать занятия, но в любой момент может перейти на другой курс, поэтому все записи о посещаемости (баллы и т. Д.) Принадлежат организации Student
.
Пользователь встречается со студентом иПользователь регистрирует студента на курсах на основе текущего контракта, но есть много правил для этого сверх возможностей, большинство из которых требуют изучения текущего статуса Student
, как предыдущие задания или посещаемость.Бизнес довольно ограничен в этом, и одна из целей системы - обеспечить соблюдение этих правил, не оставляя выбора пользователям.Тем не менее, разные стратегии регистрации могут быть выбраны на основе разрешений пользователя.О, и есть много пользователей, выполняющих одну и ту же операцию с разными учащимися в одно и то же время, и бизнес-запрос требует, чтобы пользователи видели изменения «в реальном времени», насколько это возможно.
Есть и другие правила, конечнонекоторые из них касаются возможностей курса, но это проблеск области.Я смоделировал как Course
(до этого как A
), так и Student
(до того как B
) как Совокупные корни.Для выполнения сценария использования регистрации (операция «присоединить») Course
извлекается из его репозитория и взаимодействует с доменной службой.Course
нужны идентификаторы для своих нынешних учеников, чтобы, по крайней мере, рассчитать его емкость, поскольку другой пользователь может изменять тот же курс в одно и то же время (возникает исключение параллелизма, и правила должны быть проверены снова);Не существует варианта использования для прямой ссылки на Student
из Course
, только по id.Экземпляр Student
необходим для проверки его текущего контракта, и Course
s зарегистрированы / были зарегистрированы, включая "тот же сеанс зачисления", чтобы определить результат запрошенной операции.