Случалось ли вам делать рефакторинг "толстых" контроллеров? Приходилось ли создавать многоэтажные модели представлений? Добавлять в представление данные и переписывать при этом код контроллера? Казалось ли вам, что что-то идёт неправильно?
Причина в том, что многие MVC-фреймворки не вполне следуют шаблону MVC, а люди, использующие их, сами того не замечая, ещё больше отклоняются от него. Казалось бы, он довольно прост, и описан в Википедии, но раз за разом возникают проблемы его понимания.
Взгляните на это классическое действие "толстого" контроллера:
public ActionResult AddComment(int articleId, string content)
{
// бизнес-логика
var article = ArticleRepository.Instance.GetById(articleId);
if (article == null)
return View("ArticleNotFound");
var comment = new Comment
{
ArticleId = articleId,
Content = content
};
CommentRepository.Instance.Add(comment);
//формирование модели представления
var viewModel = new ArticleViewModel
{
Article = article,
Comments = CommentRepository.Instance.GetByArticleId(articleId)
};
return View("Article", viewModel);
}
И на этот шаблон представления:
@Model ArticleViewModel
<header><h1>@Model.Article.Title</h1></header>
<article>@Model.Article.Content</article>
<ul>
@foreach (var comment in Model.Comments)
{
<li>@comment.Content</li>
}
</ul>
Какие здесь есть проблемы?
- Конечно, бизнес логика в контроллере. Многие с ней умеют бороться, и выносят её с переменным успехом в сервисы предметной области. Здесь обычно людей останавливает проблема понимания термина Model в MVC.
- Зависимость кода контроллера от того, что кроме самой статьи мы хотим отобразить на её странице. Представление беспечно: оно полностью уповает на то, что контроллер передаст ему все нужные данные. Это следствие непонимания термина View.
И такие ситуации возникают не только в проектах на ASP.NET MVC, а почти на любых MVC-фреймворках. Чтобы бороться с ними, нужно их понять.
Проблемы понимания MVC
Model — не схема БД, не сущность и не ORM, а модель предметной области
Очень часто моделью называют классы, отображающиеся на сущности схемы БД. Однако в рамках MVC этот термин имеет другой смысл. Model — это модель предметной области, содержащая всю бизнес-логику приложения. В том числе сущности, различные сервисы, репозитории, фабрики и многое другое. Модель хранит состояние и может его изменять в зависимости от действий над ней.
Часть функционала модели (или весь) может быть даже реализована с помощью внешних сервисов, но с точки зрения контроллера и представления это не важно: модель предоставляет им API: классы данных и сервисы, имеющие методы для каждого действия, которое мы можем совершить с моделью. Модель может существовать в отрыве от нашего веб-приложения, и поэтому её обычно стоит реализовывать в виде одного или нескольких отдельных проектов.
В контексте нашего примера стоит создать класс, предоставляющий возможность управления статьями (в частности, добавление комментария) и, желательно, соответствующий интерфейс:
public interface IArticleService
{
// Другие методы
// Об успешности операции мы можем узнавать либо из исключений,
// которые может выбросить этот метод, либо из возвращаемого им значения.
void AddComment(string commentContent);
}
View — не анемичный шаблон, а активный инструмент представления
В веб-разработке часто под View понимается шаблон со специальной разметкой, куда должны быть подставлены данные, предоставленные ему контроллером. При этом он сам не обращается к модели предметной области и не получает оттуда данные. Это приводит к тому, что контроллер должен подготовить для него данные в специальном виде (это вынуждает его нарушать принцип единственной ответственности). А если нужно добавить какую-то информацию в шаблон, приходится менять свойства модели представления и код контроллера или провайдера модели представления (если таковой используется).
Модель представления при этом является обособленным классом, и наполняется не связанными по смыслу свойствами, совокупности которых нельзя даже дать связного названия, кроме как %PageName%ViewModel
(ещё один плохой признак).
Но в описании MVC говорится, что представление имеет связь с моделью, и может запрашивать у неё данные, а некоторых случаях даже менять состояние модели! (Но от последнего, по моему мнению, по возможности лучше отказаться.)
Если следовать этому правилу, для передачи данных в представление не понадобится модель представления с большим количеством свойств. Скорее всего её вообще не будет, или это будет идентификатор необходимой сущности или сама сущность в крайнем случае. Остальные данные, связанные с ней, представление должно получить само через предоставленные через внедрение зависимости сервисы, принадлежащие Model.
Если многим представлениям приходится схожим образом обрабатывать данные перед показом, могут быть созданы другие классы, помогающие им в этом. Это могут быть не только "Helper"-классы. И, хоть они и не будут шаблонами разметки, они все равно будут относиться к слою View.
Controller — не место для бизнес-логики и подготовки данных для отображения
Контроллер, а в частности его действие (в веб-разработке) является конечной точкой маршрутизации запроса. Всё, что должно сделать действие, это:
- Выполнить валидацию запроса (опционально)
- Оповестить модель (если это необходимо), и получить ответ (тоже не всегда обязательно)
- Отобразить представление, передав ему только действительно важные данные от модели
(В некоторых вариантах MVC с активной моделью, не относящихся к веб-разработке, может отсутствовать последний пункт, так как представление подписывается на изменения в модели, и изменяется автоматически.)
Если придерживаться этого, контроллеры будут оставаться действительно тонкими, и вам не придется для этого делать уловки вроде %PageName%ViewModelProvider
, чтобы собрать нужные данные для представления.
Внедрение зависимостей в ASP.NET MVC
Хорошо. Но для того, чтобы следовать всему этому, нужно внедрить зависимости в контроллеры и представления. Как это сделать в ASP.NET MVC? Широко распространена практика внедрения в контроллер через свойства. Эту возможность предоставляют многие библиотеки вроде Autofac или Ninject, но есть и другие варианты, которые следует знать.
Внедрение зависимостей в контроллер через конструктор
Внедрить зависимость в контроллер через конструктор можно двумя способами:
- Используя Autofac или другой инструмент внедрения зависимостей с поддержкой этой функции.
- Написав свою собственную фабрику контроллеров, и использовав внутри неё любой инструмент для разрешения зависимостей.
Внедрение зависимостей во View через публичные свойства
В основной линейке фреймворков ASP.NET MVC нет возможности внедрить зависимости в конечный класс представления, так как этот класс не существует до тех пор, пока оно в первый раз не потребуется. В этот момент на лету будет разобран код шаблона, и создастся наследник класса WebViewPage
c перегруженным методом Execute
из одного из предков — WebPageExecutingBase
, содержащим сложный код генерации ответа по шаблону.
Но есть возможность отнаследовать этот класс не от WebViewPage
, а от собственного класса с помощью директивы @inherits
в начале кода шаблона:
(новый класс-предок представления, файл можно разместить рядом с шаблоном)
using System.Web.Mvc;
namespace ASP
{
// здесь я упростил параметр типа ArticleViewModel
// до int-идентификатора статьи
public abstract class ArticlePageBase: WebViewPage<int>
{
// Autofac не требует дополнительных атрибутов для внедрения,
// но другие инструменты могут требовать.
// Источник, из которого представление получит статью
public IArticleRepository ArticleRepository { get; set; }
// Источник, из которого представление получит комментарии к ней
public ICommentRepository CommentRepository { get; set; }
// Рекомендательный сервис, выдающий список статей,
// имеющих сходство с текущей
public IArticleRecomendationService RecomendationService { get; set; }
}
}
(Razor-шаблон представления)
@inherits ArticlePageBase
@{
// получение нужных данных из предоставленных сервисов
var article = ArticleRepository.GetById(Model);
var comments = CommentRepository.GetByArticleId(Model);
var recommendedArticles = RecomendationService.GetRecomendations(Model);
}
@*Вывод полученных данных*@
К сожалению, здесь невозможно сделать внедрение через конструктор, так как генерируемый наследник не будет его использовать, что вызовет исключение во время выполнения. Но есть возможность внедрить зависимости через свойства. Это может сделать Autofac и, возможно, другие инструменты.
Логику получения и подготовки данных для отображения, если она слишком сложна, можно написать, перегрузив метод InitializePage
предка представления, и сохранив полученные данные в защищённые поля, которые можно использовать в шаблоне:
public abstract class ArticlePageBase: WebViewPage<int>
{
public IArticleRepository ArticleRepository { get; set; }
// Свойство, используемое в шаблоне для отображения данных
protected Article Article;
protected override void InitializePage()
{
Article = ArticleRepository.GetById(Model);
}
}
@inherits ArticlePageBase
<h1>@Article.Title</h1>
или прямо в шаблоне в блоке C#-кода (что на мой взгляд даже лучше, если нет необходимости писать дополнительные методы для преобразования данных модели в подходящий формат):
@inherits ArticlePageBase
@{
// получение нужных данных из предоставленных сервисов
var article = ArticleRepository.Get(Model);
var comments = CommentRepository.GetByArticleId(Model);
var recommendedArticles = RecomendationService.GetRecomendations(Model);
}
<h1>@ArticleService.Get(Model).Name</h1>
<header><h1>@article.Title</h1></header>
<article>@article.Content</article>
<ul>
@foreach (var comment in comments)
{
<li>@comment.Content</li>
}
</ul>
<ul>
@foreach (var recommendedArticle in recommendedArticles)
{
// Вывод ссылки на рекомендуемую статью
}
</ul>
Так же в базовом классе этого представления можно описать несколько защищенных методов для обработки данных, которые можно потом использовать в шаблоне.
Внедрение зависимостей во View в ASP.NET MVC Core
Движок Razor в ASP.NET MVC Core позволяет внедрить зависимость с помощью директивы @inject
в начале шаблона. На самом деле происходит всё то же создание свойства, но уже не в предке представления, а в самом его классе, генерируемом движком:
@model int
@inject IArticleRepository ArticleRepository
@{
var article = ArticleRepository.GetById(Model)
}
<h1>@article.Title</h1>
Но для этого необходимо, чтобы этот интерфейс был зарегистрирован в методе ConfigureServices
класса Startup
:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Некоторые действия
services.AddTransient<IArticleRepository, ArticleRepository>();
// Некоторые действия
}
После всего этого
Используя эти техники, можно упростить код действия контроллера до следующего:
public ActionResult AddComment(int articleId, string content)
{
// Вместо исключений можно анализировать
// возвращаемое сервисом значение. Это не принципиально.
try
{
ArticleService.AddComment(articleId, content);
}
catch (ArticleNotFoundException e)
{
return View("ArticleNotFound");
}
return View("Article", articleId);
}
Теперь он действительно тонкий. На него можно даже написать лаконичные модульные тесты. Вся бизнес-логика находится в модели, а все, что относится к показу — в представлении.
Альтернатива MVC — ADR
Сам шаблон MVC в контексте веб-разработки тоже имеет некоторые недостатки. Обработчики запросов объединяются в виде методов-действий внутри одного класса-контроллера. Это ведёт к тому, что для того, чтобы совершить одно действие, нужно проинициализировать весь контроллер, внедрив в него зависимости, необходимые для всех его действий. Это тоже нарушение SRP. Так же часто возникает проблема правильной группировки действий по контроллерам.
Пол М. Джонс предложил альтернативный шаблон — ADR (реализованный на PHP), некое подобие которого можно встретить в других библиотеках. У этого шаблона есть преимущества перед MVC:
- Замена классов-контроллеров с методами-действиями на отдельные классы-действия. Это дает несколько плюсов:
- Нет необходимости внедрять все зависимости, необходимые для целой группы действий, достаточно лишь тех, которые нужны для конкретного действия.
- Нет дилеммы, как сгруппировать действия в контроллеры: по иерархии страниц или по смыслу, так как действия — отдельные классы. Можно организовать произвольное дерево папок с такими классами.
- Нет необходимости использовать Reflection-механизмы для поиска нужного метода и подстановки параметров. Все данные можно передавать через конструктор наряду с зависимостями. Таким образом у действия остается только один метод — "Исполнить", что приводит все действия в соответствие одному интерфейсу.
- Responder более гибок, чем View в широко известных фреймворках: в зависимости от ситуации он может выбирать, какой шаблон использовать, или вообще ответить в виде XML или JSON или любом другом формате.
Остается ждать или попробовать самим реализовать ADR-фреймворк на .NET.
Всем хорошего кода и тонких контроллеров!
Ссылки
Принцип единственной ответственности (SRP) на Википедии
Внедрение зависимости в контроллер через конструктор с помощью Autofac
Внедрение зависимости в контроллер через конструктор с помощью Castle Windsor
Создание собственной фабрики контроллеров
Внедрение зависимости в представление через свойства с помощью Autofac
Внедрение зависимости в представление в ASP.NET MVC Core
Комментарии (225)
adeptuss
20.11.2017 12:10А как таким способом вызывать асинхронные методы сервисов из представления?
mayorovp
20.11.2017 12:14В ASP.NET MVC — никак. В ASP.NET Core — с помощью ключевого слова
await
.polly5315 Автор
20.11.2017 12:52Да, увы, в ASP.NET MVC не получится, все методы синхронные. Сначала и действия контроллеров были синхронными. Нужно ждать асинхронных представлений или искать другие лазейки.
caballero
20.11.2017 13:20Строго говоря асинхронный метод это когда сервер получает запрос и какой нибудь колбеск а потом по своей инициативе вызывает клиента чтобы отдать ему данные.
В данном случае клиент все равно ждет ответа а await это просто реализация паралелизма. На самом деле это проблема архитектуры IIS — сделать его многопоточным и никакого await не понадобится.
Вот на WebSocket уже можно реализовывать асинхронные запросы. Причем они будут асинхронными даже если сервер будет однопоточным и выполнять запросы по очереди.
mayorovp
20.11.2017 13:32+2Нет, это вовсе не проблема архитектуры IIS. IIS уже многопоточный и таковым быть не перестает. Тут проблема в том, что при большом числе потоков возрастают накладные расходы, потому и является трендом переход на асинхронные архитектуры с простых многопоточных.
Строго говоря асинхронный метод это когда сервер получает запрос и какой нибудь колбеск а потом по своей инициативе вызывает клиента чтобы отдать ему данные.
Нет, к сетевому уровню асинхронность отношения не имеет. Один и тот же запрос клиент может считать синхронным, а сервер асинхронным — на уровне сети синхронность и асинхронность пропадают.
caballero
20.11.2017 13:46на уровне сети синхронность и асинхронность пропадают.
Тогда почему AJAX называбт асинхронным (хотя на самом деле никакой асинхронности там нет — это просто фоновый то есть паралельный запрос)?
На самом деле понятие синхронности относится к взаимодействию двух компонентов (условно называемых клиентом и сервером). Среда передачи сообщения или запроса сути дела не меняет.
lair
20.11.2017 13:50Тогда почему AJAX называбт асинхронным (хотя на самом деле никакой асинхронности там нет — это просто фоновый то есть паралельный запрос)?
Потому что с точки зрения вызывающего кода он асинхронен. Вызвали и забыли.
caballero
20.11.2017 14:04многопоточный и таковым быть не перестает.
зависит от настройки пула. Обычно там один поток.
при большом числе потоков возрастают накладные расходы, потому и является трендом переход на асинхронные архитектуры с простых многопоточных
Перекладывание контекста с одного потока в другой — тоже нехило затратная операция. И это никак не решает ни проблему многопоточности ни время ожидания клиентом ответа. Это просто разгружает основной поток выполнения
для ответа на более быстрые запросы. То есть на самом деле await async это
костыль для исправления недостатков архитектуры IIS.
Вот в яве — каждый сервлет выполняется в отдельном треде и никаких костылей не надо.
lair
20.11.2017 14:16зависит от настройки пула. Обычно там один поток.
Конечно же, нет.
processorThreadMax
Specifies the maximum number of worker threads per processor that ASP can create.
The default value is25
.
Перекладывание контекста с одного потока в другой — тоже нехило затратная операция.
Поэтому везде, где можно, синхронизации контекста стараются избежать. А дальше начинается простая операция балансировки — мы смотрим, какие расходы выше, на многопоточность или на перекладку контекста, и выбираем, что нам важнее.
mayorovp
20.11.2017 14:37Вот в яве — каждый сервлет выполняется в отдельном треде и никаких костылей не надо.
Напомните, сколько запросов в секунду тянет сервер на обычных сервлетах?
caballero
20.11.2017 15:12-1сколько надо столько и потянет. Зависит от ресурсов. Но особенность нынешней ситуации в том что железо сейчас гораздо дешевле труда програмиста.
Посему добавить проца или памяти проще и соответственно дешевле чем заниматься хитроумной оптимизацией. Посмотрите на размеры дистибутивов и требования к памяти и количеству ядер для современных приложений. Счет идет на гигабайты.
Не говоря уже об облаках и прочих виртуализациях где ресурсы динамически распределяются в зависимости от нагрузки — по сути гиганский
await async о котором разработчику не надо парится.
lair
20.11.2017 15:19Не говоря уже об облаках и прочих виртуализациях где ресурсы динамически распределяются в зависимости от нагрузки — по сути гиганский
await async о котором разработчику не надо парится.… это пока разработчик не платит из своего кармана за эти динамически выделенные ресурсы.
caballero
20.11.2017 15:39Платит владелец приложения но это гораздо дешевле чем платить разработчику за оптимизацию а тем более за переписывание приложения на новомодную асинхронку.
lair
20.11.2017 15:40Ну вот некоторым владельцам уже оказалось дешевле платить разработчикам за оптимизацию.
caballero
20.11.2017 15:57-1когда я пишу что невыгодно оптимизировать я имею ввиду ДОПОЛНИТЕЛЬНЫЕ усилия по оптимизации в целом грамотно написанного кода. Особенно всякого рода экономии на спичках или переписывании (а значит и тестировании и отладке) работающего приложения на новый фреймворк только чтобы применить await.
конечно если некий умелец написал цикл в цикле а в внутри цикла sql запрос то конешно другого варианта как переписать нетmayorovp
20.11.2017 16:01Ну а если вместо переписывания на новый фреймворк с самого начала писать на новом фреймворке?
caballero
20.11.2017 16:13даже если так. Вы надеюсь понимаете что недостаточно поставить перед какой угодно функцией await чтобы она стала асинхронной?
И не факт что асинхронность даст существенный прирост производительности с учетом такой дорогостоящей операции
как перекладывание контекста из потока в поток.
Ведь железо становится не только дешевым а и более быстродействующим. Может на обычном диске и есть смысл делать await для чтения небольшого файла но в SSD вряд ли.
Если речь не о фейсбуке то файл будет прочитан и оддан (и соответственно освобожден поток) быстрее чем придет следующий запрос пользака.
mayorovp
20.11.2017 16:25Зависимость обратная: если операция асинхронная, то перед ней нужно писать await. Соответственно, если в каком-то контексте нельзя писать await — значит, любые асинхронные операции запрещены.
caballero
20.11.2017 16:48речь о том что для await функция должны быть соответственно заимплементировна. То есть дополнительная работа а не просто ключевое слово.
mayorovp
20.11.2017 16:53Нет никакой проблемы "соответственно заимплементировать" функцию если все ее зависимости имеют асинхронные версии. Таким образом, проблема асинхронного кода сводится к наличию асинхронных библиотек.
caballero
20.11.2017 17:02если все ее зависимости имеют асинхронные версии.
если. Но не всегда можно обойтихсь готовыми библиотеками.mayorovp
20.11.2017 17:26Как минимум, у TcpServer, TcpClient, UdpClient и NetworkStream есть все необходимые асинхронные операции. А значит, реализовать любой сетевой запрос асинхронным образом — не проблема. Да и у FileStream асинхронные операции тоже присутствуют.
Все плохо только с асинхронным открытием файла и асинхронными DNS-запросами.
lair
20.11.2017 17:32… еще HttpClient, все основные операции поверх SqlClient, и генерящиеся прокси в WCF-ных клиентах. Так что реально покрыто почти все рутинное.
lair
20.11.2017 16:30+1Если речь не о фейсбуке то файл будет прочитан и оддан (и соответственно освобожден поток) быстрее чем придет следующий запрос пользака.
Это пока у вас один пользователь. А когда у вас запросы реально идут в параллель, а под асинхронией у вас лежат не локальные файлы, а сетевые запросы — тут-то профит и начинает чувствоваться.
lair
20.11.2017 16:29+1когда я пишу что невыгодно оптимизировать я имею ввиду ДОПОЛНИТЕЛЬНЫЕ усилия по оптимизации в целом грамотно написанного кода.
Это тоже бывает выгодно. А еще у людей разное понимание "грамотно написанного кода", и часто сначала выпускают неоптимальную, но работающую версию, а потом ее оптимизируют.
Ну и да, когда у нас приложение с нуля, его тем более выгодно писать с учетом асинхронии, потому что весь нынешний .net туда уверенно ползет. И на этом фоне утверждение об облаках, благодаря которым разработчику не надо париться, выглядит еще более смешным.
caballero
20.11.2017 17:17-1весь нынешний .net туда уверенно ползет.
Ну в разработке каждый день возникает новый модный тренд куда все ползут независимо от здравого смысла. Стадное чувство.
Яркий пример — нынешнее яваскриптовое безумие.
А в случае с .NET так и вариантов нет больше. Решили в майкрософте что надо влепить паттерн MVC в ASP.NET и все — других то вариантов нет. Тоже с асинхронкой. Вместо решить вопросы с оптимизацией потоков переложили эту проблему на разработчика.
То есть разраб кроме писать собственно веб приложение должен еще и заботится о его распарралеливании.
lair
20.11.2017 17:31+1Ну в разработке каждый день возникает новый модный тренд куда все ползут независимо от здравого смысла.
Вот только не это "модный тренд", а осознанное решение MS, которые имеют для этого вполне описанные основания.
А в случае с .NET так и вариантов нет больше.
Ну да, и именно поэтому надо уметь писать и работать с асинхронным кодом.
Вместо решить вопросы с оптимизацией потоков переложили эту проблему на разработчика.
А напомните, какие именно вопросы с оптимизацией потоков должен был решить MS?
То есть разраб кроме писать собственно веб приложение должен еще и заботится о его распарралеливании.
Да нет, в общем-то. Вы можете запариться и писать без асинков, asp.net приложение все равно будет работать параллельно. Как и работало с самой первой версии.
caballero
20.11.2017 17:48-1а осознанное решение MS, которые имеют для этого вполне описанные основания.
типа как выкупить у нокии мобильный бизнес и спустить его в унитаз. Или заниматься бесконеным перекладыванием иконок меню в скайпе вместо заставить его нормально работать.
Ну да, и именно поэтому надо уметь писать и работать с асинхронным кодом.
вот и я об этом. Не потому что это разумное решение а потому что вариантов нет.
А напомните, какие именно вопросы с оптимизацией потоков должен был решить MS?
это их продукты. Веб разработчик вообще не должен заморачиватся что там у них за проблемы.
Вы можете запариться и писать без асинков, asp.net приложение все равно будет работать параллельно
тогда нафига await. Сделайте чтобы внутри все работало и не пудрите людям мозги пусть занимаются решением прикладных задач.lair
20.11.2017 17:58Веб разработчик вообще не должен заморачиватся что там у них за проблемы.
Может не заморачиваться. Получит субоптимальный код.
Но вы говорите, что есть проблемы, которые надо было решить. Какие?
тогда нафига await.
Чтобы решить проблему нехватки потоков.
Сделайте чтобы внутри все работало
Сделали. Работает. Но вот беда, людям недостаточно получившейся производительности. Да, тем самым, которые прикладные задачи решают.
mayorovp
20.11.2017 12:13К сожалению, в ASP.NET MVC рендер представления синхронный, что ограничивает в возможности запроса дополнительных данных непосредственно из шаблона. Говорят, в ASP.NET Core все немного по-лучше.
Но я бы не стал даже в ASP.NET Core запрашивать из шаблона основные данные, как вы сделали с
var article = ArticleRepository.GetById(Model)
. Хотя бы потому что в некоторых случаях следует отображать данные которые еще никуда не сохранены и существуют лишь в памяти. Например, при попытке отправки невалидно заполненной формы без AJAX.polly5315 Автор
20.11.2017 12:24Согласен с вами. Да, этот пример просто показывает возможность, он не диктует, как вам поступать. Можно передать в представление сущность статьи. Это нормально (выше было сказано, что ViewModel часто может стать просто сущностью). Но список комментариев к ней, рекомендации и другая подобная информация должна получаться самим представлением.
polly5315 Автор
20.11.2017 12:18Несоответствие именно в том, что конкретно для ASP.NET MVC нет специального места для размещения логики представления, но его можно получить указанными в статье способами. Они снимают с контроллера и модели зависимость от представления.
На счет модели — тут нельзя сказать, как она должна быть организована. Для каждой задачи она своя. Важно лишь то, что в нее должен быть включен и функционал предметной области, не только доступ к данным, и именно к нему и должен обращаться контроллер.lair
20.11.2017 13:48Подождите, я же задал простой вопрос: какое именно определение MVC вы используете, и почему. Без ответа на этот вопрос весьма сложно обсуждать все остальное.
polly5315 Автор
20.11.2017 14:03Да, извините. Я руководствуюсь такой схемой:
М — полнофункциональная модель предметной области. Не имеет связи ни с контроллером, ни с представлением.
V — средство отображения, кроме шаблонов обладающее собственной логикой (порой и сами шаблоны не нужны, если вывод происходит в других форматах). Имеет связь с моделью, но обычно только на извлечение данных. И имеет связь с контроллерами (конкретно здесь не напрямую, а через запрос и его маршрутизацию).
C — набор действий — конечных точек маршрутизации. Имеет связь с моделью: переводит действие пользователя на её язык, и связь с представлением: выбирает, какое нужно отобразить.lair
20.11.2017 14:12А почему вы считаете, что это определение применимо к asp.net MVC, хотя документация и литература (ну вот например Эспозитовский Programming Microsoft ASP.NET MVC) говорят об обратном? Вот одна цитата:
In general, there are at least two distinct models—the domain model and the view model. The former describes the data you work with in the middle tier and is expected to provide a faithful representation of the domain entities. [...] The view model just describes the data being worked on in the presentation. [...] two distinct models logically exist, and coexist, in a typical layered web solution. You might decide that for your own purposes the two models nearly coincide, but you should always recognize the existence of two distinct models that operate in two distinct layers.
Если опираться на это описание, то предлагаемое вами решение для структурирования всех трех компонентов становится полностью неприемлемым.
Я не говорю, заметьте, что это решение неприемлемо вообще. Я говорю всего лишь, что asp.net MVC построен на другой модели, и пытаться навязать ему вашу будет болезненно.
polly5315 Автор
20.11.2017 14:23Согласен с вами. Не совсем применимо. Да, примерно здесь как раз и находится основная несостыковка этого шаблона и их реализации. Тем не менее приблизиться к шаблону можно, и я показываю, как. Приходится писать класс-предок представления или использовать Helper-классы из слоя View. Будет хорошо, если в следующих версиях фреймворка это будет проще. Но достигаемое разделение ответственности между слоями сыграет хорошую службу, повысит тестируемость и гибкость приложения.
lair
20.11.2017 14:28Да, примерно здесь как раз и находится основная несостыковка этого шаблона и их реализации.
Ээээ… нет. Вы почему-то считаете, что в asp.net MVC реализован тот шаблон, который вы описали выше, просто "неправильно". А на самом деле там просто реализован шаблон Model2, и вот он как раз реализован правильно и логично.
Тем не менее приблизиться к шаблону можно, и я показываю, как.
А зачем? Почему просто не использовать Model2?
Но достигаемое разделение ответственности между слоями сыграет хорошую службу, повысит тестируемость и гибкость приложения.
А если ничего не вызывать из View, внезапно, тестируемость становится еще лучше (а разделение как было, так и осталось, потому что весь Model2 — это один слой, presentation, а границей в domain является контроллер).
polly5315 Автор
20.11.2017 14:50Хм, да, вы правы. Используется Model 2. Но разве вам не кажется, что у него есть изъяны?
По поводу тестирования — зависимости, которые внедряются в эти слои, обычно тестируются отдельно, а View тестируется через связку виртуального браузера и Page Object Patternlair
20.11.2017 14:53Но разве вам не кажется, что у него есть изъяны?
Нет, мне кажется, что изьянов у него намного меньше, чем когда мы пытаемся натягивать MVC на серверную разработку.
View тестируется через связку виртуального браузера и Page Object Pattern
Вот именно этого я и хочу избежать. View тестировать тяжело, поэтому чем меньше в нем будет кода, который надо тестировать, тем лучше. Из этого вытекают view-specific models, а дальше, собственно, мы и приходим к классическому Model2.
polly5315 Автор
20.11.2017 15:57А как бы вы отнеслись к простому модульному тестированию класса-предка представления, если не делать его абстрактным, и всю логику поместить в него? Ведь Razor по большому счету просто добавляет метод рендеринга шаблона.
lair
20.11.2017 16:31А как бы вы отнеслись к простому модульному тестированию класса-предка представления, если не делать его абстрактным, и всю логику поместить в него?
… и что будет публичным тестируемым интерфейсом этого класса? Покажите пример.
Но в любом случае, это дополнительные усилия, которых я бы предпочел избежать.
polly5315 Автор
20.11.2017 16:41Например, свойства, в которые будут помещаться «подготовленные» для отображения данные после исполнения метода InitializePage.
lair
20.11.2017 16:46+1Вот вы и получили view model. Теперь выделяем его в отдельный класс, чтобы не возиться с наследниками, и получаем то, с чего начинали.
alexeystarchikov
20.11.2017 14:51М — полнофункциональная модель предметной области. Не имеет связи ни с контроллером, ни с представлением.
Не очень понятно, что такое «полнофункциональная», но да, она не должна иметь ничего общего с другими компонентами
V — средство отображения, кроме шаблонов обладающее собственной логикой
View может обладать своей логикой, но реализовывать ее. Строго говоря, в нем и логики нет, ее реализует средство управления отображением.
C — набор действий — конечных точек маршрутизации.
Нет! базовый класс Controller в ASP.NET MVC — таковым является, потому как определяем какие данные принять и какой view вернуть. Но Контроллер в MVC не является средством маршрутизации.
Вообще, в статье, на мой взгляд, поднято несколько проблем, заслуживающих внимания и пояснения. Но вот пояснения в некоторых случаях только больше путают.
На Хабре было две замечательные статьи по мотивам публикаций Реенскауга, где рассказывается, что именно «прородитель MVC» имел в виду код каждым компонентом системы.
Один
Дваpolly5315 Автор
20.11.2017 15:06Полнофункциональная модель — это когда в ней есть функционал для всех возможных действий над ней. Есть счет — должен быть какой-то класс-менеджер, или что-то иное, с помощью которого можно этот счет открыть, закрыть, пополнить, получить какие-то связанные с ним данные и т.п.
Конечно, в представлении логика есть, просто обычно она предельно упрощена. Нет смысла запрещать ей быть сложнее.
По поводу контроллера как конечной точки маршрутизации — да, в общем описании шаблона нет таких слов, но здесь я везде указываю уточнение «в веб-разработке», а в ней это именно так.
Да, я понимаю. Я разбирал этот шаблон (и других его родственников) достаточно глубоко, и не только в вебе. Конкретно — в десктопных приложениях и в игровой архитектуре. Но это тема для отдельной статьи.
mayorovp
20.11.2017 12:21- Нет необходимости внедрять все зависимости, необходимые для целой группы действий, достаточно лишь тех, которые нужны для конкретного действия.
- Нет дилеммы, как сгруппировать действия в контроллеры: по иерархии страниц или по смыслу, так как действия — отдельные классы. Можно организовать произвольное дерево папок с такими классами.
На самом деле, тут проблемы решают друг друга: надо группировать действия в контроллеры по их зависимостям.
polly5315 Автор
20.11.2017 12:33Очень хорошее замечание, спасибо! Но согласитесь, даже в этом случае не всегда каждое действие такого контроллера будет использовать все зависимости, внедренные в него.
Кстати, есть возможность внедрять «ленивые» зависимости, инициализирующиеся при первом обращении, это тоже снижает нагрузку. Тем не менее с точки зрения принципов SOLID нет никакой необходимости группировать действия. Напротив, индивидуальные чействия чище.mayorovp
20.11.2017 12:44Зато такая необходимость есть с точки зрения организации кода. Вы же тут в посте уже объяснили что идеальное действие — в 9 строчках. Что, на каждые 9 строчек кода создавать отдельный файл?
Нет, можно конечно несколько классов в один файл запихнуть, но тут тоже кто-нибудь возмутится…polly5315 Автор
20.11.2017 12:58Понимаю, я тоже раньше старался не писать слишком мало в одном файле, но в этом нет ничего плохого. Несколько классов в один файл тоже можно поместить, это дело вкуса и руководств по стилю кода, принятых в вашей организации. Так часто делается, например, когда нужно написать целую серию шаблонных классов с одинаковым функционалом, но разным количеством переменных типа.
caballero
20.11.2017 13:40-2Проблема понимания MVC в том что этот паттерн абсолютно неестественнен для веба. Когда решение логичное и очевидное — проблем с его пониманием не возникает. Не помню чтобы в свое время были какие то проблемы с пониманием MVC при разработке десктопных приложений, для который паттерн и был придуман.
альтернативный шаблон — ADR
ADR конечно ближе к здравому смыслу но непонятно зачем что то изобретать да еще и вводить новую терминологию а потом объяснять чем домейн отличается от модели.
На самом деле наиболее разумный подход компонентный. Раз мы уж говорим о .NET то это старый добрый WebForms. Все что майкам надо было сделать — выкинуть визуальный редактор чтобы не мешал верстке (ведь в ASP.NET MVC его все равно нет)
и осталась бы логичная компонентная событийная модель, персистентностью состояния страницы, и с взаимодействием разработчика с компонентами (элементами страницы) через их свойства не заморачиваясь как оно там передается через HTTP.
Остается ждать или попробовать самим реализовать ADR-фреймворк на .NET.
Ну вот я себе компонентный реализовал и вполне доволен.
lair
20.11.2017 13:51Проблема понимания MVC в том что этот паттерн абсолютно неестественнен для веба.
"Классический" — да. Model2, который в asp.net MVC и JSP — совершенно естественнен.
caballero
20.11.2017 14:11Если оно такое естественное то откуда все это бесконечные дискуссии (включая эту) о том как должны взаимодействовать компоненты, должен контроллер передавать данные от модели представлению или не должен и т.д. и т.п. Как то далековато от «естественного». Паттерн по определению должен быть однозначен — иначе это вообще не паттерн хоть модель два хоть модель стопицот.
lair
20.11.2017 14:20Если оно такое естественное то откуда все это бесконечные дискуссии (включая эту) о том как должны взаимодействовать компоненты
Во многом — из непонимания, какой же именно паттерн используется. Еще — из-за эволюции паттерна со временем. Дело, однако в том, что эти дискуссии возникают не только для серверной имплементации паттерна (т.е., того, что вы назваете "вебом"), но и для клиентской, а это как раз изначальное его (паттерна) место.
Я скорее соглашусь с тем, что этот паттерн вообще неоднозначен (или его оригинальная трактовка плохо применима к нынешним реалиям).
mdErrDX5341
20.11.2017 15:07у MVC нет проблем, проблема часто в понимании того что MVC это паттерн для уровня пердставления, а не для всего приложения.
polly5315 Автор
20.11.2017 15:10Да, кстати, именно по этому строение модели всегда поверхностно освещается при обсуждении MVC. Нет каких-то законов, говорящих, какой ей быть, кроме предоставления API
caballero
20.11.2017 15:29И не надо никаких законов. Модель — место где живут бизнес данные и бизнес логика. Посему какой ей быть зависит от реализации. Может быть бекенд в виде сервера приложений а может быть просто БД с хранимками.
Но в вебье приходится применять некую искуствено созданную структуру в виде класса по имени Model. При чем этих «моделей» десятки хотя по сути у приложения одна единственная модель.
mdErrDX5341
20.11.2017 15:38presentation layer
вот здесь находится mvc
domain layer
а здесь находится бизнес логика
data soure layer
а вот тут уже данные и т.д.
Из вашего комментария получается что M из MVC размазано по всему приложению...lair
20.11.2017 15:42Ну если быть совсем занудным, то M из классического MVC находится на границе presentation-domain. То есть для V/C M — это и есть domain layer, точнее, его фасад, за которым спрятано все остальное. Но, повторюсь, это для классического MVC, не для Model2.
mdErrDX5341
20.11.2017 15:49да, полностью согласен, я скорее хотел сказать, что если мы обсуждаем mvc, ну или в нашем случае model2, то паттерны достаточно понятны, а споры скорее происходят из-за того что часто их пытаются наложить на все приложение, хотя они решают конкретные задачи.
caballero
20.11.2017 15:43это просто ваш вариант видения. Именно в этом и проблема паттерна — каждый обясняет его как хочет терминами какими хочет.
Из вашего комментария получается что M из MVC размазано по всему приложению..
Оно никуда не размазано — оно находится в ОДНОМ месте ОДНИМ куском.
caballero
20.11.2017 15:16ну да лично у паттерна проблем нет. Лишь нет понимания для чего он предназначен и всего делов.
mdErrDX5341
20.11.2017 15:13а model2, это грубо говоря адаптация mvc для веб приложений
caballero
20.11.2017 15:21То есть бесконечные обсуждения пробоем применения паттерна только в том что люди не подозревают о существовании некоей сакральной Model2 которая не вызывает вопросов и все ставит на свои места?
mdErrDX5341
20.11.2017 16:15Это говорит о том, что для веб приложений придумали более подходящий паттерн взамен mvc(но в чем то похожий), но почему-то постоянно говорят про mvc и как раз возникает много споров.
caballero
20.11.2017 16:28Отож. Взяли мотоцикл сделали из него автобус и сказали что это модель2. Ну какая разница сколько колес, людей же тоже перевозит.
Но если даже переименовать, проблем это не особо убавит потому как архитектурно эти решения все равно неудачны для веба и все равно возникнут те же вопросы и бесконечные дискусии.
Почему то нет дискусий что такое паттерн например singleton.
И не потому что он проще а потому что там все четко и понятно.
lair
20.11.2017 16:33Но если даже переименовать, проблем это не особо убавит потому как архитектурно эти решения все равно неудачны для веба
… а вот это совершенно не очевидно.
Почему то нет дискусий что такое паттерн например singleton.
Зато есть дискуссии, стоит его применять, или нет. И их не сильно меньше, чем дискуссий про MVC.
И не потому что он проще а потому что там все четко и понятно.
Дадада, особенно в условиях конкурентного доступа и ленивой инициализации.
caballero
20.11.2017 16:55Зато есть дискуссии, стоит его применять, или нет.
вполне логично. Как и дискусия какой применять язык програмирования или технологию. В случае с паттерном MVC монополия. Особенно это касается нового поколения разработчиков которые не представляют что веб и MVC это не то же самое.lair
20.11.2017 16:56В случае с паттерном MVC монополия.
Эээ… что? Где монополия? У меня вот веб-фреймворк безо всякого MVC (и Model2).
mdErrDX5341
20.11.2017 17:13Особенно это касается нового поколения разработчиков которые не представляют что веб и MVC это не то же самое.
Ну почему же? https://en.wikipedia.org/wiki/Architectural_pattern
новое покаление разработчиков могут как минимум перейти по этой ссылке и начать изучение паттернов.
А опытное покаление разработчиков в это время будет проектировать приложения.
Обычно так оно и происходит.
Я как не очень опытный разработчик, читаю комментарии более опытных… так что не какой монополии.caballero
20.11.2017 17:28новое поколение разработчиков могут как минимум перейти по этой ссылке и начать изучение паттернов.
никто никуда не будет переходить. Все будут смотреть на типовые готовые решения которые все на MVC. Даже там где есть варианты как в PHP все равно большинство фреймворков на MVC. Просто потому что так сделал Zend которому пофиг как оно на практике.
mdErrDX5341
20.11.2017 17:55если молодое поколение разработчиков не будет куда либо переходить, это скорее всего по той причине что они пока не учавствуют в проектировании.
Типовые готовые решения на MVC… наверное по тому что такой подход решает большую часть типовых задач. Так как фрейморки созданы для того что бы ускорить разработку.
Если к вашей задаче такое решение не подходит, то вы можете спроектировать его как угодно… Об этом Вам уже ответили.
У меня вот веб-фреймворк безо всякого MVC (и Model2).
VolCh
21.11.2017 08:39Если вы про ZendFramework, то особого его влияния на другие фреймворки незаметно. А создатель Symfony явно заявляет, что Symfony не MVC фреймворк. В Symfony Framework есть, по сути, только контроллеры, которым передаётся запрос и ожидается от них ответ. Модель, представление — опционально и реализуются как угодно.
polly5315 Автор
20.11.2017 16:45+1Одна из причин в том, что Singleton — порождающий шаблон ООП, а MVC — архитектурный шаблон
polly5315 Автор
20.11.2017 13:53Да, MVC не очень хорош для веба. По большому счету тут могут быть полезны разные подходы. Можно ведь делать и одностраничное приложение с логикой представления полностью вынесенной в браузер, оставив на сервере только REST-API. Но чаще хорошо подходят гибридные решения.
caballero
20.11.2017 14:20Можно ведь делать и одностраничное приложение с логикой представления полностью вынесенной в браузер
Такие решения более трудоемкие. Это просто попытка изобразить на клиенте некое подобие десктопного приложения. Но как ни крути для веба все таки проще классический подход — сформировать страницу на сервере и выплюнуть готовую в браузер чем реализовывать сотни API для которых кстати, все равно нужен бекенд — ведь сайт обычно — это просто витрина к серверным данным. Особенно странно нагружать клиента (особенно мобильного) тоннами тяжелого яваскрипта в эпоху дешевых облачных вычислений.
SPA решения оправданы для не небольшого класса задач — каких нибудь браузерных игр или обмена сообщениями в реалтайме типа как в соцсетях… Но подавляющее большинсво сайтов не требуют подобной интерактивности.mdErrDX5341
20.11.2017 18:55В какой то мере я согласен. Да, часто для сайтов SPA не подходит, особенно возникают проблемы с поисковиками.
Но все же это не попытка изобразить на клиенте подобие десктопного приложения.
Часто это нужно для элементов интерфейса которым критична перезагрузка страницы. Допустим для формы ввода комментария который я сейчас набирал.
mdErrDX5341
20.11.2017 21:44По поводу облачных технологий.
Хотелось бы намекнуть на достаточно большие организации у которых очень жесткая политика безопасности…
Как правило такие предпочтут что бы у них сгорела серверная чем перенести данные в облако, как правило в таких компаниях даже покупка платы оперативной памяти это бюрократия, доказательства и т.д.
По этому казалось бы простой ajax запрос для формы обратного звонка снижает нагрузку на сервер, так как не надо формировать полноценную страницу.caballero
20.11.2017 23:01в соответствии с нынешней модой вытаскивать логику на клиента, там все делается аяксом. Поэтому вместо возвращения одним запросом простой html страницы делается стопицот аякс запросов к API которые загрузят сервер намного больше.
lair
20.11.2017 23:05стопицот аякс запросов к API которые загрузят сервер намного больше.
… почему?
caballero
20.11.2017 23:12потому что каждый аякс запрос это полноценный HTTP запрос. Со всеми вытекающими требованиями к ресурсам — парсинг, роутинг, выделение потока, памяти и т.д. и т.п.
калбек-хелл на клиенте и прочее — это само собойlair
20.11.2017 23:17потому что каждый аякс запрос это полноценный HTTP запрос. Со всеми вытекающими требованиями к ресурсам — парсинг, роутинг, выделение потока, памяти и т.д. и т.п.
Зато они могут оперировать существенно меньшими данными. И/или вообще не строить HTML (который тоже жрет ресурсы).
А еще они могут (до определенной степени) идти в параллель, что делает загрузку страницы субъективно быстрее. Да, я понимаю, что это не серверные ресурсы, но это способ сделать клиент быстрее теми же серверными ресурсами.
caballero
20.11.2017 23:59Зато они могут оперировать существенно меньшими данными
если надо отдать энное количество данных то их по любому надо отдать. Да еще и в json заворачивать.
И/или вообще не строить HTML
HTML по любому надо отдать. и сколько там того HTML.
страницы субъективно быстрее
а еще они дергаются и прыгают за каждым движением мыши как судорожные.
клиент быстрее теми же серверными ресурсами
не теми же а БОльшими. Плюс нагружает клиента. Плюс трудоемкость разработки.
Собрать HTML страницу вместе с данными и выплюнуть на клиента проще и ресурсов это забирает меньше. но главное проще писать. при нынешних скоростях трафика и стоимости железа — остальное экономия на спичках. А танцы с бубном с каким нибудь ангуляром плюс стопицот API которые все равно надо написать и сходить с ними в Бд и т.д. это намного более трудоемкая задача.
Особенно если страница сложная и данных не ней разные и зависящиее друг от друга.
lair
21.11.2017 00:06если надо отдать энное количество данных то их по любому надо отдать.
Иногда отдача кучи маленьких кусков данных дешевле, чем одного большого.
HTML по любому надо отдать. и сколько там того HTML.
Если речь идет, например, о большой таблице с данными, то HTML может быть много. А если отдавать под клиентское отображение, то можно отдать одну строчку шаблона и данные.
не теми же а БОльшими.
Не доказано.
при нынешних скоростях трафика и стоимости железа — остальное экономия на спичках.
Это вам так кажется. Иногда ускорение (клиентское, click-to-last-byte) может быть на порядок.
Особенно если страница сложная и данных не ней разные и зависящиее друг от друга.
… а теперь представьте, что у вас на странице два несвязанных грида, в каждом из которых пейджинг, фильтрация и сортировка. И в одном из них поменяли страницу.
mdErrDX5341
20.11.2017 23:20если я организую точный запрос к сервису, с только определенной целью, то он выполнить определенные действия и отдаст клиенту ответ да, нет...
мой комментарий к строчкам с которыми я согласен
Но чаще хорошо подходят гибридные решения.
которые я как понимаю Вы упустили.
VolCh
21.11.2017 08:41SPA решения оправданы, как ни странно, для веб-приложений, а не для сайтов.
caballero
21.11.2017 10:14можете назвать принципиальную разницу? Ну кроме «веб-приложение» круче звучит
mayorovp
21.11.2017 10:44Эталонный сайт:
- построен вокруг контента;
- рассчитан на то что пользователь уйдет с него как только закончит потреблять контент;
- имеет большую долю входящих переходов по прямой ссылке.
Эталонное приложение:
- построено вокруг взаимодействия с пользователем и решения его задач;
- рассчитано на то что пользователь будет долгое время оставаться в нем или даже на "вечную" работу в фоновой вкладке;
- для большинства пользователей начинается со стартового экрана.
caballero
21.11.2017 12:04как я и говорил — по сути разницы никакой. Чисто словоблудие.
То что вы перечислили — это то же самое только в одном месте называется «решать задачи пользователя» в другом — «потреблять контент».
Неудивительно что мы в очередной раз мусолим темы типа что такое MVCVolCh
22.11.2017 00:02+1Ключевое слово — взаимодействие. У эталонных сайтов его нет.
mdErrDX5341
22.11.2017 00:10-1Кстати пинать на мой взгляд глупо, нет понятия веб приложения и веб сайтов, это по факту взаимозаменяемые слова, различия существуют только в цели, но автор предыдущего коммента его не раскрыл, так что давайте разъесняйте чем "веб приложение", отличается от "веб сайта", на мой взгляд это просто приложения.
VolCh
22.11.2017 22:30Можете считать веб-сайт простейшим веб-приложением типа "Hello world". Вы же не будете устанавливать MVC-фреймворк (настоящий :) ), если всё, что вам нужно — это вывести "Hello world" в консоль?
mdErrDX5341
23.11.2017 23:05а вот тут я тоже не соглашусь…
для меня веб сайт это когда apache отдает файлы по заданному адрессу, а веб приложение это все что имеет логику и построение ответа на запрос пользователя. И если что то формирует ответ в зависимости от запроса пользователя это уже приложение, а не просто отдает html файл. А имя веб-приложение дано так как оно определяем контекст. Но можно сказать и без контекста, приложение.VolCh
24.11.2017 18:59Apache тоже формирует ответ в зависимости от запроса пользователя. То же кэширование взять: 304 отдать или 200 с контентом — чем не логика?
Magic_B
20.11.2017 15:12Полагаю, что такой уровень абстракции, как MVC использовать можно как душе угодно. Споры тут неуместны. Готовлю статью как раз на эту тему. Я разобрался как можно точно, без условностей определить как называть контроллер, какие там будут действия, в т.ч. какие нужны зависимости. Каждый для себя определяет такую логику. Кто-то интуитивно, кто-то строит аргументированный вариант использования. В общем случае, усложнять не стоит. Не стоит, например, внедрять во View сервисы доступа к данным, тогда что должен делать контроллер!? Мухи и котлеты — не логично.
polly5315 Автор
20.11.2017 15:19Как раз, если представление сможет само позаботиться о себе, контроллер перестанет делать эту работу за него. И у него останется вполне себе понятная задача: изменить состояние модели (если это нужно) и выбрать представление для показа.
На счет споров — да, отходить от шаблона можно, ничего такого в этом нет, это может быть нужно по разным причинам. Я здесь не для споров, а для того, чтобы помочь кому-то посмотреть глубже в эту абстракцию, увидеть ее суть.Magic_B
20.11.2017 15:29Как раз, если представление заботиться само о себе — это уже не MVC, насколько я могу судить… С Вашей логикой, лучше было бы заюзать Pipeline… Я и заюзал бы сам, но придется много кода писать, потому предлагаю четко определить, что такое патерн, шаблон, модель, вьюшка, контроллер и экшн. Когда такие определения появятся, как опора для становления логики использования, можно и писать приложение. Для меня все началось с Castle MonoRail. Весь путь, который был проделан с этой точки, я смог испробовать со всех сторон, и точно могу сказать, что вариантов миллион, но нужна, повторюсь, четкая опора для рассуждений. К сожалению, такой теоритической опоры Вы не предоставили в полной мере. В любом случае, спасибо за статью! Успехов Вам!
caballero
20.11.2017 23:08Я разобрался как можно точно,
Приветствую ваш оптимизм но нет. В этом вопросе вообще невозможно разобраться. Уж как нибудь бы уже разобрались за последние лет 15.
Не стоит, например, внедрять во View сервисы доступа к данным, тогда что должен делать контроллер!
То что ему положено — контролировать, то есть передавать команды. Контроллер должен указать предствлению что именно оно должно отрендерить а представление уже само знает какие данные ему для этого надо и где их брать.
lair
20.11.2017 23:18Контроллер должен указать предствлению что именно оно должно отрендерить а представление уже само знает какие данные ему для этого надо и где их брать.
… и как же выглядит "что именно отрендерить"?
(впрочем, этот разговор имеет смысл только в рамках конкретного паттерна. Вы сейчас о MVC или Model2?)
caballero
21.11.2017 00:01и как же выглядит «что именно отрендерить
Отрендери профиль пользователя с id=2
А представление уже знает как искать пользователя какие поля выбрать откуда подтянуть аватар там и все такое.
lair
21.11.2017 00:06Отрендери профиль пользователя с id=2
И в чем тогда функция контроллера? Который уже, благодаря роутингу и модел-биндингу, получил ровно эту команду.
polly5315 Автор
21.11.2017 00:21Если запрос был именно таким, то это частный случай, когда контроллеру только и остается, как передать управление представлению. Нормальная ситуация.
Если же запрос был каким-то другим, например, «сохранить профиль пользователя», то контроллер сначала повоздействует на модель, и по успеху операции решит, «Отрендерить профиль пользователя с id=2» или показать сообщение об ошибке.lair
21.11.2017 00:25Если же запрос был каким-то другим, например, «сохранить профиль пользователя», то контроллер сначала повоздействует на модель,
Input model binding + вызов сервиса. Однострочник.
и по успеху операции решит, «Отрендерить профиль пользователя с id=2» или показать сообщение об ошибке.
Угу. Отрендерить профиль, учитывая PRG, это тот же экшн, что и выше. А (генеричное) сообщение об ошибке обрабатывается стандартным хэндлером.
Суммарно три строчки кода на контроллер.
VolCh
21.11.2017 09:26В таком случае, имхо, контроллер должен передать представлению как параметр метода render или анлогичного этого пользователя, а уж всё остальное (например, права текущего пользователя для активации кнопки «забанить») представление должно вытаскивать само из инжектированных сервисов.
caballero
21.11.2017 10:11а если нет метода рендер и инжекции? Одна из причин непонимания сути — углубление в, не имеющие к отношения к делу, частности.
Еще раз — контроллер предает представлению команду с указанием какой пользователь должен быть показан а не лазит в Бд и другие ресурсы за собственно данными.
А еще в некоторых случаях модель сама может сообщать представлению что данные поменялись и надо обновится.VolCh
22.11.2017 00:05Именно, команду с указанием пользователя. Не идентификатора, а самого пользователя (в рамках бизнес-модели)
Magic_B
21.11.2017 10:15Знаете, Вы натолкнули меня на мысль: Не слишком ли много в данном патерне MVC неопределенностей? Может быть, тогда это плохой патерн? Если рассматривать вопрос с позиции однозначности применения...
caballero
21.11.2017 11:58Он не плохой. Просто не по делу применяется в вебе.
На самом деле этот паттерн — это как бы общий принцип а не руководство к програмированию. Простыми словами MVC можно описать так — мухи отдельно, котлеты отдельно.
То есть модель (данные), место где эти данные предьявляются пользователю (представление) и та часть приложения которая непосредственно принимает команды управления от пользователя (контроллер) не должны смешиватся в кучу.
С точки зрения какая часть с какой взаимодействует — то на самом деле все части могут взаимодействовать. Как на классическом рисунке с википедии где все стрелки двунаправлены.
А еще проблема непонимания паттерна возникает когда человек не понимает сути ООП как такового. Поэтому ему и непонятно какая часть что должна выполнять.Magic_B
21.11.2017 12:03Про мух я выше писал. А не могли бы Вы подробнее (белее или менее) раскрыть суть ООП, применительно к шаблону проектирования MVC.
caballero
21.11.2017 12:18ООП я упомянул в том плане что если человек не умеет мыслить в стиле ООП то есть объектами и сущностями то ему сложно сориентироватся какая часть патерна что должна делать, то он ищет некую подробную инструкцию.
Вообще програмирование — процесс творческий и к нему нужны определенные
задатки, как и к например игре на пианино. Ща многие идут в сидящее на аутсорсе ИТ изза заработка не соотносясь с наличием способностей.
Поэтому иногда непросто понять почему человек не понимает очевидных вещей. Паттерн MVC — яркий пример. Для меня он настолько очевиден что даже затрудняюсь понять какая суть тут может быть неясна.
lair
21.11.2017 12:04Простыми словами MVC можно описать так — мухи отдельно, котлеты отдельно.
… это вы описали separated presentation. MVC, конечно, частный случай оного, но надо же тогда и частности описывать.
caballero
21.11.2017 12:22я и описал
модель (данные), место где эти данные предьявляются пользователю (представление) и та часть приложения которая непосредственно принимает команды управления от пользователя (контроллер)
подробнее — это уже конкретная имплементация.
lair
21.11.2017 12:04Архитектурным паттернам вообще свойственно быть размытыми. Иначе бы разработка архитектуры ПО уже давно была простой и понятной дисциплиной.
Ordos
20.11.2017 21:33А как предлагается быть с обработкой ошибок? Ведь одно дело, когда мы полностью сформировали какую-то модель, отдали её представлению, и оно её показало. Но в примере выше, например:
<h1>@ArticleService.Get(Model).Name</h1>
Мы отрендерили h1, вызвали ArticleService.Get, получили exception. Клиенту ушёл битый html.mayorovp
20.11.2017 21:36Нет, клиенту ушло стандартное сообщение об ошибке вместо битого html. Но вопрос, конечно, важный. Я считаю, выносить в представление можно те вызовы которые не могут упасть когда приложение хотя бы частично работает, а страница — правильно написана.
mdErrDX5341
20.11.2017 22:21-1По этому мне именно и нравится разделения приложения на слои, и как мне кажется плохо когда применяя паттерн MVC, М является бизнес логикой приложения, как уже выше писали это фасад над бизнес логикой, проще тестировать, а так же можно аккуратно выводить нормальный ответ пользователю даже если и произошла ошибка, но в плане реализации лучше обратиться к более опытным программистам.
mdErrDX5341
20.11.2017 22:42обращение к тем кто поставил минус)))
давайте решения данной проблемы, на архитектурном уровне)))
lair
20.11.2017 22:57весь MVC живет в одном слое приложения.
mdErrDX5341
20.11.2017 23:15Именно об этом я и писал, я так понимаю что он живет в слое presentation, а собой представляет фасад над уровнем бизнес логики… по этому в нем легко менять поведение при сбоях и т.д. не совсем понимаю где я сделал ошибку в своем утверждении.
lair
20.11.2017 23:19по этому в нем легко менять поведение при сбоях
Вот только поведение домена при сбоях не может само давать осмысленного пользовательского представления, потому что это дело уровня представления. Поэтому обработка (некоторых) ошибок и их визуализация все равно должна быть в presentation.
mdErrDX5341
20.11.2017 23:27Вот вот, именно про это я и писал, а так же использую слова view и presentation, так что бы указать разницу паттерна и слоя приложения. Увы на русском языке эти понятия смешиваются.
я писал
По этому мне именно и нравится разделения приложения на слои, и как мне кажется плохо когда применяя паттерн MVC, М является бизнес логикой приложения, ....
добавлю, так как бизнес логика это другой уровень приложения.
lair
20.11.2017 23:34… и что? На отображение ошибок это все не влияет.
mdErrDX5341
20.11.2017 23:45Влияет конечно, тут я соглашусь, но на уровне presentation во view используя model из mvc проще и легче организовывать адекватный вывод ошибок в зависимости от элемента интерфейса который в данном случае создается, и тестировать этот элемент проще, так как он не тянет за собой всю бизнес логику приложения....
lair
21.11.2017 00:02… проще по сравнению с чем? Это всегда важный вопрос.
mdErrDX5341
21.11.2017 00:07По сравнению с тем когда бизнес логика лежит монолитном в M из MVC
lair
21.11.2017 00:08Извините, но вы сравниваете M из MVC с M из MVC. Что-то не так.
mdErrDX5341
21.11.2017 00:46я вообще не сравниваю, я говорю о разных сущностях… об m из mvc из уровня представления и о бизнес логике. Фасад для бизнес логики в виде m для mvc проще создать и тестировать чем тянуть целый уровень бизнес логики. я к этому клоню.
lair
21.11.2017 00:49Фасад для бизнес логики в виде m для mvc проще создать и тестировать чем тянуть целый уровень бизнес логики.
Это совершенно бессмысленное противопоставление. Чтобы сделать фасад, вам нужно иметь, что за него положить. Будет это "целый уровень" или полтора метода, зависит от сложности домена, а не от чего-то еще.
mdErrDX5341
21.11.2017 01:05Хороший ответ))) соглашусь))) но с другой стороны разрабатывая 1 програмную единицу мне не нужно думать о всем приложении и всех ее зависимостях, что мне позволяет создавать ее не зависимо от всех составляющих огромного приложения
lair
21.11.2017 01:08Разрабатывая "одну программную единицу" вам нужно думать о ее зависимостях. Один конкретный контроллер зависит от его конкретных прикладных (и инфраструктурных) сервисов, вот о них и придется думать. "model как фасад" вам тут никак не помогает (эти сервисы — и есть model в этом понимании), помогает только хорошая инкапсуляция этих сервисов.
mdErrDX5341
21.11.2017 01:23конечно об этом стоит задуматься. Но первым делом я думаю о требованиях в задачах, по этому в данном случае мне проще декомпозировать все на программные единицы так что бы они выполняли требования(ну а требования на небольшие задачи), так что я особых проблем не вижу, но скорее проблема в том что я не следую классическому MVC или Model2.
Прошу если честно указать проблемы в моих, рассуждениях, выводах и комментариях, что бы не путать других людей. Явно я пишу не точно...lair
21.11.2017 01:30Я уже написал выше: проблема вашего суждения в этой ветке в том, что вы сравниваете нечто само с собой.
mdErrDX5341
21.11.2017 01:40Можно немного точнее? я вроде как утверждал что писал о view в mvc и presentation как слое приложения, тоесть я разделял эти два понятия… теперь мне интересно где я ошибаюсь при таком разделении обязаностей кода...
lair
21.11.2017 01:46Я говорю про вашу путаницу с model и "слоем бизнес-логики".
mdErrDX5341
21.11.2017 01:56может я криво пишу… что не исключено, но я пишу о том что M в mvc не является "слоем бизнес логики", и что программная единица построенная на базе паттерна mvc это отдельная единица программного кода. Но в тоже время она использует в любом случае "слой бизнес логики". Но в любом случае это только элемент интерфейса, который в свою очередь не обязан и не должен зависить от "бизнес логики".
lair
21.11.2017 02:01но я пишу о том что M в mvc не является "слоем бизнес логики",
Это ошибка — если мы говорим о "классическом" MVC, конечно.
программная единица построенная на базе паттерна mvc это отдельная единица программного кода.
Нет. "Отдельные" — это view и controller. А model как раз совместно используется доменным слоем и слоем представления.
Но в любом случае это только элемент интерфейса, который в свою очередь не обязан и не должен зависить от "бизнес логики".
Тоже неверно. Если интерфейс не будет зависеть от бизнес-логики, то какой в нем вообще смысл? Ровно наоборот, интерфейс в своей структуре опирается на бизнес-логику, иногда напрямую, иногда через посредников.
Идея MVC, как и других separated presentation-шаблонов в том, чтобы бизнес не зависел от интерфейса. Если вы разорвете еще и обратную зависимость, приложение перестанет выполнять свою функцию.
mdErrDX5341
21.11.2017 02:22такой элемент как список, как галочки, такой элемент как кнопка, такой элемент как всплывающее окно, могу продолжать до бесконечности..., что в вебе, что в десктопных приложениях…
Не чего не напоминает?
Библиотеки или фрейворки не раз повторяют реализацию этих элементов в своей стандартной поставке. А да еще и с тестами.
И идея не оторвать отображение от бизнеса, скорее идея оторвать похожее поведение от бизнес логики.
В интерфейсе же есть такое понятие как эффект меньшей неожиданности, вот от сюда слой и представления...
А так да, можно каждый раз свою кнопку разрабатывать.., нафига нам фреймворки, нафига нам...
lair
21.11.2017 12:07Если вы под "элементами интерфейса" понимаете элементы управления, то да, они не зависят от бизнес-логики. Но! они заодно и не зависят от MVC (в них почти никогда нет полной триады, а в большей части случаев они вообще живут только во view). И это не говоря о том, что элементы управления есть не только в MVC, и даже не только в separated presentation.
mdErrDX5341
21.11.2017 02:43Я кстати замечу что именно в вебе появилось такое понятие как style guide
lair
21.11.2017 12:07Можно подтверждающую цитату?
mdErrDX5341
21.11.2017 21:39При чем здесь цитата.
Давайте с простых примеров(так скажем типичных), допустим нам нужен список новостей и статей, как правило этот список не чем не отличается(минимальные отличия которые можно решить во view), по этому для view это почти одинаковые объекты, допустим нам нужен небольшой список с поиском, модель тоже не отличается, а может нам нужен список страниц для постраничной навигации, так он тоже не отличается, или меню?
Получается что в вебе мы скорее уходим от таких вот подобных элементов?
по поводу styleGuide, я к тому что в свое время для десктопных кроссплатформенных приложений пытались создать интерфейс похожий для самой платформы.
В вебе когда туда пришли маркетологи и поняли что сайт не кого не зацепит, решили творить вместе с дизайнерами огого разные интерфейсы для каждого сайта, от сюда и появился styleGuide для интерфейса каждого отдельного сайта… от сюда все вытекающее.
Но если мы будем пытаться смотреть на сайт как на отдельные страницы, то есть воспринимать их как view, то конечно смысли MVC пропадает… Но в данном случае появляется монолит.
Я опять же не утверждаю на правильность своей мысли, я так же согласен что паттерн MVC это достаточно абстрактная вещь, ну и я не проектировщик приложений))) так что я пытаюсь понять где я ошибаюсь, но в тоже время я не смешиваю слои бизнес логики и presentation, если бы я хотел их смешать, то скорее всего сразу передал во вью функцию в которую можно бросить запрос и получить массив с ответом.
lair
21.11.2017 21:46При чем здесь цитата.
При том, что я хочу видеть основания для вашего громкого утверждения.
допустим нам нужен список новостей и статей, как правило этот список не чем не отличается
Не отличается между чем и чем?
по этому для view это почти одинаковые объекты
Какие объекты одинаковые?
Получается что в вебе мы скорее уходим от таких вот подобных элементов?
Не понимаю вопроса.
по поводу styleGuide, я к тому что в свое время для десктопных кроссплатформенных приложений пытались создать интерфейс похожий для самой платформы.
"Кроссплатформенных" — "интерфейс похожий для самой платформы". Это как вообще?
В вебе когда туда пришли маркетологи и поняли что сайт не кого не зацепит, решили творить вместе с дизайнерами огого разные интерфейсы для каждого сайта, от сюда и появился styleGuide для интерфейса каждого отдельного сайта…
Вы никогда не слышали про различающийся дизайн в разных изданиях? Это было задолго до веба. Да и вообще понятие корпоративного стиля тоже существует далеко за пределами веба.
так что я пытаюсь понять где я ошибаюсь
Вы ошибаетесь там, где постоянно смешиваете в одну кучу совершенно разные термины из разных областей.
но в тоже время я не смешиваю слои бизнес логики и presentation, если бы я хотел их смешать, то скорее всего сразу передал во вью функцию в которую можно бросить запрос и получить массив с ответом.
… а это, заметим, почти то же самое, что предлагается в статье.
mdErrDX5341
21.11.2017 21:56… а это, заметим, почти то же самое, что предлагается в статье.
Вы не читаете о чем я пишу, потому как я как раз пытаюсь сказать что в статье есть смешивания бизнес логики и отдельной части паттерна mvc… ну раз мы его хотим применить к клиент-серверной технологии.
VolCh
21.11.2017 09:36M в MVC является интерфейсом (программным) к бизнес-логике, от которого зависит, как минимум, C. Будет это абстракция, имплементации которой легко подменять для, например, целей юнит-тестирования интерфейса (клиентского) или конкретные классы бизнес-логике — ваше решение.
mdErrDX5341
21.11.2017 21:22в статье и так имплементации.
lair
21.11.2017 21:34В статье не MVC, уж если быть въедливым.
mdErrDX5341
21.11.2017 21:45Согласен, так как MVC не возможно создать на стыке веб-браузера и сервера, ну пока нет нормальной поддержки веб сокетов.
lair
21.11.2017 21:48Согласен, так как MVC не возможно создать на стыке веб-браузера и сервера
Вообще-то, прекрасно возможно — если вы пишете JS-приложение в браузере.
mdErrDX5341
21.11.2017 22:02тогда без веб сокетов объясните как можно сделать паттерн наблюдатель.
lair
21.11.2017 22:08Long polling, например. А чтобы обо всем этом не думать, можно просто взять SignalR, который абстрагирует нас от конкретной сетевой технологии, и получать свои события на клиенте.
Но на самом-то деле, вам не обязателен наблюдатель между сервером и клиентом. Если ваша модель (или ее фасад) расположена на клиенте, то и источником событий может быть клиент. А дальше все просто.
mdErrDX5341
21.11.2017 22:24-1ну своеобразный костыль, если брать запрос-ответ, по этому и model2 достаточно отличается от mvc.
А что бы разбавить это безумие, то добавляются AppConroll..., PageControll..., BundleControll..., RestAPI, Ajax со всеми вытекающими… Я же больше говорю о формировании страницы и многих ее UI элементов, а не пробрасывание на сервере модель во вью, скорее о том что все эти технологии, паттерны надо использовать, так как среда приложений веб-браузер <-> сервер пока так скажем слаба, от сюда и другой архетиктурный паттерн как микросервисы...
lair
21.11.2017 22:27ну своеобразный костыль, если брать запрос-ответ, по этому и model2 достаточно отличается от mvc.
Это не костыль, это способ получить серверные события на клиенте. Работающий.
А что бы разбавить это безумие то добавляются AppConroll, PageControll, BundleControll, RestAPI, Ajax со всеми вытекающими…
А это вообще никак не связано.
скорее о том что все эти технологии, паттерны надо использовать
Какие "эти"?
от сюда и другой архетектурный паттерн как микросервисы...
Нет, микросервисная архитектура не имеет никакого отношения к веб-браузерам и их общению с сервером.
mdErrDX5341
21.11.2017 22:32-1Я думаю прекращать это бесполезные общения, а чем вам не нравится браузер как архетиктурная точка микросервиса или тонкого клиента?
lair
21.11.2017 22:34чем вам не нравится браузер как архетиктурная точка микросервиса или тонкого клиента?
Я не знаю, что такое "архитектурная точка".
mdErrDX5341
21.11.2017 22:38-1вам и не надо)))), продолжайте смешивать бизнес логику с уровнем представления в приложении...
mdErrDX5341
21.11.2017 22:44-1нет, но есть паттерн, но правда пока у вас есть возможность реализовать клиент-сервер-умерло соединение… то увы придется смериться с реальностью, от сюда всякие ajax и веб сокеты)))
lair
21.11.2017 22:48AJAX и веб-сокеты появились не в ответ на умирающие соединения. А те, в свою очередь, начали умирать тогда же, когда появились клиент-серверные приложения поверх БД (то есть задолго до MVC), и продолжают умирать до сих пор, и будут умирать дальше, причем, по мере разрастания распределенных систем, все больше и больше. На существующие паттерны это влияет мало, надо просто понимать зону их применимости.
mdErrDX5341
21.11.2017 22:56-1эх… такие вещи как ajaх и веб сокеты появились тогда когда веб браузер перестал быть приложением для гиков… а стал еденицей маркейтига и когда нужно постоянно обновлять информацию, давать клиенту актуальную информацию, а паттерн такой как MVC и наблюдатель пришелся к месту… и технологии здесь не причем, скорее такие архитектурные паттерны удобны что бы веб отвечал последним стандартам бизнеса...
lair
21.11.2017 22:59такие вещи как ajaх и веб сокеты появились тогда когда веб браузер перестал быть приложением для гиков…
Ничего, что они появились в сильно разное время?
а паттерн такой как MVC и наблюдатель пришелся к месту…
MVC тут только ни при чем, а так все хорошо.
и технологии здесь не причем, скорее такие архитектурные паттерны удобны что бы веб отвечал последним стандартам бизнеса...
Observer — не архитектурный паттерн. А MVC к стандартам бизнеса (не знаю, откуда вы их взяли) имеет мало отношения.
И да, при чем тут умирающие соединения?
mdErrDX5341
21.11.2017 23:07-1а то что умирающее соединение не дает бизнесу как раз таки кидать постоянные сообщения пользователю, но тут я смотрю больше на реалии бизнеса и маркейтинга. Вот от сюда и растут ноги быстро шагающего JS и CSS. И постоянные запросы к серверу именно не устраивают бизнес, а не программистов)))
lair
21.11.2017 23:09а то что умирающее соединение не дает бизнесу как раз таки кидать постоянные сообщения пользователю
Уже выяснили, что прекрасно дает. Точно так же, как раньше умирающее соединение не мешало людям работать с бэкендом.
Вот от сюда и растут ноги быстро шагающего JS и CSS.
Ни JS, ни CSS никак не могут повлиять на соединение с сервером.
И постоянные запросы к серверу именно не устраивают бизнес, а не программистов
Бизнесу на эти запросы искренне положить.
mdErrDX5341
21.11.2017 23:01-1от сюда и такое развите JS и CSS, так как бизнесу нужен результат… а результат это быстрые деньги.
mdErrDX5341
21.11.2017 22:50-1А давайте еще вспомним что не клиент запрашивает данные, а его оповещают..., так что ваши минусы идут вам в карму для меня))) так что...
lair
21.11.2017 22:52А давайте еще вспомним что не клиент запрашивает данные, а его оповещают...
Это верно не во всякой архитектуре.
mdErrDX5341
21.11.2017 23:16-1хороши минусы без ответа…
Клиент не будет ждать когда его браузер запросит актуальную информацию, да и интефейс должен отвечать удобностью и актуальностью информацией…
давай еще минус, прям как девочка))) нажалаловаться надо еще на меня кому нибудь)))lair
21.11.2017 23:27Клиент не будет ждать когда его браузер запросит актуальную информацию,
Да ладно. Сейчас прекрасно ждет.
интефейс должен отвечать удобностью и актуальностью информацией
Актуальность информации — штука очень относительная в любой распределенной системе.
mdErrDX5341
21.11.2017 23:31-1нет, актуальность информации это возможность бизнеса принимать своевременные решения на ситуацию возникшие на рынке, и ваша возможность их предоставления бизнес не волнуют, так как ему нужно здесь и сейчас(а не ваши технологии)...
lair
21.11.2017 23:34Это не отменяет того, что эта самая "актуальность как возможность" — штука относительная. Особенно относительной она становится, когда ты приносишь бизнесу табличку: задежка доставки информации 5с — x k$, задежка доставки информации 1с — 20x k$. Часто говорят "а мы и минуту подождем".
mdErrDX5341
21.11.2017 23:38-1пусть ждут, от сюда и технологии такие как аякс и веб сокет))) а паттерны и слои помогают в этом не увязнуть)))
lair
21.11.2017 23:43пусть ждут, от сюда и технологии такие как аякс и веб сокет
Не, не отсюда. Аякс возник когда захотели обновлять страницу без полной ее перезагрузки, а вебсокеты — когда захотели иметь постоянное соединение с сервером.
mdErrDX5341
21.11.2017 23:52-1а не об этом я писал(правда в контексте бизнеса)? дык в самом начале…
вы вообще читаете что я пишу? или мне продолжать Вас стебать за каждый минус))) я так вспоминаю вы мне даже в личку начали писать, хотелось бы тогда иметь веб сокеты, что бы Себя не утруждать...lair
21.11.2017 23:55а не об этом я писал?
Нет, не об этом. Или опять "пытались написать", но вышло что-то другое.
я так вспоминаю вы мне даже в личку начали писать,
В хранящейся у меня истории диалогов я никогда не писал вам в личку.
mdErrDX5341
22.11.2017 00:05-1Сергей Роговцев lair 10 апреля в 01:56
я тогда еще пытался вам сказать что не хочу поддерживать диалог.
Не, не отсюда. Аякс возник когда захотели обновлять страницу без полной ее перезагрузки, а вебсокеты — когда захотели иметь постоянное соединение с сервером.
так как бизнес требует...
lair
22.11.2017 00:10Сергей Роговцев lair 10 апреля в 01:56
Очень любопытно. У меня этого сообщения нет, есть только ваше от 10 апреля в 02:23. Впрочем, я специально сказал "в хранящейся у меня истории", я не претендую на идеальную память.
так как бизнес требует
Нет, бизнес требует другого. Бизнес требует скорость, или бизнес требует фичи, или бизнес требует снизить расходы на ресурсы, или еще что-нибудь. Терминами "постоянное соединение" бизнес не мыслит.
mdErrDX5341
22.11.2017 00:18-1мыслит, решениями которые он будет принимать в зависимости от ситуации.
Самое интерестнное что теперь вы повторяете мои слова))) Но правда в другой формулировке)))
mdErrDX5341
22.11.2017 00:37-1а теперь еще раз прочитайте мои сообщения про разделения логики, слоях, элементах интерфеса… по ходу у вас уже просто рефлекторно мышечная память на минус, но как в таком положено у вам нет аддекватных ответов)) я Вам продублирую в личные сообщения, для размышления на выходных))) что бы у Вас не развилось логики присутсвующих на тонущих короблях)))
lair
22.11.2017 00:39как в таком положено у вам нет аддекватных ответов
Я однажды уже писал вам, и еще раз повторю: вы практически не задаете осмысленных вопросов.
mdErrDX5341
22.11.2017 00:46-1скорее вы не читаете комментариев и по этому просто цитируете фразы, вместо того что бы прочитать и понять, а после этого вы не осознаете сами комментрии…
вы даже не могли понять когда Вас просили остановится, скорее всего Вы были увлечены своим величием)))
mdErrDX5341
22.11.2017 01:21-1Самое интерестное что Вы повторили мои многие слова или выводы, из комметариев которым сами поставили минус))) читать это по ходу проблема...))) а самое главное что я и не пытаюсь задать вопросов, я пока ставил утверждения… Вы же не гуру, которому я должен задавать вопросы)))
lair
22.11.2017 01:31Самое интерестное что Вы повторили мои многие слова или выводы, из комметариев которым сами поставили минус
Вам кажется.
самое главное что я и не пытаюсь задать вопросов, я пока ставил утверждения…
По большей части, некорректные либо по сути, либо по формулировке, либо и то, и другое.
mdErrDX5341
22.11.2017 01:44-1самое главное что я и не пытаюсь задать вопросов, я пока ставил утверждения…
По большей части, некорректные либо по сути, либо по формулировке, либо и то, и другое.
чтоооооо? вы хоть сами понимаете что пишете, я не задавал вопросов, а спрашивал о правильности своих утверждениях)))
Вы вчитаете или печаете на автомате, считая себя...lair
22.11.2017 01:49я не задавал вопросов, а спрашивал
Уже прекрасно.
о правильности своих утверждениях
Ну вот вам и рассказали, что они некорректны.
mdErrDX5341
22.11.2017 01:57-1это как раз и говорит о том что вы не читали…
я утвержадал, и только пару раз спрашивал у ананимусов…
а утверждал что уровень представления на мой взгляд нужно отделять от уровня вью в моделе mvc, Вы наверное не помните… мои многие примеры с элементами интерфейса. А так же указвал на перевод, так что бы люди не путались о чем я пишу....lair
22.11.2017 02:05а утверждал что уровень представления на мой взгляд нужно отделять от уровня вью в моделе mvc,
Это единственное, что вы утверждали?
(да, нет такой вещи как «уровень вью»)
мои многие примеры с элементами интерфейса.
Я их помню, но я не понимаю, как эти примерв указывают на необходимость этого разделения. Никто не спорит, что надо отделять термины одной системы (слои) от терминов другой системы (компоненты), ну так они и разделены в оригинале.
mdErrDX5341
22.11.2017 02:40отвечу....
Я столько видел over-engineered code, хотя в большинстве случаев просто нужно было взять данные из БД, удобно и быстро показать их пользователю. Все.
это из последних комментариев, я думаю что в данном случае вообще надо забить.
как мне кажется нужно просто в скрипт php вернуть ответ из базы и после этого его в цикле вывести.
mdErrDX5341
22.11.2017 02:45Это единственное, что вы утверждали?
Да Вы проницательны)))
Никто не спорит, что надо отделять термины одной системы (слои) от терминов другой системы (компоненты), ну так они и разделены в оригинале.
Но только не в MVC)))
lair
22.11.2017 11:43Но только не в MVC
… и в MVC тоже. Пока вы находитесь внутри MVC, у вас вообще есть только одна терминологическая система, ее собственная (плюс тактические паттерны, которые с ней не пересекаются), и в этой системе нет термина presentation. Когда вы смотрите на MVC снаружи, оценивая его положение в приложении, у вас, наоборот, есть (должна быть) только другая терминологическая система, стратегическая, в которой есть presentation, но не имеет смысла говорить о view.
Bounded contexts, как обычно.
polly5315 Автор
20.11.2017 22:38Если в контракте сервиса указано, что он может бросать такие-то исключения, и это нормально, то можно ловить их прямо в представлении (если мы можем показать вместо этих данных какую-то альтернативу).
Если же нет, можно воспользоваться перегрузкой методаOnException
контроллера:
protected override void OnException(ExceptionContext filterContext) { // Логирование ошибки filterContext.ExceptionHandled = true; filterContext.Result = RedirectToAction("OperationFailed"); }
mdErrDX5341
20.11.2017 22:47я далек от C# и ASP.NET MVC, вы имеете в виду view или уровень presentation?
lair
20.11.2017 22:58View — это и есть уровень presentation.
mdErrDX5341
20.11.2017 23:04В данном случае я пытась отделить view в mvc от presentation слоя, так как перевод неоднозначен
mdErrDX5341
21.11.2017 09:25вот опять мне не понятен минус.
view как и presentation переводится в данном случае как представление, от сюда и может возникнуть путаница о чем идет речь…
и почему минус хз, хз, хз....
mdErrDX5341
20.11.2017 22:55для меня это решается на уровне представление, что то в духе null object, что бы в template не попадало лишних проверок, да и тестировать на мой взгляд так проще...
lair
20.11.2017 22:58Если в контракте сервиса указано, что он может бросать такие-то исключения, и это нормально, то можно ловить их прямо в представлении
Это слишком много логики в представлении. Ее всю нужно тестировать.
polly5315 Автор
20.11.2017 23:54Если бы она была в контроллере или где-либо еще, ее все равно пришлось бы тестировать.
lair
21.11.2017 00:07Но контроллер тестировать проще, чем представление.
polly5315 Автор
21.11.2017 00:14Не факт.
Во-первых, если эта логика будет в контроллере, там же будет находиться еще оповещение модели о действии пользователя и, возможно, предварительная валидация входных данных.
Во-вторых, если это представление используется в нескольких действиях контроллера или даже в нескольких контроллерах, то при изменении логики отображения приходится переписывать тесты для всех затронутых действий (нарушение SRP в действии).lair
21.11.2017 00:20Не факт.
Факт. Контроллер всегда легче тестировать, чем представление, просто в силу контракта.
Во-первых, если эта логика будет в контроллере, там же будет находиться еще оповещение модели о действии пользователя
Нет в Model2 никакого оповещения модели о действии пользователя.
возможно, предварительная валидация входных данных.
И что?
Во-вторых, если это представление используется в нескольких действиях контроллера или даже в нескольких контроллерах, то при изменении логики отображения приходится переписывать тесты для всех затронутых действий (нарушение SRP в действии).
Ээээ… нет? Что такое "логика отображения", на конкретном примере?
polly5315 Автор
21.11.2017 00:31Нет в Model2 никакого оповещения модели о действии пользователя.
Вы ведь манипулируете моделью прежде, чем отобразить представление?
Что такое "логика отображения"
Мы с вами это уже обсуждали в другой ветке. Это сбор представлением всех недостающих данных и их подготовка к вставлению в шаблон.
lair
21.11.2017 00:34Вы ведь манипулируете моделью прежде, чем отобразить представление?
Нет. Если пользователь производит действие, то оно выражено в input model, на основании которой производится действие на домене, результат которого мапится во view model. Это в asp.net MVC, как частном случае Model2.
Мы с вами это уже обсуждали в другой ветке. Это сбор представлением всех недостающих данных и их подготовка к вставлению в шаблон.
А как так вышло, что у вас одни и те же данные для одного и того же представления могут приходить из разных контроллеров? Можно, опять-таки, на конкретном примере?
polly5315 Автор
21.11.2017 01:17Самый простой пример — сообщение об ошибке или любое другое сервисное представление, которое показывается через
return View("%ViewName%");
. Некоторые из них тоже нуждаются в каких-то данных.
А так же частичные представления, которые вызываются из остальных, и показываются на многих страницах.lair
21.11.2017 01:29Самый простой пример — сообщение об ошибке
Для сообщения об ошибке данными является сама ошибка (либо Exception, либо выделенный Error Model поверх Exception), нет там никакой дополнительной логики ее сбора.
любое другое сервисное представление, которое показывается через return View("%ViewName%");. Некоторые из них тоже нуждаются в каких-то данных.
… например? В моей практике они либо берут данные из данных мастер-пейджа (а за этот трюк я набрал овердофига плюсов на SO), либо их можно завернуть в presentation service и делать так (в экшне):
return _standardViews.Alpha();
В этом случае при изменении этого сервисного представления не надо ни переписывать ни тесты, ни рабочий код.
А так же частичные представления, которые вызываются из остальных, и показываются на многих страницах.
А частичные представления — это как раз дырка в абстракции. Но опять-таки, если они не привязаны к содержимому страницы, то они, скорее всего, являются частью мастера. А если привязаны, то они берут данные из обычной view model.
(в некий момент сложность всего этого цирка зашкаливает, и приходилось переходить на Html Helpers, которые хоть тестировать немножко проще, чем представления)
В asp.net Core, кстати, если я ничего не путаю, эту дырку заткнули view components, у которого разделение компонентов такое же, как у обычной триады.
lair
21.11.2017 01:37… в той же документации на asp.net Core, кстати, явно написано (несмотря на наличие dependency injection во view):
You should try to maintain separation of concerns between your controllers and views. Most of the data your views display should be passed in from the controller.
HackerDelphi
22.11.2017 08:47Согласен — очень правильно.
Читал много, но подытожу ответы здесь, если Вы не против:
- Данные готовит контроллер, но через "толстую" модель или пару доменная модель -> модель вью, а затем передает их в модель.
- Партиалки и/или вью компоненты (а также — таг хелперы) используются либо для отображения какой-либо части вью модели (отдельное комплексное свойство модели) либо же для отображения обобщённых моделй (базовый класс или интерфейс). В целом, подход такой, чтобы партиады (как их не называй) получали "конкретную" модель для отображения, а не были просто кусочком большого представления.
- При необходимости можно из контроллеров отдавать данные для клиентского рендеринга, либо, иногда, куски HTML, которые вставляются в финальный код.
lair
22.11.2017 11:49Для начала, важно отметить, что это все может быть верно только в контексте asp.net MVC, который базируется на Model2. Про другие "MVC" и MVC-фреймворки я говорить не буду.
Данные готовит контроллер, но через "толстую" модель или пару доменная модель -> модель вью, а затем передает их в модель.
Это уже избыточное обобщение. С уверенностью можно сказать, что контроллер передает в представление готовые к представлению (простите) данные. Подготовил ли он их сам, или он только скоординировал репозиторий с маппером — вопрос сильно вторичный.
В целом, подход такой, чтобы партиады (как их не называй) получали "конкретную" модель для отображения, а не были просто кусочком большого представления.
Вот как раз для view components это неверно. Можно вызвать view component "сегодняшняя погода", и он сам получит для себя данные; именно благодаря тому, что у него собственнная триада, это не нарушает паттерна в целом.
При необходимости можно из контроллеров отдавать данные для клиентского рендеринга
… это уже не совсем Model2.
либо, иногда, куски HTML
А это неверно, потому что контроллер должен отдавать некий result, который уже рендерится конвеером. Если мы хотим отдать HTML — значит это view result с данными внутри (или без данных, если он от данных не зависит).
lair
21.11.2017 01:45У этого шаблона [ADR] есть преимущества перед MVC:
Вообще, то ли я криво читаю, то ли все описанное прекрасно можно было сделать на старом asp.net WebAPI + пакет для razor views. D Core, который объединил MVC и WebAPI вообще проблем быть не должно — там уже есть view responses и formatted responses из MVC-контроллеров.
Westernstorm
21.11.2017 14:18В итоге, автор перенес часть логики из контроллера в шаблон представления. Переложил из одного в другое, но в чем выигрыш?
Тонкий контроллер убыстряет разработку? вряд ли
Увеличивает скорость приложения? нет
Я столько видел over-engineered code, хотя в большинстве случаев просто нужно было взять данные из БД, удобно и быстро показать их пользователю. Все.DrPass
22.11.2017 01:23У меня возникло такое же впечатление. Многие архитекторы следуют паттернам ради самих паттернов, хотя это на самом деле не самоцель. Есть один, причем не слишком четкий, критерий качества кода — код должен быть читабельным, понятным и легко сопровождаемым. А паттерны — это лишь инструменты достижения этого критерия. И если следование всем канонам паттерна усложняет код, то этого делать не надо. Более того, надо себя вовремя одергивать, когда в голову приходит любимая мантра архитектора: «Если нагрузка/функционал/что-то ещё будет расти, то вот тут надо сразу заложить масштабирование, балансировку, что-то ещё, чтобы потом не переделывать». Во-первых, надо чётко понимать, когда ожидается наступление этого самого «если», и произойдет ли оно вообще, а во-вторых, все равно зачастую окажется проще потом переделать, чем сейчас навернуть.
lair
Давайте начнем прямо вот с простого вопроса: какое именно определение шаблона MVC вы используете, и почему вы думаете, что asp.net MVC ему соответствует?
Anarions
lair
Чтобы говорить о "несоответствии", надо сначала найти "соответствие". Иными словами, чтобы говорить, что "там-то модель неправильная", нужно сначала найти определение правильной, и объяснить, почему это определение применимо.
polly5315 Автор
Случайно ответил в основную ветку