Понимаю, я тоже раньше старался не писать слишком мало в одном файле, но в этом нет ничего плохого. Несколько классов в один файл тоже можно поместить, это дело вкуса и руководств по стилю кода, принятых в вашей организации. Так часто делается, например, когда нужно написать целую серию шаблонных классов с одинаковым функционалом, но разным количеством переменных типа.
Да, увы, в ASP.NET MVC не получится, все методы синхронные. Сначала и действия контроллеров были синхронными. Нужно ждать асинхронных представлений или искать другие лазейки.
Очень хорошее замечание, спасибо! Но согласитесь, даже в этом случае не всегда каждое действие такого контроллера будет использовать все зависимости, внедренные в него.
Кстати, есть возможность внедрять «ленивые» зависимости, инициализирующиеся при первом обращении, это тоже снижает нагрузку. Тем не менее с точки зрения принципов SOLID нет никакой необходимости группировать действия. Напротив, индивидуальные чействия чище.
Согласен с вами. Да, этот пример просто показывает возможность, он не диктует, как вам поступать. Можно передать в представление сущность статьи. Это нормально (выше было сказано, что ViewModel часто может стать просто сущностью). Но список комментариев к ней, рекомендации и другая подобная информация должна получаться самим представлением.
Несоответствие именно в том, что конкретно для ASP.NET MVC нет специального места для размещения логики представления, но его можно получить указанными в статье способами. Они снимают с контроллера и модели зависимость от представления.
На счет модели — тут нельзя сказать, как она должна быть организована. Для каждой задачи она своя. Важно лишь то, что в нее должен быть включен и функционал предметной области, не только доступ к данным, и именно к нему и должен обращаться контроллер.
Конечно, так тоже можно, но если представление хочет кроме этого еще много всего, и контроллеру приходится собирать для него все дополнительные данные, паковать во POCO-вьюмодель, вот этого как раз и можно избежать.
Например, завтра нам понадобилось в представлении показывать еще и последние пять постов пользователя. Не придется изменять код контроллера, чтобы их показать, только код представления.
Контроллер — это конечная точка маршрутизации запроса. Его роль только сообщить бизнес-модели, что что-то произошло (как раз тот изменяющий состояние вызов, который нельзя делать из представления), и возврат представления. Он не должен заниматься сбором особенных данных для конкретно этого представления. Он должен сказать:
— Бизнес-логика, тут хотят создать нового пользователя! Оп, спасибо за идентификатор.
— Представление, покажи пользователя!
И ему нет разницы, какое это представление сложное, что ему нужно о пользователе знать его баланс, подписчиков или особенности его ролей.
Если он будет это все делать, он будет превращаться в ТУУК.
И тут я понял, что я забылся: по поводу наследования вида и всего этого с Razor я писал в контексте ASP.NET MVC, а тут речь про Java. Прошу прощения. Но я думаю, что там все-таки есть какой-то похожий способ внедрения в представление через конструктор.
Потому что представление знает о модели. Это прямо описывается в паттерне MVC. В этой фразе под словом «модель» подразумевается не POCO, представляющий какую-то сущность, передаваемую обычно из контроллера в модель, а «бизнес-модель». То есть оно вполне вправе запросить какие-то недостающие данные. Поищите, что пишет Мартин Фаулер об MVC, можно даже просто зайти на Википедию, там говорится: «Представление отвечает за получение необходимых данных из модели и отправляет их пользователю.» Уверяю, нет никаких причин запрещать представленю делать неизменяющие запросы к сервисам.
И это не помешает верстальщикам. Получение данных происходит в абстрактном классе, а Razor-шаблон в cshtml просто пользуется уже тем, что есть в его родительском классе. Ведь этот шаблон по сути просто такой язык, из которого собирается код одного метода рендера, с которым создается представление-наследник.
Не нарушается, потому что представление не лезет самостоятельно в базу данных или другие сервисы, оно пользуется своим инжектированным интерфейсом, предоставляющим ей то, что ему нужно. Вы можете сделать много реализаций такого интерфейса: моки, получение из репозиториев, и т.п. Ответственность получения данных оказывается на том классе, который реализует интерфейс провайдера.
Попробуйте сами себе ответить, зачем вообще нужны большие странные POCO-ViewModels? Есть ли у них свой смысл? В них всегда столько разномастной информации. Если бы View мог получать недостающие нужные ему данные, этих классов бы вообще не было в большинстве случаев.
Я вас понимаю, но рискну не согласиться. Обычно так оно делается, да, но вы не подумайте, что я имею в виду, что из вида нужно изменять состояние приложения, нет. Но получать данные о состоянии — вполне можно, и это не противоречит MVC. Смотрите: действие контроллера — это конечная точка маршрутизации. Здесь код максимально простой: оповещение бизнес-логики о том, что что-то произошло и возврат вида. Контроллер буквально говорит виду: «Покажи пользователя с таким-то идентификатором», и все. Вид его показывает. Если нужно передать еще какие-то данные, стоит передать, но не все те мелочи, которые представление собирается всавить в свой шаблон. Только ключевое. Потому что иначе контроллер (или провайдер ViewModel, которым он пользуется) становится жестко зависим от реализации представления.
Хм… да, я не ответил на вопрос «почему»: потому что это соответствует SRP, и переносит ответственность вида из контроллера в вид. Если ему для выполнения ответственности нужны какие-то зависимости, их нужно в него внедрить.
Нет, не является. Вообще я считаю хорошей практикой, когда контроллер выбирает, какой вид нужно показать и передает ему минимальный нужный набор опций для показа (то есть не большие сложные так называемые «вью-модели», а что-то маленькое). И после этого представление исходя из этих данных и своих зависимостей должно собрать страницу. То есть если у нас есть какой-нибудь userId, а представление должно показать пользователя и множество связанных с ним объектов, то не стоит собирать их все в коде контроллера, собирать в один сложный объект и передавать во View, достаточно передать только userId, а представление само обратится ко всем инжектированным в него репозиториям и покажет то, что надо.
Для видов возможна инъекция через конструктор, но делается она немного нетривиально. Для начала нужно создать класс (желательно абстрактный), отнаследованный от базового класса вида (сейчас не могу сказать точно, какой это класс, возможно, WebViewPage). В этом абстрактном классе вы реализуете конструктор с зависимостями, и в cshtml-коде вида указываете его через @inherit. Реальный класс вида, который будет использоваться приложением будет создан на лету на основе Razor-кода, и будет отнаследован от вашего класса, и будет иметь все нужные зависимости, которые внедрит, допустим Autofac, или какйо у вас там используется IoC.
Случайно ответил в основную ветку
Кстати, есть возможность внедрять «ленивые» зависимости, инициализирующиеся при первом обращении, это тоже снижает нагрузку. Тем не менее с точки зрения принципов SOLID нет никакой необходимости группировать действия. Напротив, индивидуальные чействия чище.
На счет модели — тут нельзя сказать, как она должна быть организована. Для каждой задачи она своя. Важно лишь то, что в нее должен быть включен и функционал предметной области, не только доступ к данным, и именно к нему и должен обращаться контроллер.
Например, завтра нам понадобилось в представлении показывать еще и последние пять постов пользователя. Не придется изменять код контроллера, чтобы их показать, только код представления.
— Бизнес-логика, тут хотят создать нового пользователя! Оп, спасибо за идентификатор.
— Представление, покажи пользователя!
И ему нет разницы, какое это представление сложное, что ему нужно о пользователе знать его баланс, подписчиков или особенности его ролей.
Если он будет это все делать, он будет превращаться в ТУУК.
И это не помешает верстальщикам. Получение данных происходит в абстрактном классе, а Razor-шаблон в cshtml просто пользуется уже тем, что есть в его родительском классе. Ведь этот шаблон по сути просто такой язык, из которого собирается код одного метода рендера, с которым создается представление-наследник.