Могу ли я иметь "неполные" агрегаты в DDD?
DDD заявляет, что вы должны получать доступ к объектам только через свой совокупный корень. Так скажите, например, что у вас есть совокупный корень X, который потенциально имеет много дочерних Y-объектов. Теперь, для некоторого сценария, вы действительно заботитесь только о подмножестве этих объектов Y за один раз (возможно, вы показываете их в выгружаемом списке или что-то еще).
Правильно ли реализовать репозиторий, чтобы в таких сценариях он возвращал неполную совокупность? То есть. объект X, который представляет собой коллекцию Ys, содержит только интересующие нас экземпляры Y, а не все из них? Это может, например, вызвать методы на X, которые выполняют некоторый расчет, в котором Ys не будет вести себя так, как ожидалось.
Возможно, это указание на то, что рассматриваемый объект Y следует считать продвинутым до совокупного корня?
Моя текущая идея (на С#) заключается в том, чтобы использовать задержанное выполнение LINQ, так что мой объект X имеет IQueryable, чтобы представлять его связь с Y. Таким образом, я могу иметь прозрачную ленивую загрузку с фильтрацией... Но получение это для работы с ORM (Linq to Sql в моем случае) может быть немного сложнее.
Любые другие умные идеи?
Ответы
Ответ 1
Я считаю, что совокупный корень с большим количеством дочерних объектов будет запахом кода или запахом DDD, если вы это сделаете.:-) Обычно я смотрю на два варианта.
- Разделите свой агрегат на множество небольших агрегатов. Это означает, что мой оригинальный дизайн не был оптимальным, и мне нужно определить некоторые новые объекты.
- Разделите свой домен на несколько ограниченных контекстов. Это означает, что существуют конкретные наборы сценариев, которые используют общее подмножество сущностей в совокупности, тогда как существуют другие наборы сценариев, в которых используется другое подмножество.
Ответ 2
Джимми Нильссон намекает в своей книге, что вместо чтения полного агрегата вы можете прочитать его снимок. Но вы не должны сохранять изменения в классах снимков в базе данных.
Книга Джимми Нильссона Глава 6: Подготовка к инфраструктуре - Запросы. Страница 226.
Шаблон снимка
Ответ 3
Вы действительно задаете два совпадающих вопроса.
-
Название и первая половина вашего вопроса являются философскими/теоретическими. Я думаю, что причина доступа к сущностям только через их "совокупный корень" - это абстрагирование тех деталей описания, которые вы описываете. Доступ через совокупный корень - это способ уменьшить сложность, имея надежную точку доступа. Вы устраняете трение/неоднозначность/неопределенность, придерживаясь конвенции. Неважно, как это реализовано в корне, вы просто знаете, что когда вы попросите сущность, он будет там. Я не думаю, что эта перспектива исключает "фильтрованный репозиторий", как вы описываете. Но для обеспечения ямы успеха для разработчиков, которые должны впасть, должно быть невозможно создать экземпляр репозитория без явной информации о его "фильтрации"; аналогично, если возможен общий доступ к экземпляру репозитория, "фильтрация" должна быть явной при кодировании в вызывающем абоненте.
-
Вторая половина вашего вопроса касается реализации на конкретной платформе. Не уверен, почему вы упомянули о задержке выполнения, я думаю, что это действительно ортогонально фильтрующему вопросу. Сама фильтрация может быть немного сложной для реализации с LINQ. Возможно, вместо того, чтобы встраивать "Где лямбды", вы создаете их коллекцию и выбираете ее в зависимости от фильтра, который вам нужен.
Ответ 4
Вам разрешено, так как код будет компилироваться в любом случае, но если вы собираетесь создать чистый дизайн DDD, у вас не должно быть неполных экземпляров объектов.
Вы должны заглянуть в LazyLoading, если вы боитесь загружать огромный объект, из которого вы будете использовать только небольшую часть своих дочерних объектов.
LazyLoading задерживает загрузку того, что вы решите на ленивую загрузку, до момента обращения к ней. Они используют обратные вызовы для вызова метода загрузки после вызова кода.
Ответ 5
Можно ли тогда реализовать репозиторий, чтобы в таких сценариях он возвращал неполный агрегат?
Не за что. Агрегат - это транснациональная граница для изменения состояния вашей системы. Никогда не используйте агрегаты для запроса данных. Разделите систему на стороны записи и чтения. (читать о CQR и CQRS). Когда мы думаем о "CRUD", мы внедряем нашу систему, основанную на каком-то ресурсе. Допустим, у вас есть совокупность "Назначение". Мышление "Crudish" означает, что мы должны реализовать сценарии создания, обновления, удаления, назначения GetAll. Это означает, что Appointment [] должен быть возвращен для GetAll. Когда вы думаете, что на основе прецедентов (HexagonalArchitecture) вашими прецедентами будут ScheduleAppointment, RescheduleAppointment, CancelAppointment. Но для стороны запроса это может быть: /myCalendar. Мы возвращаем все встречи для конкретного пользователя в объекте ClientCalendar. Создайте отдельный DTO для сторон запроса. Никогда не используйте агрегаты для этой цели.