Должен ли уровень сервиса возвращать модели представления для приложения MVC?

Предположим, у вас есть проект ASP.NET MVC и вы используете уровень обслуживания, например, в этом руководстве по диспетчеру контактов на сайте asp.net: http://www.asp.net/mvc/tutorials/iteration-4-make-the-application-weakly-coupled-cs

Если у вас есть модели просмотра для ваших представлений, является ли уровень обслуживания подходящим местом для предоставления каждой модели представления? Например, в примере кода сервисного уровня есть метод

    public IEnumerable<Contact> ListContacts()
    {
        return _repository.ListContacts();
    }

Если вместо этого вам нужен IEnumerable, должен ли он находиться на уровне обслуживания или есть еще какое-то «правильное» место?

Возможно, более уместно, если у вас есть отдельная модель представления для каждого представления, связанного с ContactController, должен ли ContactManagerService иметь отдельный метод для возврата каждой модели представления? Если уровень обслуживания не подходит, где следует инициализировать объекты модели представления для использования контроллером?


person erg39    schedule 08.06.2010    source источник
comment
Вы также можете взглянуть на weblogs.asp.net/scottgu/archive/2009/04/28/   -  person Robert Paulson    schedule 09.06.2010
comment
Учебник NerdDinner - худшее место, где можно найти эту информацию. Он отлично подходит для демонстрации функций MVC, но подразумевает ужасную архитектуру.   -  person Aaronaught    schedule 09.06.2010
comment
@Aaronaught, раз уж вы, по-видимому, эксперт, где ваше подробное руководство по правильной архитектуре MVC?   -  person Robert Paulson    schedule 10.06.2010


Ответы (6)


В общем, нет.

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

Просачивая модели представлений на «служебный» уровень, вы размываете свои слои и теперь можете смешать возможные приложения и представления, связанные с тем, что должно быть сфокусировано на обязанностях на уровне предметной области.

person captaintom    schedule 09.06.2010
comment
Том: В этом примере метод контроллера Index () строит представление, которое он возвращает из метода ListContacts (), показанного выше, который возвращает IEnumberable ‹Contact›, так разве модель представления уже не просачивается на уровень сервиса? Это плохой пример? И если да, то где подходящее место для инициализации моделей представления для использования различными представлениями контроллера? - person erg39; 09.06.2010
comment
@ erg39: Разве Контакт не является типом модели предметной области? Если так, то нет. У вас может быть что-то похожее на контакт для вашей модели представления, но также может быть дополнительная информация, относящаяся к одному представлению или набору представлений. Контакт не знает (или не должен) ничего знать о каких-либо конкретных деталях представления. - person captaintom; 09.06.2010
comment
@ erg39: Что касается инициализации моделей представления, контроллер несет ответственность за создание экземпляров новых моделей представлений и проверку / проверку моделей представлений, полученных от представления. - person captaintom; 09.06.2010
comment
Спасибо всем, очень полезно - person erg39; 10.06.2010
comment
Я знаю, что это устарело, но что делать, если вам нужно агрегировать данные для просмотров. Например, если вы хотите показать, сколько пользователей принадлежит к роли в отношениях nm ... У вас есть варианты: вернуть роли, затем получить доступ к Users.Count, что приводит к 1+n запросам, или агрегировать их. с помощью одного запроса в модель представления RoleSummary. - person Sam; 05.03.2016
comment
В большинстве случаев должен Service потреблять ViewModels или мы должны сопоставить его с соответствующей сущностью в Controller перед передачей данных Post в Сервис? - person Levi Fuller; 06.09.2016

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

person duffymo    schedule 09.06.2010

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

Но когда вы приступите к реализации самостоятельно с помощью Entity Framework, MVC, Repository и т. Д., Вы поймете кое-что еще.

Кто-то должен сопоставить модели Entity / DB с ViewModels (DTO упоминается в конце). Должно ли это быть сделано в [A] слое пользовательского интерфейса (контроллером) или в [B] слое сервиса?

