Могу ли я иметь «неполные» агрегаты в DDD?

DDD утверждает, что вы должны когда-либо обращаться к сущностям только через их совокупный корень. Скажем, например, что у вас есть совокупный корень X, который потенциально имеет много дочерних Y-сущностей. Теперь, для некоторого сценария, вы на самом деле заботитесь только о подмножестве этих Y-сущностей одновременно (может быть, вы отображаете их в выгружаемом списке или как угодно).

Можно ли тогда реализовать хранилище, чтобы в таких сценариях он возвращал неполный агрегат? То есть. объект X, чья коллекция Ys содержит только интересующие нас экземпляры Y, а не все ? Это может, например, привести к тому, что методы на X, которые выполняют некоторые вычисления с использованием Y, будут вести себя не так, как ожидается.

Возможно ли это указывает на то, что рассматриваемый объект Y следует рассматривать как повышенный до совокупного корня?

Моя текущая идея (в C #) - использовать отложенное выполнение LINQ, чтобы мой объект X имел IQueryable для представления его отношений с Y. Таким образом, я могу иметь прозрачную отложенную загрузку с фильтрацией ... Но заставить это работать с ORM (Linq to Sql в моем случае) может быть немного сложнее.

Любые другие умные идеи?

24.08.2008 13:40:15
5 ОТВЕТОВ

Вы действительно задаете два дублирующих вопроса.

  1. Название и первая половина вашего вопроса философско-теоретическая. Я думаю, что причина доступа к сущностям только через их «совокупный корень» состоит в том, чтобы абстрагироваться от деталей реализации, которые вы описываете. Доступ через совокупный корень - это способ уменьшить сложность, имея доверенную точку доступа. Вы устраняете трение / двусмысленность / неуверенность, придерживаясь соглашения. Неважно, как это реализовано в корне, вы просто знаете, что когда вы запрашиваете сущность, она будет там. Я не думаю, что эта перспектива исключает «отфильтрованное хранилище», как вы описываете. Но чтобы обеспечить яму успехадля того, чтобы разработчики попали в него, было бы невозможно создать экземпляр хранилища без явного указания на его «фильтруемость»; аналогично, если возможен общий доступ к экземпляру репозитория, «фильтруемость» должна быть явной при кодировании в вызывающей стороне.

  2. Вторая половина вашего вопроса о реализации на конкретной платформе. Не уверен, почему вы упоминаете задержку выполнения, я думаю, что это действительно ортогонально к вопросу фильтрации. Сама фильтрация может быть немного сложнее реализовать с помощью LINQ. Возможно, вместо того, чтобы вставлять лямбды Where, вы настраиваете их коллекцию и выбираете в зависимости от того, какой фильтр вам нужен.

1
24.08.2008 15:30:26

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

  1. Разделите ваш агрегат на множество небольших агрегатов. Это означает, что мой оригинальный дизайн не был оптимальным, и мне нужно идентифицировать некоторые новые объекты.
  2. Разделите ваш домен на несколько ограниченных контекстов. Это означает, что существуют определенные наборы сценариев, которые используют общее подмножество объектов в совокупности, в то время как есть другие наборы сценариев, которые используют другое подмножество.
6
20.09.2008 16:44:03
Это может быть запах кода, но может и не быть. У меня та же проблема. У меня есть класс Trace, который содержит географические путевые точки. Теперь эта трасса может быть произвольно длинной, и хотя сама трассировка является сущностью, путевые точки не являются. Они являются ценностными объектами. Поэтому, если я хочу обновить трассировку в чистом смысле DDD, мне придется загрузить полную трассировку корня агрегата со всеми ее путевыми точками, добавить несколько путевых точек и снова сохранить полную трассировку. Это явно не подходит для больших следов. Так что из-за проблем с производительностью я столкнулся с возможностью разрешения частично загруженных агрегатных корней ...
Jonny Dee 4.05.2012 09:35:38
Я понимаю, что этому ответу 11 лет ... Но я полагаю, что нужно моделировать их совокупности на основе того, что говорят эксперты в области. Таким образом, в случае, если кто-то неверно истолковал то, что сказали эксперты по доменам, огромная совокупность должна быть преобразована в меньшую, чтобы лучше соответствовать видению экспертов по предметной области. В случае, если огромная совокупность согласуется с ментальной моделью экспертов в предметной области - формально говоря, DDD требует, чтобы она была огромной. Не повезло, наверное. На самом деле Эрик Эванс упомянул эту проблему и возможные решения в своем выступлении: youtu.be/lE6Hxz4yomA?t=1778
Nik 5.05.2019 23:17:24

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

Книга Джимми Нильссона Глава 6: Подготовка к инфраструктуре - Запросы. Страница 226.

Шаблон снимка

4
6.10.2008 12:42:14

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

Вам следует изучить LazyLoading, если вы боитесь загрузить огромный объект, из которого вы будете использовать только небольшую часть его дочерних объектов.

LazyLoading задерживает загрузку всего, что вы решите lazy-load, до момента, пока к ним не будет получен доступ. Они используют обратные вызовы для вызова метода загрузки, когда код вызывает их.

0
2.12.2008 20:23:16

Можно ли тогда реализовать хранилище, чтобы в таких сценариях он возвращал неполный агрегат?

Не за что. Агрегат - это транснациональная граница для изменения состояния вашей системы. Никогда не используйте агрегаты для запроса данных. Разделите систему на стороны записи и чтения. (читать о CQR & CQRS). Когда мы думаем о «CRUD», мы внедряем нашу систему, основанную на каком-то ресурсе. Допустим, у вас есть совокупность «Назначение». Мышление «Crudish» означает, что мы должны реализовать сценарии создания, обновления, удаления, назначения GetAll. Это означает, что Appointment [] должен быть возвращен для GetAll. Когда вы думаете, что на основе вариантов использования (HexagonalArchitecture) вашими вариантами использования будут ScheduleAppointment, RescheduleAppointment, CancelAppointment. Но для стороны запроса это может быть: / myCalendar. Мы возвращаем все встречи для конкретного пользователя в объекте ClientCalendar. Создайте отдельные DTO для сторон запроса. Никогда не используйте агрегаты для этой цели.

0
4.02.2019 12:09:54