Обнаружение изменений в объектах в совокупном корне

Я хочу увидеть, какие подходы люди могли бы использовать для обнаружения изменений в объектах, которые являются частью их совокупностей. У меня есть кое-что, что работает, но я не без ума от этого. По сути, мой репозиторий отвечает за определение того, изменилось ли состояние совокупного корня. Предположим, что у меня есть совокупный корень с именем Book и сущность с именем Page внутри агрегата. Book содержит одну или несколько Page сущностей, хранящихся в коллекции Pages.

В первую очередь, сценарии вставки и обновления выполняются путем проверки совокупного корня и его сущностей для определения наличия ключа. Если ключ присутствует, предполагается, что объект был однажды сохранен в базовом источнике данных. Это делает его кандидатом на обновление; но он не является окончательным, основываясь только на этом для сущностей. С совокупным корнем ответ очевиден, поскольку существует только один и это особая точка входа, можно предположить, что наличие ключа будет определять операцию. В моем случае приемлемый сценарий - снова сохранить сам совокупный корень, чтобы я мог зафиксировать дату модификации.

Чтобы облегчить такое поведение самих сущностей, мой класс EntityBase содержит два простых свойства: IsUpdated(), IsDeleted(). Оба они по умолчанию имеют значение false. Мне не нужно знать, новый он или нет, потому что я могу сделать это определение на основе наличия ключа, как упоминалось ранее. Методы реализации, в данном случае страница, будут иметь каждый метод, который изменяет набор данных резервного копирования IsUpdated() на true.

Так, например, у страницы есть метод UpdateSectionName(), который изменяет значение поддержки свойства SectionName, доступного только для чтения. Этот подход используется последовательно, поскольку он позволяет использовать логическую точку подключения валидаторов в методе (предотвращающем переход объекта в недопустимое состояние), который выполняет эту настройку данных. Конечным результатом является то, что я должен поставить this.IsUpdated() = true; в конце метода.

Когда совокупный корень отправляется в репозиторий для Save() (логический переход к операции Insert() или Update()), он может затем перебирать коллекцию Pages в Book, ища любые страницы, которые имеют один из трех сценариев:

  1. Нет ключа. Будет вставлен Page без ключа.
  2. IsDeleted = true; Удаление имеет приоритет перед обновлением, и удаление будет зафиксировано без учета любых обновлений для Page.
  3. IsUpdated = true; Для Страницы будет выполнено обновление.

Поступая таким образом, я не могу просто слепо обновлять все, что находится в коллекции Pages, что могло бы быть пугающим, если бы, например, в Книге было несколько сотен сущностей Page. Я рассматривал возможность получить копию Книги, провести сравнение и зафиксировать только обнаруженные изменения (вставки, обновления и удаления на основе присутствия и / или сравнения), но это казалось ужасно болтливым способом сделать это. .

Главный недостаток заключается в том, что разработчик должен не забывать устанавливать IsUpdated в каждом методе объекта. Забудьте об одном, и он не сможет обнаружить изменения для этого значения. Я поигрался с идеей своего рода настраиваемого резервного хранилища, которое могло бы прозрачно изменять временные метки, что, в свою очередь, могло бы сделать IsUpdated свойство только для чтения, которое репозиторий мог бы использовать для агрегирования обновлений.

Репозиторий использует реализацию шаблона единицы работы, действия которого основаны на отметке времени, созданной при добавлении к нему совокупного корня. Поскольку в очереди на выполнение операций может стоять несколько сущностей, операции сущностей объединяются и выполняются сразу после выполнения агрегированных корневых операций, которым принадлежат эти сущности. Я мог бы сделать еще один шаг и создать еще одну единицу работы, чтобы просто обрабатывать операции сущности и основывать их на каком-то виде отслеживания событий, используемом в сущности (именно так я предполагаю, что некоторые из продуктов ORM на рынке выполняют аналогичный уровень функциональности).

Однако, прежде чем я продолжу двигаться в этом направлении, я хотел бы услышать идеи / рекомендации / опыт по этому поводу.

Изменить: несколько дополнительных сведений, которые может быть полезно знать:

  1. Текущий язык, с которым я работаю, - C #, хотя я старался сохранить как можно больше информации о языке, потому что это скорее теоретическое обсуждение.
  2. Код для репозиториев / сервисов / сущностей / и т. Д. основан на концепции Тима Маккарти из его книги «.NET Domain-Driven Design with C #» и вспомогательном коде на CodePlex . Он обеспечивает удобное понимание применяемого подхода, хотя то, с чем я работаю, в значительной степени переписано с нуля.

person Joseph Ferris    schedule 14.09.2009    source источник


Ответы (1)


Короче говоря, я отвечу, что я согласился с тем, что предлагал. Он работает, хотя я уверен, что его можно улучшить. На самом деле изменения заняли очень мало времени, поэтому я чувствую, что в данном случае я не стал отходить слишком далеко от принципов KISS или YAGNI. :-)

Я все еще чувствую, что есть место для проблем, связанных с синхронизацией операций, но я должен иметь возможность обойти их в реализациях репозитория. Не идеальное решение, но я не уверен, что стоит изобретать колесо, чтобы исправить проблему, которой можно избежать за меньшее время, чем требуется для устранения.

person Joseph Ferris    schedule 16.09.2009