#java #domain-driven-design #ddd-repositories
#java #дизайн, управляемый доменом #ddd-репозитории
Вопрос:
Из того, что я прочитал в Интернете, агрегат при загрузке из базы данных должен находиться в состоянии завершения. Это означает, что он должен иметь доступ ко всем объектам внутри него, также загруженным из базы данных, чтобы агрегат никогда не находился в недопустимом / неполном состоянии.
Что, если совокупность содержит объект, которых может быть миллионы, например. Aggregate A
содержит Entity B
и Entity C
. Теперь в наихудшем сценарии может быть до 100 Entity B
экземпляров под Aggregate A
, но могут быть миллионы (если не миллиарды) Entity C
под Aggregate A
.
Пример использования:
Примером использования было бы то, что я хочу удалить один конкретный экземпляр Entity C
из Aggregate A
, используя идентификатор. Чтобы сделать это DDD
способом, мне пришлось бы сначала загрузить Aggregate A
из базы данных и загрузить все ее объекты в память. а затем удалите элемент, возможно, используя метод, подобный приведенному ниже:
public class AggregateA extends AbstractAggregateRoot<AggregateA>{
private String aggregateId;
private Map<String, EntityC> entityC;
public void removeEntityC(String idToRemove) {
this.entityC.remove(idToRemove);
registerEvent(new EntityCRemoved(aggregateId, idToRemove));
}
}
Вопрос:
Загрузка миллионов объектов в память для выполнения любого Write Operation (Command in CQRS)
для одного запроса не кажется правильным способом.
Я что-то здесь упускаю?
Ответ №1:
Вы неправильно понимаете мандат DDD «загрузить полный агрегат». Как вы сказали, это совершенно непрактично делать. Моя рекомендация — загружать полные части aggregate, которые необходимы.
Например: Мое действие (хотя и надуманное) заключается в редактировании определенного комментария в блоге в записи блога с миллионами комментариев. Что я сделаю, так это загружу совокупный блог и единственный комментарий, который я пытаюсь обновить, из репозитория. Затем я продолжу и попрошу блог aggregate обновить комментарий. Конечно, другим вариантом дизайна было бы загрузить только комментарий (поскольку вы можете идентифицировать его по его идентификатору), но это было бы загрузкой неполного объекта, поскольку в нем отсутствовал бы совокупный корень (это то, что мандат говорит не делать).
Одна из проблем здесь, конечно, заключается в том, что выполнение пакетных обновлений в DDD становится неэффективным. Это слабое место по дизайну и проблема, решаемая за пределами DDD.
Комментарии:
1. Спасибо, это имеет больше смысла, чем ответ бркнера, но теперь не возникнет ли проблемы, если, допустим, у вас есть инвариант домена, согласно которому никакие два комментария не могут быть одинаковыми (просто в качестве примера в продолжение вашего ответа). Теперь, когда вы редактируете комментарий в блоге, предположим, что он конфликтует с другим комментарием. Поскольку вы загрузили только один комментарий, вы не будете знать, конфликтует ли он с другим комментарием, если не будете полагаться на базу данных для уникальной индексации. Но целью DDD было сохранить все инварианты как часть домена и не зависеть от инфраструктуры для обеспечения инвариантов.
2. Хороший вопрос. В вашем расширенном примере: Ваш UL теперь требует, чтобы комментарий был уникальным в блоге. Итак, содержимое вашего комментария теперь является ключевым. Когда вы увлажняете свой блог (свой aggregate), вашему репозиторию необходимо взять ключ (содержимое) и также увлажнить его. Это так, потому что для функции, которую вы пытаетесь выполнить, «завершить» означает репозиторий, увлажняющий блог и любой комментарий с существующим ключом комментариев. Таким образом, вы получаете блог с одним комментарием из репозитория еще до запуска blog.add (комментарий). Остальное оттуда тривиально.
3. Я не совсем понимаю
hydrate
я что-то упускаю? Не могли бы вы указать мне направление, в котором я могу это понять?4. гидратировать означает, когда вы извлекаете его из хранилища. Итак, когда в вашем сообщении в блоге заканчивается хранилище (возможно, база данных), чтобы добавить комментарий, также извлеките все комментарии, которые имеют тот же ключ, что и новый комментарий.
Ответ №2:
Я думаю, это было бы первым признаком пересмотра ваших совокупных границ. Одним из возможных решений было бы рассматривать объект C как совокупность и иметь ссылку по идентификатору на совокупность A. Это зависит от вашего бизнес-домена, так что это всего лишь расплывчатая рекомендация. У вас мог бы быть фабричный метод в Aggregate A или в отдельной службе домена, которая генерирует экземпляр Aggregate C. В общем случае вам следует избегать загрузки сотен объектов в агрегат, потому что это будет охватывать огромную транзакционную границу вокруг этого агрегата.
public class AggregateA {
public AggregateC createAggregateC(...){
//create AggregateC and return it with reference set to AggregateA
return new AggregateC(this.id, ...)
}
}
Комментарии:
1. Но за исключением количества
Entity C
вAggregate A
, это идеальный случай для того, чтобы это былоEntity
. Вот некоторые из них: 1) ДляEntity C
не имеет смысла находиться там безAggregate A
. 2) При удаленииAggregate A
все связанныеEntity C
также должны быть удалены. 3) Любое изменение вEntity C
всегда должно происходить черезAggregate A
, поскольку существуют некоторые инварианты, которыеAggregate A
обеспечивают наEntity C
. Честно говоря, не имело бы смысла разбиватьEntity C
на собственный агрегат2. Это зависит от вашей бизнес-логики. Не имеет смысла загружать тысячи объектов в пределах одного агрегата, поскольку границы ваших транзакций разорвутся. 1) Почему бы и нет? У вас все равно будет ссылка между ними. 2/3) Вы могли бы принудительно применять такие инварианты через доменную службу. Я не уверен, но я думаю, что Вон Вернон рассматривает некоторые из ваших проблем в этой серии блогов. Может быть, взгляните туда. dddcommunity.org/library/vernon_2011
3. Вы правы в части масштаба, и в этом был смысл вопроса «Загрузка миллиона экземпляров объекта, связанного с aggregate», не имеет смысла. Не могли бы вы подробнее остановиться на своем ответе? Поскольку я нигде не читал, я не могу найти какие-либо ресурсы по превращению агрегата в часть другого агрегата