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

Чтобы сделать этот пример менее сложным, скажем, у нас есть домен двух сущностей: ресторан и время открытия. Ресторан определяется как совокупный корень.

Text

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

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

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

Я мог бы придумать два возможных решения:

Ленивая загрузка

Ленивая загрузка времени открытия / суб-сущностей через прокси свойств структуры сущностей. Таким образом, совокупный корень может существовать без необходимости загружать их, но всякий раз, когда они необходимы, к ним можно получить доступ. Однако везде, где я искал ответ, я заметил, что ленивая загрузка в совокупных корнях считается плохой практикой. Может быть, кто-нибудь может объяснить, почему.

Меньшие совокупные корни

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

Во всех приведенных выше примерах я говорю только о командной стороне (а не о запросах или сериализации).

Может быть, мне не хватает некоторых фундаментальных идей. Как должны быть организованы агрегатные корни в этом примере, и почему ленивая загрузка считается плохой практикой?

< Сильный > ИЗМЕНИТЬ Не уверен, почему этот вопрос был закрыт из-за «мнения основаны». Я спрашиваю передовой опыт и почему ленивая загрузка не в этом случае.

4
Nando 18 Фев 2020 в 16:59

3 ответа

Лучший ответ

Однако везде, где я искал ответ, я заметил, что ленивая загрузка в совокупных корнях считается плохой практикой.

Это не совсем так. Фактическое ограничение для отложенной загрузки других агрегатов из-за прямых ссылок в корни агрегатов; вместо этого рекомендуется ссылаться на другие агрегаты с их идентификаторами. Это ограничение имеет очень веские причины.

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

Каждый агрегированный корень определяет свою собственную границу согласованности; каждая транзакция предназначена для обеспечения согласованности одного агрегата . Операции обновления (или транзакции) между агрегатами, передаваемые через события домена , должны быть в конечном итоге согласованными .

Если вы держите прямую ссылку на другой агрегат, что требует ленивой загрузки и выполнения обновлений для них, вам следует пересмотреть свой дизайн.

Выбор в вашем сценарии, как обычно, зависит от бизнес-контекста или области, с которой он имеет дело. Если вы думаете, что OpeninigTime является отдельным агрегатом с собственной границей согласованности, вы должны хранить только его идентификатор и публиковать события домена, содержащие идентификатор, который будет обрабатывать агрегат OpeningTime путем извлечения соответствующего агрегата. Если, однако, это не так (что кажется более вероятным), вы можете очень долго держать ссылку, лениво загружать и выполнять ее обновление.

1
f.nasim 20 Фев 2020 в 04:36

Эта проблема классифицируется как проблема проверки набора, и есть несколько потенциальных решений в зависимости от потребностей домена.

Строгая согласованность

  1. Создайте AR, чтобы защитить весь набор. В приведенном выше примере, если такой набор имеет разумную длину, вы можете создать выделенный RestaurantSchedule AR. Вы даже можете разделить такой AR дальше на RestaurantWeekSchedule, где у вас будет 1 AR на каждую неделю. Если вы хотите добавить / удалить дни открытия, это создаст / загрузит AR данной недели. Другим вариантом может быть настройка ORM для загрузки только подмножества коллекции, например schedule = scheduleRepo.loadForWeek(openingTime.week()); schedule.add(openingTime); . Оптимистическая блокировка все еще позволяет обнаруживать конфликты.

  2. Применять правило в БД. Например, если у вас есть реляционная БД, вы можете использовать OpeningTime в качестве AR, а затем использовать уникальные ограничения для предотвращения нарушений. В конечном итоге правило будет жить за пределами домена, но оно может быть приемлемым. Правила уникальности не так уж интересны.

Возможная последовательность

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

2
plalx 19 Фев 2020 в 13:25

На ваш вопрос, почему ленивая загрузка считается плохой практикой:

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

0
Davis Jahn 18 Фев 2020 в 15:02