Загрузка всех объектов как части совокупного корня

#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», не имеет смысла. Не могли бы вы подробнее остановиться на своем ответе? Поскольку я нигде не читал, я не могу найти какие-либо ресурсы по превращению агрегата в часть другого агрегата