Отслеживание изменений в графе сложных объектов

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

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

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

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

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

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

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

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

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

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

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

Ответ 1

Все об ответственности.

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

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

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

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

Как я передаю данные назад? -

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

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

Любые соображения?