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