Обнаружение изменений в объектах внутри совокупного корневого каталога
-
07-07-2019 - |
Вопрос
Я хочу посмотреть, какие подходы люди могли бы использовать для обнаружения изменений в объектах, которые являются частью их агрегатов.У меня есть кое-что, что работает, но я не в восторге от этого.По сути, мой репозиторий отвечает за определение того, изменилось ли состояние агрегированного корневого каталога.Давайте предположим, что у меня есть совокупный корень, называемый Book
и сущность , называемая Page
в пределах совокупности.A Book
содержит один или несколько Page
объекты, хранящиеся в Pages
Коллекция.
Прежде всего, вставить противсценарии обновления выполняются путем проверки корневого каталога aggregate и его сущностей для определения наличия ключа.Если ключ присутствует, предполагается, что объект был когда-то сохранен в базовом источнике данных.Это делает его кандидатом на обновление;но это не является окончательным, основанным только на этом для сущностей.С совокупным корнем ответ очевиден, поскольку существует только один и это единственная точка входа, можно предположить, что присутствие ключа будет определять операцию.В моем случае приемлемым сценарием является сохранение самого aggregate root обратно, чтобы я мог зафиксировать дату изменения.
Чтобы облегчить такое поведение для самих объектов, мой EntityBase
класс содержит два простых свойства: IsUpdated()
, IsDeleted()
.Оба они по умолчанию имеют значение false.Мне не нужно знать, является ли это новым или нет, потому что я могу сделать это определение на основе наличия ключа, как упоминалось ранее.Методы в реализации, в данном случае на Странице, будут содержать каждый метод, который изменяет набор резервных данных IsUpdated()
к истине.
Так, например, у Page есть метод, который называется UpdateSectionName()
который изменяет базовое значение SectionName
свойство, доступное только для чтения.Этот подход используется последовательно, поскольку он допускает логическую точку привязки средств проверки в методе (предотвращающем переход объекта в недопустимое состояние), который выполняет эту настройку данных.Конечным результатом является то, что я должен поставить this.IsUpdated() = true;
в конце метода.
Когда совокупный корневой файл отправляется в репозиторий для Save()
(логический переключатель либо на Insert()
или Update()
операция), затем он может выполнить итерацию по Pages
коллекция в Book
, ищет любые страницы , на которых есть один из трех сценариев:
- Ключа нет.A
Page
при этом ключ не будет вставлен. IsDeleted = true;
Удаление превосходит обновление, и удаление будет зафиксировано - игнорируя любое обновление дляPage
.IsUpdated = true;
Для Страницы будет зафиксировано обновление.
Выполнение этого таким образом не позволяет мне просто вслепую обновлять все, что есть в коллекции Pages, что могло бы быть сложной задачей, например, если бы в Книге было несколько сотен страниц.Я подумывал о том, чтобы получить копию Книги, провести сравнение и зафиксировать только обнаруженные изменения (вставки, обновления и удаления на основе присутствия и / или сравнения), но мне показалось, что это ужасно болтливый способ сделать это.
Основным недостатком является то, что разработчик должен помнить о необходимости установки IsUpdated в каждом методе объекта.Забудьте одно из них, и оно не сможет обнаружить изменения для этого значения.Я поиграл с идеей какого-то пользовательского хранилища резервных копий, которое могло бы прозрачно фиксировать изменения во времени, что, в свою очередь, могло бы IsUpdated
свойство, доступное только для чтения, которое репозиторий может использовать для агрегирования обновлений.
Репозиторий использует реализацию шаблона unit of work, которая основывает свои действия на временной метке, сгенерированной при добавлении к нему aggregate root.Поскольку в очереди для операций может быть несколько объектов, операции с объектами сворачиваются и выполняются сразу после выполнения агрегированных корневых операций, к которым принадлежат объекты.Я мог бы сделать еще один шаг вперед и создать еще одну единицу работы, чтобы просто обрабатывать операции объекта и основывать их на каком-то отслеживании событий, используемом в объекте (именно поэтому я предполагаю, что некоторые продукты ORM на рынке обладают аналогичным уровнем функциональности).
Однако, прежде чем я продолжу двигаться в этом направлении, я хотел бы услышать идеи / рекомендации / опыт относительно этого.
Редактировать: Несколько дополнительных фрагментов информации, которые было бы полезно знать:
- Текущий язык, с которым я работаю, - C #, хотя я старался не раскрывать как можно больше информации, относящейся к конкретному языку, потому что это скорее теоретическое обсуждение.
- Код для репозиториев / сервисов/сущностей / и т.д.основан на концепции Тима Маккарти, изложенной в его книге ".NET Domain-Driven Design with C #", и вспомогательном коде на CodePlex ( Кодовый комплекс ).Это обеспечивает доступное понимание используемого подхода, хотя то, с чем я работаю, в значительной степени было переписано с нуля.
Решение
Короче говоря, мой ответ заключается в том, что я согласился с тем, что предложил.Это работает, хотя я уверен, что есть возможности для улучшения.Изменения на самом деле заняли очень мало времени, поэтому я чувствую, что в данном случае я не слишком далеко ушел от принципов KISS или YAGNI.:-)
Я все еще чувствую, что в операциях есть место для проблем, связанных со сроками, но я должен быть в состоянии обойти их в реализациях репозитория.Не идеальное решение, но я не уверен, что стоит изобретать велосипед, чтобы исправить проблему, которой можно избежать за меньшее время, чем требуется для устранения.