Я выбираю вариант Б. Вариант А - нет, потому что несколько моделей сущностей объединяются вместе, образуя ViewModel. Мы не можем передавать ненужные данные на уровень пользовательского интерфейса, тогда как в варианте B служба может играть с данными и передавать только необходимое / минимум на уровень пользовательского интерфейса после сопоставления (в ViewModel).

Но все же давайте выберем вариант A, поместим ViewModel на уровень пользовательского интерфейса (и модель сущности на уровень обслуживания).

Если уровень сервиса должен быть сопоставлен с ViewModel, тогда сервисный уровень должен получить доступ к ViewModel в слое пользовательского интерфейса. Какая библиотека / проект? Viewmodel должна находиться в отдельном проекте на уровне пользовательского интерфейса, и на этот проект должна ссылаться Service Layer. Если ViewModel не находится в отдельном проекте, тогда есть циклическая ссылка, так что не пойдем. Выглядит неудобно, когда уровень сервиса обращается к слою пользовательского интерфейса, но все же мы можем с этим справиться.

Но что, если есть другое приложение пользовательского интерфейса, использующее эту службу? Что делать, если есть мобильное приложение? Чем может отличаться ViewModel? Должен ли Сервис получать доступ к тому же проекту модели представления? Все ли проекты пользовательского интерфейса будут иметь доступ к одному и тому же проекту ViewModel или у них будет собственный?

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

Наконец, есть обсуждение DTO. А также об аннотации данных в ViewModels. ViewModels с аннотациями к данным (Microsoft.Web.Mvc.DataAnnotations.dll) не могут находиться на уровне сервиса, вместо этого они находятся в слое пользовательского интерфейса (но ComponentModel.DataAnnotations.dll может находиться на уровне сервиса). Если все проекты находятся в одном решении (.sln), то не имеет значения, на какой слой вы его разместите. В корпоративных приложениях для каждого уровня будет собственное решение.

Таким образом, DTO на самом деле является ViewModel, потому что в большинстве случаев между ними будет сопоставление один на один (скажем, с AutoMapper). Опять же, DTO по-прежнему имеет логику, необходимую для пользовательского интерфейса (или нескольких приложений), и находится на уровне обслуживания. А ViewModel уровня пользовательского интерфейса (если мы используем Microsoft.Web.Mvc.DataAnnotations.dll) предназначен просто для копирования данных из DTO с добавлением к ним некоторых «поведенческих» / атрибутов.

[Теперь это обсуждение станет интересным, читайте дальше ...: I]

И не думайте, что атрибуты аннотации данных предназначены только для пользовательского интерфейса. Если вы ограничиваете проверку с помощью System.ComponentModel.DataAnnotations.dll, то ту же ViewModel можно также использовать для проверки внешнего и внутреннего интерфейса (таким образом удаляя UI-residing-ViewModel-copy-of-DTO). Более того, атрибуты также могут использоваться в моделях сущностей. Например: с использованием .tt модели данных Entity Framework могут быть автоматически сгенерированы с атрибутами проверки для выполнения некоторых проверок БД, таких как максимальная длина, перед отправкой на серверную часть. Это экономит круговые пути от пользовательского интерфейса к бэкэнду для проверки. Это также позволяет серверной части повторно проверять по соображениям безопасности. Таким образом, модель - это самодостаточный валидатор, и вы можете легко ее передавать. Еще одно преимущество заключается в том, что если проверка бэкэнда изменяется в БД, тогда .tt (считывает специфику БД и создает атрибут для класса сущности) автоматически подбирает это. Это также может привести к сбою модульных тестов проверки пользовательского интерфейса, что является большим плюсом (так что мы можем исправить это и проинформировать все пользовательские интерфейсы / потребителей вместо того, чтобы случайно забыть и потерпеть неудачу). Да, обсуждение продвигается к хорошему дизайну фреймворка. Как видите, все это связано: многоуровневая проверка, стратегия модульного тестирования, стратегия кеширования и т. Д.

Хотя не имеет прямого отношения к вопросу. Упомянутый здесь «ViewModel Façade» должен смотреть ссылку на канал 9 тоже стоит изучить. Он начинается ровно в 11 минут 49 секунд ролика. Потому что это будет следующий шаг / мысль после того, как будет решен ваш текущий вопрос, данный выше: «Как организовать ViewModels?»

