Архитектура корпоративных приложений WPF / Silverlight.. что вы делаете?

#wpf #silverlight #architecture #enterprise

#wpf #silverlight #архитектура #Предприятие

Вопрос:

Мне уже довольно давно интересно, чем живет сообщество; я говорю о больших, ориентированных на бизнес корпоративных приложениях WPF / Silverlight.

Теоретически, существуют разные модели:

  • Модель данных (обычно связанная с вашими таблицами БД, Edmx / NHibarnate/.. сопоставленные объекты)
  • Бизнес-модель (классы, содержащие фактическую бизнес-логику)
  • Модель передачи (классы (dto), открытые для внешнего мира / клиента)
  • Модель представления (классы, к которым привязаны ваши фактические представления)

Совершенно очевидно, что такое разделение имеет очевидные преимущества; Но работает ли это в реальной жизни? Это кошмар для обслуживания?

Итак, что вы делаете? На практике вы используете разные модели классов для всех этих моделей? Я видел много вариантов этого, например:

  • Модель данных = бизнес-модель: модель данных сначала реализовала код (как POCO) и использовалась как бизнес-модель с бизнес-логикой
  • Бизнес-модель = Модель передачи = Модель представления: бизнес-модель предоставляется клиенту как таковая; Не происходит сопоставления с DTO, … Представление напрямую привязывается к этой бизнес-модели
  • Готовые сервисы Silverlight RIA с открытой моделью данных: модель данных = бизнес-модель = Модель передачи. А иногда даже модель переноса = модель просмотра.
  • ..

Я знаю, что здесь есть ответ «это зависит»; Но от чего это зависит тогда; Какие подходы вы использовали и как вы оглядываетесь на это?

Спасибо, что поделились,

С уважением, Коэн

Ответ №1:

Хороший вопрос. Я никогда не кодировал ничего по-настоящему корпоративного, поэтому мой опыт ограничен, но я начну.

В моем текущем проекте (WPF / WCF) используется модель данных = Бизнес-модель = Модель передачи = Модель просмотра! Серверной части БД нет, поэтому «модель данных» — это фактически бизнес-объекты, сериализованные в XML.

Я играл с DTO, но быстро обнаружил, что ведение домашнего хозяйства крайне сложно, и вездесущему преждевременному оптимизатору во мне не нравилось ненужное копирование. Это может еще вернуться, чтобы укусить меня (например, сериализация связи иногда имеет другие потребности, чем сериализация сохраняемости), но пока это не было большой проблемой.

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

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

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

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

Ответ №2:

Разделение вовсе не кошмар, фактически, поскольку я использую MVVM в качестве шаблона проектирования, я очень поддерживаю его использование. Недавно я был частью команды, где я написал компонент пользовательского интерфейса довольно большого продукта, используя MVVM, Который взаимодействовал с серверным приложением, которое обрабатывало все вызовы базы данных и т. Д., И могу честно сказать, что это был один из лучших проектов, над которыми я работал.

В этом проекте был

  • Модель данных (базовые классы без поддержки INotifyPropertyChanged),
  • Модель представления (поддерживает INPC, всю бизнес-логику для связанного представления),
  • Просмотр (только для XAML),
  • Модель передачи (методы только для вызова базы данных)

Я классифицировал модель переноса как одно целое, но на самом деле она была построена как несколько уровней.

У меня также была серия классов ViewModel, которые были оболочками вокруг классов моделей, чтобы либо добавить дополнительную функциональность, либо изменить способ представления данных. Все они поддерживали INPC и были связаны с моим пользовательским интерфейсом.

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

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

Преимущество этого, конечно, в том, что код мгновенно становится более удобным в обслуживании, поскольку вызовы базы данных обрабатывались в моем приложении вызовом службы. это означало, что работа метода service может быть изменена, если возвращаемые данные и требуемые параметры остаются неизменными в пользовательском интерфейсеникогда не нужно знать об этом. То же самое касается пользовательского интерфейса: наличие пользовательского интерфейса без привязки к коду означает, что пользовательский интерфейс можно легко настроить.

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

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

Ответ №3:

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

1) Модель передачи не должна совпадать с моделью данных

Например, у вас есть следующий объект в вашем ADO.Модель данных сетевой сущности:

 Customer
{
    int Id
    Region Region
    EntitySet<Order> Orders
}
 

