Воссоздайте часть агрегата только в хранилище, чтобы избежать двунаправленной связи в коде - PullRequest
0 голосов
/ 20 декабря 2018

У меня есть ограниченный контекст, в котором две агрегаты являются центральными для бизнеса в этой конкретной модели.Модель начинает проявлять себя достаточно хорошо, но связь между этими требует, чтобы был двунаправленным (я изучаю и создаю альтернативные проекты, поэтому, вероятно, я в конечном итоге нашел лучший подход).

Допустим, 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 зарегистрированы / были зарегистрированы, включая "тот же сеанс зачисления", чтобы определить результат запрошенной операции.

1 Ответ

0 голосов
/ 20 декабря 2018

Как вы указали, должна быть лучшая модель.Двунаправленные отношения уже намекают на то, что эта модель не отражает происходящее.Я не говорю, что двунаправленные ссылки всегда плохи, но они всегда являются «подсказкой».

Похоже, вы моделируете «отношения» между этими двумя вещами, а не поведение ,Это еще более подтверждается тем, что вы на самом деле не описываете, что эти вещи на самом деле делают .Вы не можете моделировать что-либо, основываясь только на данных и на том, как эти данные связаны друг с другом.

Итак, сначала, пожалуйста, дополните описание фактической бизнес-функцией, которую должны выполнять эти объекты.Это не что иное, как хранить, извлекать, загружать, перечислять, выбирать или эти вещи.Я имею в виду бизнес-функции.

Например, если B не имеет реальной функции (это просто данные), ее можно просто свернуть в A.Etc ..

Re: Больше контекста

Хорошо, что я могу извлечь из вашего описания, бизнес-кейс регистрация из Stundent в Course.И проблема в том, что существуют правила, которые могут требовать или не требовать знания об этих двух вещах.

Я бы, возможно, пошел следующим образом: поскольку нам нужны обе эти вещи, кажется, что-то не хватает.Я бы назвал эту вещь Enrollments сейчас.Вот некоторый Java-псевдокод, как это будет работать:

public interface Enrollments {
    Student find(String name); // Or whatever is needed
}

public interface Student {
    List<Course> findEnrollableCourses();
}

public interface Course {
    void enroll();
}

В этой модели Course не моделирует курс самостоятельно, он всегда находится в контексте StudentStudent находится в контексте Enrollments.Это означает, что я могу зарегистрировать a Student в Course, где все «данные» для этой регистрации остаются в этом семантическом контексте (или совокупном корне).Это означает, что в методе enroll() у меня есть доступ ко всем данным «легально».Нет необходимости в круговых зависимостях между этими вещами, существует четкая иерархия.

...