И, наконец, многие из этих проблем модели и логики можно решить с помощью REST. Потому что каждый клиент может обладать интеллектом, чтобы запрашивать данные и получать только те данные, которые ему нужны. И он сохраняет модель в пользовательском интерфейсе, нет модели / логики уровня сервера / сервиса. Единственное дублирование будет в автоматических тестах, которые необходимо выполнить каждому клиенту. Также, если есть изменения в данных, некоторые клиенты терпят неудачу, если они не адаптируются к изменениям. Тогда возникает вопрос, удаляете ли вы уровень обслуживания полностью (и модели, которые они несут) или подталкиваете уровень обслуживания к своему проекту пользовательского интерфейса (поэтому проблема с моделью все еще сохраняется), который вызывает REST API. Но с точки зрения ответственности уровня обслуживания они одинаковы.

Также в вашем примере «_repository.ListContacts ()» возвращает ViewModel из репозитория. Это не зрелый способ. Репозитории должны предоставлять модели сущностей или модели БД. Это преобразуется в модели представления, и именно эта модель представления возвращается уровнем сервиса.

person Blue Clouds    schedule 04.07.2012
comment
Разве введение прикладного уровня здесь не является правильным шагом? он будет отображать модели просмотра и сущности предметной области, делая друг друга независимыми и счастливыми. - person sergio; 01.02.2017
comment
В любом случае модели представления и сущности предметной области независимы. Является ли прикладной уровень новым слоем только для отображения объектов? также думали ли вы о слое пользовательского интерфейса и сервисе доступа к этому уровню приложения? потому что в конце им все еще нужен доступ к своим моделям. Или, другими словами, что решит дополнительный слой? - person Blue Clouds; 02.02.2017
comment
Какое прекрасное объяснение ... идеально. Это означает, что уровень сервиса может либо возвращать модель представления, либо он также может возвращать DTO. Потому что мы будем создавать ViewModel только тогда, когда нам нужно объединить несколько DTO. Я прав, @BlueClouds? - person Ankush Jain; 21.05.2018
comment
@AnkushJain: мы создадим ViewModel только тогда, когда нам нужно объединить несколько DTO. Нет. Мы создадим ViewModel, когда view понадобится. DTO - это носитель (объект передачи данных) от объекта БД к ViewModel. ViewModel может быть копией DTO. А ViewModel уровня пользовательского интерфейса (если мы используем Microsoft.Web.Mvc.DataAnnotations.dll) предназначен просто для копирования данных из DTO с добавлением к ним некоторых «поведенческих» / атрибутов. Если мы не используем атрибут / код конкретного пользовательского интерфейса в модели просмотра, тогда нам вообще не нужна модель просмотра, мы можем использовать сам DTO в качестве модели просмотра. С этой точки зрения объединение нескольких DTO в клуб станет еще одним новым DTO. - person Blue Clouds; 25.05.2018
comment
@AnkushJain также логика клуба находится в сервисе (а не вне). Сначала нам нужно распределить обязанности между каждым слоем. Объединение нескольких сервисных вызовов - важная обязанность, делает ли это ваша точка зрения, или это делает уровень сервиса? - person Blue Clouds; 25.05.2018

Это было немного похоже на то, где я работаю - у нас обычно был контроллер, потребляющий некоторые услуги - затем объединяющий возвращенные DTO в 'ViewModel', который затем передавался клиенту - либо через результат JSON , или связанный в шаблоне Razor.

Дело в том, что примерно в 80% случаев отображение DTO на ViewModel было 1-1. Мы начинаем двигаться в направлении «Где необходимо, просто используйте DTO напрямую, но когда DTO и то, что нам нужно в нашем клиенте / представлении, не совпадают - тогда мы создаем ViewModel и выполняем сопоставление между объектами по мере необходимости».

Хотя я до сих пор не уверен, что это лучшее или правильное решение - так как оно приводит к горячим дискуссиям на тему: «Неужели мы просто добавляем X в DTO, чтобы удовлетворить потребности представления?»

