Отслеживание изменений в графе сложных объектов - PullRequest
10 голосов
/ 10 августа 2010

Я начал задумываться об отслеживании изменений в графе сложных объектов в отключенном приложении. Я уже нашел несколько решений, но я хотел бы знать, есть ли лучшая практика или какое решение вы используете и почему? Я передал тот же вопрос на форум MSDN , но получил только один ответ. Я хотел бы получить больше ответов, чтобы узнать из опыта других разработчиков.

Этот вопрос относится к .NET, поэтому для ответов с подробностями реализации я предпочитаю ответы, относящиеся к миру .NET, но я думаю, что на других платформах то же самое.

Теоретическая проблема в моем случае определяется в многослойной архитектуре (не обязательно n-уровневой в данный момент) следующим образом:

  • Слой репозиториев, использующих ORM для работы с постоянством (инструмент ORM на данный момент не имеет значения, но, скорее всего, это будет Entity Framework 4.0 или NHibernate).
  • Набор чистых классов (постоянный ignorant = POCO, который эквивалентен POJO в мире Java), представляющих объекты домена. Хранилища сохраняют эти классы и возвращают их как результаты запросов.
  • Набор доменных сервисов, работающих с доменными сущностями.
  • Фасадный слой, определяющий шлюз к бизнес-логике. Внутренне он использует репозитории, доменные службы и доменные объекты. Объекты домена не отображаются - каждый метод фасада использует набор специализированных объектов передачи данных для параметра и возвращаемого значения. Каждый метод фасада отвечает за преобразование сущности домена в DTO и наоборот.
  • Современное веб-приложение, которое использует фасадный слой и DTO - я называю это автономное приложение. Как правило, дизайн может измениться в будущем, так что слой Facade будет обернут слоем веб-сервиса, а веб-приложение будет использовать этот сервис => переход на трехуровневый (веб, бизнес-логика, база данных)

Теперь предположим, что одним из объектов домена является Order, который имеет детали Order (строки) и связанные Order. Когда клиент запрашивает Заказ для редактирования, он может изменить Заказ, добавить, удалить или изменить любую деталь Заказа и добавить или удалить связанные Заказы. Все эти модификации делаются на данных в веб-браузере - Javascript и AJAX. Таким образом, все изменения отправляются в одном кадре, когда клиент нажимает кнопку сохранения. Вопрос в том, как справиться с этими изменениями? Хранилище и инструмент ORM должны знать, какие объекты и отношения были изменены, вставлены или удалены. Я закончил с двумя "лучшими" решениями:

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

  2. Не отслеживать изменения в DTO вообще. При получении измененных данных в фасадном слое создайте измененную сущность и загрузите фактическое состояние из хранилища (как правило, дополнительный запрос к базе данных - это точка, которой я не очень доволен) - объедините эти две сущности и автоматически отследите изменения по прокси сущности, предоставленному инструментом ORM (Entity Framework 4.0 и NHibernate позволяют это). Особое внимание необходимо уделить обработке параллелизма, поскольку фактическое состояние не обязательно должно быть начальным состоянием.

Что вы думаете об этом? Что вы порекомендуете?

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

Мой интерес к этой теме идет еще дальше.Например, предположим, что приложение использует трехуровневую архитектуру, а клиент (веб-приложение) не будет записан в классах .NET = DTO, которые нельзя использовать повторно.Отслеживание изменений в DTO будет намного сложнее, потому что от другой команды разработчиков потребуется должным образом реализовать механизм отслеживания в своих инструментах разработки.

Я считаю, что эти проблемы необходимо решать во многих приложениях, поделитесь своим опытом.

Ответы [ 2 ]

3 голосов
/ 04 февраля 2011

наша архитектура очень похожа на вашу, но с использованием клиента Silverlight, содержащего те же доменные объекты (что точно - код передается) на клиенте и сервере.Коротко о нашей архитектуре:

  • У клиента есть модель домена, и изменения отслеживаются с помощью моей собственной реализованной инфраструктуры отслеживания (она использует AOP, поэтому я могу использовать POCO на стороне клиента;Я не знаю основы для этого, и я хочу, чтобы модель домена оставалась невежественной)
  • Вся эта модель хранится в своего рода удаленном хранилище на клиенте.Когда мы сохраняем эти изменения, дерево изменений будет извлечено (моей структурой отслеживания изменений) и переведено в DTO (именно DataContracts, но это не имеет значения) с использованием ассемблеров.DTO имеют флаг состояния отслеживания (новый, измененный, удаленный).
  • На стороне сервера (уровень обслуживания реализуется веб-службами WCF) DTO преобразуются в объекты домена и подключаются к ORM (NHibernate внаш случай).Из-за этого прикрепленного процесса мне нужно состояние отслеживания.Дополнительные изменения могут быть выполнены и сохранены с помощью ORM