И вы хотите вернуть его из службы WCF, поэтому вы пишете код следующим образом:

 dc.Customers.Include("Region").Include("Orders").FirstOrDefault();
 

И есть проблема: как потребители сервиса будут уверены, что свойства Region and Orders не являются нулевыми или пустыми? И если у Order объекта есть коллекция OrderDetail объектов, они тоже будут сериализованы? И иногда вы можете забыть отключить отложенную загрузку, и весь граф объектов будет сериализован.

И некоторые другие ситуации:

  • вам нужно объединить две сущности и вернуть их как единый объект.
  • вы хотите вернуть только часть объекта, например, всю информацию из File таблицы, кроме столбца FileContent типа двоичного массива.
  • вы хотите добавить или удалить некоторые столбцы из таблицы, но не хотите предоставлять новые данные существующим приложениям.

Итак, я думаю, вы убеждены, что автоматически сгенерированные объекты не подходят для веб-служб. Вот почему мы должны создать модель передачи, подобную этой:

 class CustomerModel
{
    public int Id { get; set; }
    public string Country { get; set; }
    public List<OrderModel> Orders { get; set; }
}
 

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

Чтобы упростить процесс преобразования моделей, вы можете использовать библиотеку AutoMapper.

2) Рекомендуется, чтобы модель представления не совпадала с моделью передачи

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

  • Уведомление об изменениях свойств с помощью INotifyPropertyChanged интерфейса
  • Уведомление об изменениях коллекции с использованием ObservableCollection класса
  • Проверка свойств
  • Реакция на события представления (с использованием команд или комбинации привязки данных и установщиков свойств)
  • Преобразование свойств, аналогичное {Binding Converter...} , но на стороне модели представления

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

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

Ответ №4:

Мы используем подход, аналогичный тому, что опубликовал Purplegoldfish, с несколькими дополнительными слоями. Наше приложение взаимодействует в основном с веб-службами, поэтому наши объекты данных не привязаны к какой-либо конкретной базе данных. Это означает, что изменения схемы базы данных не обязательно должны влиять на пользовательский интерфейс.

У нас есть уровень пользовательского интерфейса, состоящий из следующих подуровней:

  1. Модели данных: сюда входят простые объекты данных, которые поддерживают уведомление об изменениях. Это модели данных, используемые исключительно в пользовательском интерфейсе, поэтому у нас есть возможность гибко разрабатывать их в соответствии с потребностями пользовательского интерфейса. Или, конечно, некоторые из этих объектов не являются простыми, поскольку они содержат логику, которая управляет их состоянием. Кроме того, поскольку мы используем много сеток данных, каждая модель данных отвечает за предоставление своего списка свойств, которые могут быть привязаны к сетке.
  2. Представления: наши определения представлений в XAML. Для удовлетворения некоторых сложных требований в некоторых случаях нам приходилось прибегать к кодированию, поскольку придерживаться подхода, основанного только на XAML, было слишком утомительно.
  3. ViewModels: здесь мы определяем бизнес-логику для наших представлений. У этих ребят также есть доступ к интерфейсам, которые реализуются объектами на нашем уровне доступа к данным, описанном ниже.
  4. Докладчик модуля: обычно это класс, который отвечает за инициализацию модуля. В его задачу также входит регистрация представлений и других объектов, связанных с этим модулем.

Тогда у нас есть уровень доступа к данным, который содержит следующее:

  1. Объекты передачи: обычно это объекты данных, предоставляемые веб-сервисами. Большинство из них генерируются автоматически.
  2. Адаптеры данных, такие как прокси-серверы клиентов WCF и прокси-серверы для любого другого удаленного источника данных: эти прокси обычно реализуют один или несколько интерфейсов, доступных для ViewModels, и отвечают за выполнение всех вызовов к удаленному источнику данных асинхронно, переводя все ответы в эквивалентные модели данных пользовательского интерфейса по мере необходимости. В некоторых случаях мы используем AutoMapper для перевода, но все это делается исключительно на этом уровне. Наш многоуровневый подход немного сложен, как и приложение. Он должен иметь дело с различными типами источников данных, включая веб-сервисы, прямой доступ к базе данных и другие типы источников данных, такие как веб-сервисы OGC.