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

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

Теоретически, в игре есть разные модели:

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

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

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

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

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

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

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

4 ответа

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

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

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

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

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

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

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

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

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

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

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

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

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

И есть проблема: как потребители услуг будут уверены, что Region а также 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) Рекомендуется, чтобы View Model не была такой же, как Transfer Model.

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

  • Уведомление об изменениях свойств с использованием INotifyPropertyChanged интерфейс

  • Уведомление об изменениях коллекции с помощью ObservableCollection учебный класс

  • Проверка свойств

  • Реакция на события представления (используя команды или комбинацию привязки данных и установщиков свойств)

  • Преобразование свойств, аналогичных {Binding Converter...}, но на стороне взгляда модели

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

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

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

Этот проект имел

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

Я назвал модель Transfer как одну вещь, но на самом деле она была построена в несколько слоев.

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

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

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

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

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

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

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

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

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

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

  3. ViewModels: Здесь мы определяем бизнес-логику для наших представлений. Эти ребята также имеют доступ к интерфейсам, которые реализованы сущностями на нашем уровне доступа к данным, описанному ниже.

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

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

  1. Объекты переноса. Обычно это объекты данных, предоставляемые веб-сервисами. Большинство из них являются автоматическими.

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

Другие вопросы по тегам