. В настоящее время сложно прикрепить сложные графики к ORM, но я надеюсь, что причина в том, что у нас нет большого опыта использования NHibernate.

Мы еще не закончили, но это выглядит многообещающе.

Для доступа к данным мы пытались использовать WCF Dataservices.Но я не думаю, что мы собираемся использовать их, потому что требование использует DataContract.Это приводит к переводу запросов LINQ на основе DataContract к запросам LINQ на основе доменных объектов.Это не удобно и слишком сложно реализовать, если модель предметной области и контракты данных сильно отличаются (что будет иметь место в некоторых итерациях).

Какие-либо соображения?

3 голосов
/ 13 августа 2010

Все дело в ответственности.

(я не уверен, что вы ответили именно на такой ответ - дайте мне знать, если это не так, я могу его обновить).

Таким образом, у нас есть несколько уровней в системе - каждый отвечает за свою задачу: доступ к данным, пользовательский интерфейс, бизнес-логика и т. Д. Когда мы проектируем систему таким образом, мы (среди прочего) пытаемся облегчить будущие изменения,сделать каждый компонент ответственным за одну задачу - чтобы он мог сосредоточиться на этой одной задаче и делать это хорошо.Это также облегчает модификацию системы с течением времени и необходимостью изменений.

Подобные мысли необходимо учитывать при рассмотрении DTO - "как отслеживать изменения?"например.Вот как я к этому подхожу: БЛ отвечает за управление правилами и логикой;учитывая природу Интернета без состояния (именно там я и выполняю большую часть своей работы), я просто не отслеживаю состояние объекта и не ищу явные изменения.Если пользователь передает данные обратно (для сохранения / обновления), я передам всю партию обратно, не заботясь о том, что было изменено.

Одной рукой это может показаться неэффективным, но поскольку объемы данных не являютсяэто просто не проблема;с другой стороны, чем меньше «движущихся частей», тем меньше может пойти не так, как процесс намного проще.

Как я передаю данные обратно?-

  • Я использую DTO (или, возможно, POCO будет более точным);когда я обмениваюсь данными между BL и DAL (через интерфейсы / DI ), данные обмениваются как DTO (или их совокупность).В частности, я использую структуру для одного экземпляра и коллекцию этих структур для нескольких.

  • DTO определены в общем классе, который имеет очень мало зависимостей.

  • Я сознательно пытаюсь ограничить количество DTO, создаваемых для конкретного объекта (например, "Порядок"), но в то же время я сделаю новые, если есть хорошийпричина.Как правило, у меня будет «толстый» DTO, который содержит большинство / все данные, доступные для этого объекта, и, вероятно, у меня будет гораздо более тонкий, который предназначен для использования в коллекциях (для списков и т. Д.).В обоих случаях эти DTO являются чистыми для возврата информации для «чтения».Вы должны помнить об обязанностях - когда BL запрашивает данные, он обычно не пытается записать данные в одно и то же время;поэтому тот факт, что DTO «только для чтения», больше соответствует соответствию чистому интерфейсу и архитектуре, чем бизнес-правилу.

  • Я всегда определяю отдельные DTO для вставки и обновления - даже еслиони имеют одни и те же поля.Таким образом, наихудшее, что может случиться, - это дублирование некоторого тривального кода, в отличие от наличия зависимостей и нескольких случаев повторного использования для распутывания.

Наконец - не путайте работу DALс тем, как работает пользовательский интерфейс;Позвольте ORM делать свое дело, просто потому, что они хранят данные определенным образом, не означает, что это единственный способ.

Самое важное - указать значимые интерфейсы между вашими слоями.

Управление тем, что изменилось, является работой BL;пусть пользовательский интерфейс будет работать так, как вам удобно, и пусть BL выяснит, как он хочет с этим справиться, а DAL (через ваш красивый чистый интерфейс с DI ) просто делает то, что ему говорят.

...