person e82    schedule 25.07.2013
comment
Если вы сохраняете DTO на уровне обслуживания и используете его как ViewModel в пользовательском интерфейсе, куда вы помещаете проверку (DataAnnotation, FluentValidation или что-то еще)? Я нахожусь в той же ситуации 80% 1: 1 DTO для VM, поэтому не уверен, что мне делать. - person Iztoksson; 07.03.2016

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

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

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

person Aaronaught    schedule 08.06.2010
comment
В примере уровень сервиса - это абстракция между контроллером и уровнем репозитория, заявленное намерение которого должно использоваться для проверки перед передачей данных в репозиторий. Он также содержит метод ListContacts () выше, а также метод GetContact () для получения одного объекта Contact, который используется контроллером для привязки представлений. Так что, похоже, для упомянутого сервисного уровня уместно знать о списке моделей представления, которые могут понадобиться полностью продуманному контроллеру, верно? - person erg39; 09.06.2010
comment
@ erg39: Если ваш сервис выполняет валидацию, то это фундаментальная часть модели. Он не обрабатывает логику представления и не должен быть связан с моделями представления. - person Aaronaught; 09.06.2010
comment
@Aaronaught: Где, по вашему мнению, будет подходящее место для инициализации моделей просмотра? Должен ли он быть отдельной абстракцией, например, сервисным слоем модели представления? - person erg39; 09.06.2010
comment
@ erg39: Обычно это часть логики контроллера. Если логика очень сложна, ее обычно делегируют слою сопоставления (или AutoMapper). - person Aaronaught; 09.06.2010
comment
Нет смысла описывать программный уровень как физически отдельный. Слой, разделенный вызовами метода SOAP для веб-службы по сравнению с тем же пространством памяти, не имеет значения и является архитектурной деталью. Хотя это может повлиять на топологию слоя, это не должно повлиять на логическое разделение. - person Robert Paulson; 09.06.2010
comment
@ Роберт: Я не знаю, как ты можешь так говорить. Физически разделенные компоненты не ведут себя так же, как логически разделенные компоненты. Задержка разная. Условия ошибки разные. Асинхронные операции полностью отличаются. Вы не можете выполнять отложенную загрузку через веб-службу. архитектура системы появляется раньше кода и влияет на то, как код должен быть написан, это не какая-то запоздалая мысль или тривиальная деталь. - person Aaronaught; 09.06.2010
comment
OP, похоже, не понимал, что такое сервис (уровень), и ваш ответ в этом отношении не особенно помог. Я сказал, что проблемы реализации не должны влиять на (логическое) разделение программных уровней. Архитектурный стиль определенно влияет на код, но я никогда не говорил, что архитектура не важна, поэтому, пожалуйста, не вкладывайте слова мне в рот. Добрый день. - person Robert Paulson; 10.06.2010

Привет, я вижу здесь очень хорошие ответы. А для себя я делаю другой подход. У меня есть типы моделей, одна - это модель просмотра, а другая - общие модели. Модели просмотра остаются на уровне пользовательского интерфейса, а общие модели остаются в отдельном проекте. Теоретически общие модели можно использовать в любом программном обеспечении, потому что они являются автономными. Эти модели обеспечивают некоторую абстракцию, если вы хотите вернуть определенные данные со своего уровня обслуживания или если вам нужно что-то конкретное из вашего репозитория. Я действительно не знаю, хороший ли это подход, но он так хорошо работает в моих проектах. Например, когда мне нужно предоставить некоторую информацию для создания новых объектов в базе данных, я могу использовать общие модели непосредственно на уровне обслуживания, это избавляет меня от некоторых сложностей. Совместно используемые модели иногда необходимо отображать, но вы можете убедиться, что ваша служба не пропускает ненужные данные в пользовательский интерфейс. Вы можете рассматривать общие модели как расширение, но не для построения логики пользовательского интерфейса с его помощью, для этого у вас должны быть модели просмотра. Вы можете комбинировать модели просмотра с этими общими моделями, чтобы сэкономить время при использовании наследования. Общие модели должны быть нейтральными и не содержать никакой логики.

person Wilmar Arias    schedule 10.11.2020