Обнаружение изменений в объектах в совокупном корне - PullRequest
3 голосов
/ 14 сентября 2009

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

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

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

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

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

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

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

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

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

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

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

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

1 Ответ

1 голос
/ 17 сентября 2009

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

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

...