Цель
Разделить взаимодействия пользовательского интерфейса между веб-клиентом и веб-приложением на три чётко определённые роли.
Предпосылки
Термин MVC испытывает некоторое семантическое размытие своего первоначального значения, особенно в контексте веба (см. видео Стефана Прибша для более подробного рассмотрения вопроса). В качестве средства устранения этого размытия предлагаю вашему вниманию описание паттерна Action-Domain-Responder, являющегося доработкой концепции MVC под нужды решения специфичных для веба задач.
Я считаю, что ADR значительно лучше соответствует тому, что мы на самом деле реализуем в процессе веб-разработки изо дня в день. К примеру, на создание этого паттерна меня частично вдохновило то, как мы решаем проблемы роутинга и диспетчеризации, ведь в общем случае при роутинге и диспетчеризации мы обращаемся не к классу контроллера per se, а к какому-то конкретному методу действия в этом классе контроллера.
Еще одной вскрывшейся проблемой является тот факт, что часто мы рассматриваем Представление (View) как шаблон (template), хотя в контексте веба, вероятно, более уместно было бы говорить о том, что Представлением является HTTP-ответ. Исходя из вышесказанного, я считаю, что ADR способен предоставить лучшее, чем MVC, разделение концепций для веб-приложений.
Компоненты
Действие (Action) — это логика, которая связывает Домен (Domain) и Ответчик (Responder). Оно использует данные ввода (input) для взаимодействия с Доменом и передает вывод Домена Ответчику.
Домен (Domain) содержит в себе логику для управления сессией, приложением и данными окружения, изменения состояния и управления данными в случае необходимости.
Ответчик (Responder) — это логика, необходимая для создания HTTP-ответа или описания ответа. Он оперирует основным контентом (body content), шаблонами и представлениями, заголовками, cookies, кодами статусов и прочим.
Взаимодействия
- Веб-обработчик получает запрос от клиента и передает его Действию.
- Действие взаимодействует с Доменом.
- Действие передает данные Ответчику (эти данные могут включать в себя результаты взаимодействия с Доменом, данные из клиентского запроса и т.д.)
- Ответчик генерирует ответ, используя данные, полученные от Действия.
- Веб-обработчик отсылает ответ клиенту.
Сравнение с MVC (Model-View-Controller)
Самый популярный паттерн, описывающий взаимодействия в рамках веба — Модель-Представление-Контроллер (Model-View-Controller). Является ли Action-Domain-Responder на самом деле всего лишь переодетым Model-View-Controller? Можно заметить, что элементы ADR находят свое довольное чёткое отражение в элементах MVC:
Model <--> Domain
View <--> Responder
Controller <--> Action
Два паттерна выглядят очень похоже. В чём же различия?
В общем и целом, из эссе Фаулера "Архитектуры GUI" [перевод здесь] мы можем увидеть, что «у вас не просто одно представление и один контроллер, вы вынуждены создавать отдельную пару представление-контроллер для каждого блока на странице, каждого элемента управления и для страницы в целом». Это основной элемент семантического размытия, возникающего при применении MVC в веб-приложениях.
И давайте более подробно сравним отдельные компоненты MVC и ADR.
Модель и Домен
Я не вижу в них радикальных различий, за исключением того, что в ADR Ответчик не взаимодействует с Доменом каким-либо существенным образом. Ответчик может использовать объекты Домена как сущности и коллекции, но только с целью отображения; Ответчик не модифицирует Домен и не отправляет ему какую-бы то ни было информацию, как это предусмотрено в рамках MVC.
Контроллер и Действие
В общем случае, большинство классов Контроллера в системе, спроектированной по принципам MVC, содержат в себе несколько методов, каждый из которых соответствует какому-то отдельному действию. Поскольку все эти методы обитают в одном Контроллере, к нему также добавляется дополнительная «обёрточная» логика для работы с каждым из этих методов, например, хуки, срабатывающие непосредственно перед или после самого действия. Значительное исключение из этого правила составляют микрофреймворки, в которых каждый Контроллер представляет из себя отдельное замыкание или вызываемый объект, что больше соответствует именно Действию (напр. Slim).
В рамках же ADR Действия видятся как отдельные классы или замыкания. Иными словами, каждое Действие должно быть вынесено в свой отдельный класс или замыкание.
Действие взаимодействует с Доменом по тем же принципам, по которым Контроллер взаимодействует с Моделью, но не взаимодействует с Представлением или системой шаблонов. Действие просто передаёт Ответчику данные и предоставляет тому самостоятельно ими распоряжаться.
Представление и Ответчик
В системе MVC метод Контроллера обычно генерирует тело ответа с помощью Представления (например, через Шаблонизатор (Template View) или Двухшаговую Шаблонизацию (Two Step View)). Затем Контроллер интегрирует сгенерированное тело ответа в сам ответ. Метод Контроллера, являющийся действием, напрямую управляет ответом для выставления необходимых заголовков.
Иногда часть методов Контроллера может предоставлять разные форматы ответа для одних и тех же данных. Поскольку эта вариантивность чаще всего не поддерживается абсолютно всеми методами, логика представления данных так или иначе меняется от метода к методу, со своими условиями в каждом конкретном случае.
В ADR у каждого Действия есть соответствующий Ответчик. Когда Действие заканчивает своё взаимодействие с Доменом, оно передаёт от Домена Ответчику и все необходимые данные, и контроль над этими данными. Ответчик целиком и полностью управляет выставлением заголовков, выбором типов контента, рендером шаблонов и прочим.
Заметим, что Ответчик может включать в себя Шаблонизатор, Двухшаговую Шаблонизацию, Преобразователь (Transform View) или любую другую систему представлений. Также заметим, что обобщённый (generic) Ответчик может использоваться более чем одним Действием. Главное — что Действие оставляет всю работу над заголовками и контентом Ответчику, а не что требуется создавать свой Ответчик под каждое отдельное Представление.
Сравнение с другими паттернами
Существуют и другие паттерны, которые рассматриваются в качестве доработки, замены или дополнения концепции MVC. Вы можете ознакомиться с этим обзором паттернов от Дерека Грира.
EBI (Entity-Boundary-Interactor)
У термина EBI существует несколько синонимов: порты и адаптеры, гексагональная архитектура, ECB (Entity-Control-Boundary). Он описан как часть "Чистой архитектуры" Роберта Мартина.
EBI является частичной альтернативой MVC, при которой основные элементы и поведения, представляемые объектами Интеракторов (Interactor) и Сущностей (Entity), отделены от входящих и исходящих данных с помощью Ограничителей (Boundary). Главным следствием такого подхода является чёткое разграничение самого приложения и тонкостей механизмов ввода и вывода, так что ключевые поведения никогда не зависят от какой-либо частной системы получения запроса или отправки ответа.
Признаюсь, я не очень хорошо знаком с концепцией EBI, так что это описание может быть не совсем корректным в целом или в частностях. После своих неполных изысканий в этой области я пришёл к выводу, что архитектура EBI, вероятно, лучше описывает взаимодействия внутри приложения, чем MVC. Если приведённое выше описание верно, то ADR довольно хорошо ложится на структуру EBI:
- ADR'овские Действие и Ответчик могут служить в качестве веб-специфичного Ограничителя
- ADR'овский Домен может служить аналогом Интерактора, инкапсулируя или иным образом скрывая элементы EBI'йных Сущностей от ADR'овского Действия
Альтернативно, в рамках терминологии портов и адаптеров или гексагональной архитектуры, более разумным может быть рассмотрение Действия как «порта», через который EBI'йный Ограничитель вызывается (invoke) как часть ADR'ного Домена. Наконец, Ответчик можно рассматривать как «адаптер», через который приложение возвращает данные клиенту.
Тем не менее, ADR не кажется прямой заменой EBI. Скорее, два подхода дополняют друг друга.
DCI (Data-Context-Interaction)
DCI описывается как дополнение MVC, а не замена. Думаю, будет справедливо назвать его в той же мере дополнением ADR.
MVP (Model-View-Presenter)
MVP был отправлен в отставку паттернами Управляющий Контроллер (Supervising Controller) и Пассивное Представление (Passive View). На первый взгляд, он выглядит весьма похожим на ADR, особенно тем, что Пассивное Представление и Модель не имеют никаких зависимостей друг от друга. Из текста Фаулера:
Управляющий Контроллер использует контроллер как для того, чтобы обрабатывать данные ввода, так и для того, чтобы управлять представлением, позволяя организовать более сложную логику отображения…
Пассивное Представление добивается этого с помощью уменьшения количества поведений у элементов UI до абсолютного минимума за счёт использования контроллера, который не только обрабатывает реакцию на пользовательские события, но еще и берёт на себя всю работу по обновлению представлений. Такой подход позволяет сфокусировать усилия на тестировании контроллера, не беспокоясь при этом о каких-то проблемах в представлениях.
Давайте взглянем чуть подробнее:
- Модель и Домен довольно сильно похожи друг на друга, как и в случае с MVC.
- Пассивное Представление в полной мере не соответствует ни Действию, ни Ответчику; скорее, оно может быть рассмотрено как ответ, который возвращается клиенту.
- Управляющий Контроллер напоминает Ответчик в плане «управления представлением, позволяющего добиваться более сложной логики отображения». С другой стороны, Ответчик не взаимодействует с Доменом и не получает входные данные от клиента, так что, похоже, он не слишком подходит на роль Управляющего Контроллера.
- В качестве альтернативы, Управляющий Контроллер можно было бы представить как Действие, но Действие не отвечает за управление представлениями (т.е. ответом).
В общем, близко, но не то же самое.
MVVM (Model-View-ViewModel)
MVVM только частично схож с ADR. Модель в MVVM является практически тем же самым, что и Модель в MVC и Домен в ADR. Аналогично, Представление в MVVM очень похоже на Представление в MVC и Ответчик в ADR.
Однако же Модель Представления (ViewModel) не похожа ни на Контроллер в MVC, ни на Действие в ADR. Поскольку ADR является доработкой MVC, разумно предположить, что сравнение MVVM с MVC будет аналогично сравнению с ADR.
Для развёрнутого описания этих различий советую вам ознакомиться со статьями Джоэля Вензеля, Автара Синг Сохи, Рейчел Аппель и Нираджа Бхатта.
(У меня состоялась интересная email-дискуссия, в рамках которой мне объяснили, что MVVM очень похож на MVC, к которому добавили Модель Представления для взаимодействий между Представлением и Моделью. Если это на самом деле так, то Модель Представления может использоваться в ADR с тем же успехом, что и в MVC).
PAC (Presentation-Abstraction-Control)
Из Википедии:
PAC представляет из себя иерархическую структуру агентов, каждый из которых является триадой из представления, абстракции и управляющей части. Агенты (триады) коммуницируют между собой только через управляющую часть каждого из них. Ещё одно отличие от MVC состоит в том, что в каждой триаде представление и абстракция (модель в терминах MVC) являются абсолютно изолированными. Такой подход позволяет обрабатывать модели и представления параллельно в разных потоках, что оставляет у пользователя впечатление очень быстрого старта, поскольку интерфейс (представления) может быть отображен еще до того, как абстракции будут полностью инициализированы.
Не слишком похоже на ADR.
RMR (Resource-Method-Representation)
Я не слышал про RMR, пока ircmaxell не указал на него на Реддите.
ADR и RMR очень похожи друг на друга, и их элементы соответствуют друг другу весьма точно.
Resource <--> Domain
Method <--> Action
Representation <--> Responder
Однако некоторые нюансы RMR заставляют меня остаться при мнении, что эти два подхода всё-таки отличаются друг от друга. Например:
Таким образом, в рамках объектно-ориентированного языка мы можем рассматривать http-ресурсы (Resource) как объекты с приватными свойствами и определённым набором публичных методов (Method), каждый из которых соответствует стандартному методу HTTP. В терминах MVC ресурс может быть представлен как модель с небольшой частичкой контроллера внутри.
Лично мне это кажется просто слишком большим смешиванием концепций. Я предпочитаю видеть более ясное отделение моделей от действий, совершаемых над приложением.
Представление (Representation) очень похоже на Представление в MVC, мы отдаём ему объект ресурса и даём команду сериализовать его в необходимый формат вывода.
Очевидно, это неверно для целого ряда HTTP-ответов, таких, например, как «Not Found». Такой ответ определённо не является представлением запрошенного ресурса.
В общем и целом, вероятно, ADR можно рассматривать в качестве дополненной и расширенной вариации RMR, в которой Ресурсы и действия, которые могут быть над ними совершены, чётко разделены на Домены и Действия, и где Представление (т.е. генерация ответа) управляется Ответчиком.
Models-Operations-Views-Events (MOVE)
С оригинального сайта:
Модели (Models) инкапсулируют в себе всё, что ваше приложение знает.
Операции (Operations) инкапсулируют всё, что ваше приложение делает.
Представления (Views) являются связующим звеном между вашим приложением и пользователем.
События (Events) используются, чтобы безопасно соединить все эти элементы.
Это очень интересный паттерн сам по себе. Идея Моделей и Операций кажется весьма подходящей в рамках «Domain-Driven Design»-подхода.
Тем не менее, я не думаю, что MOVE похож на ADR, особенно вот в этом моменте:
События — это именно то, что даёт MOVE (и MVC) инверсию управления (inversion of control), необходимую, чтобы модели имели возможность обновлять представления без получения информации о том, какое именно представление они обновляют.
В ADR, Домен и Ответчик не «обновляют друг друга». Работа Домена завершена, а результат передан Ответчику для дальнейшего отображения клиенту.
Separated Presentation
Можно найти несколько указаний на ADR, в особенности на Ответчик, в Раздельных Представлениях. Статья сама по себе заслуживает прочтения, однако Раздельные Представления скорее являются мета-паттерном, описывающим общие подходы к отделению данных от их представления, а не конкретные пути достижения этого разделения.
Сравнение MVC и ADR на примерах
Исходная точка в MVC
В MVC структура директорий для банальной блоговой системы может выглядеть примерно так. Заметьте, что index и read предоставляют в качестве альтернативы вывод в JSON, а шаблон комментариев является «частичным» (partial) и тоже даёт возможность вывода JSON в качестве альтернативы.
controllers/
BlogController.php # index(), create(), read(), update(), delete()
models/
BlogModel.php
views/
blog/
index.html.php
index.json.php
create.html.php
read.html.php
read.json.php
update.html.php
delete.html.php
_comments.html.php
_comments.json.php
А вот другой тип структуры директорий для MVC:
Blog/
BlogController.php # index(), create(), read(), update(), delete()
BlogModel.php
views/
index.html.php
index.json.php
create.html.php
read.html.php
read.json.php
update.html.php
delete.html.php
_comments.html.php
_comments.json.php
Стандартный класс Контроллера в MVC выглядит приблизительно следующим образом. Заметим, что в этом классе Контроллера присутствуют разные действия, а методы этих действий выставляют заголовки ответа.
<?php
use Framework\Controller;
class BlogController extends Controller
{
public function create()
{
// is this a POST request?
if ($this->request->isPost()) {
// retain incoming data
$data = $this->request->getPost('blog');
// create a blog post instance
$blog = $this->blog_model->newInstance($data);
// is the new instance valid?
if ($blog->isValid()) {
// yes, save and redirect to editing
$blog->save();
$this->response->redirect('/blog/edit/{$blog->id}');
return;
} else {
// no, show the "create" form with the blog instance
$this->response->setContent($this->view->render(
'create.html.php',
array('blog' => $blog),
));
return;
}
} else {
// not a POST request, show the "create" form with defaults
$this->response->setContent($this->view->render(
'create.html.php',
array('blog' => $this->blog_model->getDefault())
));
}
}
public function index()
{
// ...
}
public function read($id)
{
// ...
}
public function update($id)
{
// ...
}
public function delete($id)
{
// ...
}
}
?>
Логика метода create() может быть некоторым образом сокращена за счёт вынесения большей части взаимодействий с моделью на Сервисный Уровень (Service Layer), но суть останется прежней — именно Контроллер обычно отвечает за заголовки ответа и контент.
Взглянем на ADR
Для сравнения, структура папок при ADR может быть организована следующим образом. Обратим внимание, что у каждого Действия есть соответствующий ему Ответчик.
Blog/
Action/
BlogIndexAction.php
BlogCreateAction.php
BlogReadAction.php
BlogUpdateAction.php
BlogDeleteAction.php
Domain/
# Model, Gateway, Mapper, Entity, Collection, Service, etc.
Responder/
BlogIndexResponder.php
BlogCreateResponder.php
BlogReadResponder.php
BlogUpdateResponder.php
BlogDeleteResponder.php
html/
index.html.php
create.html.php
read.html.php
update.html.php
delete.html.php
_comments.html.php
json/
index.json.php
read.json.php
_comments.json.php
Пара из Действия и Ответчика, соответствующих рассмотренному выше методу Контроллера create(), могла бы выглядеть так:
<?php
use Framework\Action;
class BlogCreateAction extends Action
{
public function __invoke()
{
// is this a POST request?
if ($this->request->isPost()) {
// yes, retain incoming data
$data = $this->request->getPost('blog');
// create a blog post instance
$blog = $this->blog_model->newInstance($data);
// is the new instance valid?
if ($blog->isValid()) {
$blog->save();
}
} else {
// not a POST request, use default values
$blog = $this->blog_model->getDefault();
}
// set data into the response
$this->responder->setData(array('blog' => $blog));
$this->responder->__invoke();
}
}
?>
<?php
use Framework\Responder;
class BlogCreateResponder extends Responder
{
// $this->response is the actual response object, or a response descriptor
// $this->view is a view or template system
public function __invoke()
{
// is there an ID on the blog instance?
if ($this->data->blog->id) {
// yes, which means it was saved already.
// redirect to editing.
$this->response->setRedirect('/blog/edit/{$blog->id}');
} else {
// no, which means it has not been saved yet.
// show the creation form with the current response data.
$this->response->setContent($this->view->render(
'create.html.php',
$this->data
));
}
}
}
?>
Опять же, можно найти возможности для рефакторинга и в этом коде, особенно в части работы с Доменом. Главное — что Действие не выполняет никакой работы Ответчика; вся необходимая работа выполняется непосредственно логикой самого Ответчика.
Вы можете изучить расширенный пример кода на ADR здесь.
Комментарии
Не рассмотрены запросы
Я получил довольно много критики из-за того, что не включил в паттерн элемент, соответствующий «HTTP запросу». Ранняя версия этой публикации содержала в себе такой элемент и была названа «Request-Action-Domain-Response». Тем не менее, при дальнейшем изучении MVC и схожих архитектурных паттернов я заметил, что ни один из них не определяет элемент ввода. Чтобы не выбиваться из общего ряда, ADR тоже не рассматривает этот элемент.
Не рассмотрен Фронт-Контроллер
Паттерн создан для улучшения подхода Модель-Приложение-Контроллер, а не веб-приложений вообще. Таким образом, в нём умышленно не рассматриваются некоторые элементы, присущие многим веб-приложениям, и в частности это относится к Фронт-Контроллеру.
ADR не описывает элемент роутинга или диспетчеризации, и так же он не описывает, как с диспетчеризацией связаны Действие и Ответчик. Роутинг и диспетчеризация чаще всего являются зоной ответственности Фронт-Контроллера, и существует много способов наладить взаимодействие между Действием, Ответчиком и Фронт-Контроллером:
- Действие может напрямую вызывать Ответчик, возвращающий ответ на запрос;
- Ответчик и ответ могут быть доступны фронт-контроллеру, вызывающему их напрямую;
- Действие может возвращать Ответчик, который затем вызывается и возвращает ответ, который вызывается и отсылает сам себя;
- и так далее.
Паттерн ADR не описывает никаких элементов предварительной фильтрации или валидации запроса, так как они могут являться частью Фронт-Контроллера. Обратите внимание, что, в зависимости от логики предварительной фильтрации и валидации запроса, Действие может вызвать Ответчик, или оно может вернуть свой собственный ответ, или вызвать какое-то дополнительное Действие в результате работы своей логики и так далее. Аналогичным образом, вызванное Действие может иметь свой собственный набор проверок, приводящий к вызову Ответчика без взаимодействия с Доменом. Причинами таких «укороченных» цепочек вызова могут быть:
- Несогласованность HTTP-методов. Если роутинговая система не обнаружит соответствия между используемым HTTP-методом и запрошенным Действием, Фронт-Контроллер может вернуть ответ с ошибкой вместо перенаправления запроса к Действию.
- Аутентификация. Наличие или отсутствие прав доступа у клиента (и их валидность) может влиять на необходимость вызова Действия или взаимодействия с Доменом во время этого Действия.
- Авторизация. Система контроля доступа может отклонить клиентский запрос определенного Действия, или привести к тому, что Действие будет выполнено без взаимодействия с Доменом и, возможно, самостоятельно вернёт ответ.
- Несоответствие контента. Фронт-Контроллер, Действие или другие участвующие в обработке запроса элементы могут проверять Accept-заголовки клиентского запроса. Несоответствие этих заголовков может принудительно исключить Действие или Домен из обработки запроса и/или привести к раннему выводу ответа.
- Валидация контента. Если данные входящего запроса по какой-то причине не считаются валидными, Действие может отказаться от взаимодействия с Доменом и напрямую вызвать Ответчик для передачи сообщения об ошибке.
Альтернативные формулировки
ADR может быть назван вариацией на тему Контроллера и Представления в паттерне Модель-Представление-Контроллер, а не самостоятельным паттерном.
Тогда получится, что Действие представляет из себя вариацию, схожую с Контроллером Страницы (Page Controller), и в таком контексте его более точным названием может быть Контроллер Действия (Action Controller). В этом случае оно будет соответствовать Контроллеру из MVC. (Более того, формальное описание Контроллера Страницы гласит, что он представляет собой «страницу или действие»).
Аналогично можно рассматривать Ответчик как вариацию, схожую с Шаблонизатором (Template View) или Преобразователем (Transform View), и тогда разумнее было бы назвать его Представлением Ответа (Response View). Таким образом, Ответчик вполне вписывается в элемент Представления из MVC.
Несмотря на всё вышесказанное, я считаю, что эти альтернативные формулировки не так хороши и точны, как описание подхода в рамках отдельного паттерна ADR. По большей части — из-за внутренних взаимодействий между Моделью и Представлением: в MVC Представление обновляет Модель, в ADR Ответчик не обновляет Домен.
Неясный Домен
Домен охватывает очень многое: не только набор классов приложения, но еще и его состояние и состояние окружения. Вероятно, его можно было бы назвать Моделью, но это лишь внесло бы дополнительную путаницу.
Кроме того, возможно, Действию следует передавать Ответчику не данные, полученные от Домена, а Представление Модели (Presentation Model). Но в таком случае, возможно, именно Домен, вызываемый Действием, должен возвращать Представление Модели, инкапсулирующее состояние приложения.
В любом случае, ADR предлагается как доработка MVC. Исходя из этого, мы можем сказать о Домене в ADR всё то же самое, что можно сказать о Модели в MVC.
Поясняя Действия
Один из комментирующих заметил, что Действие можно организовать так, чтобы в нём срабатывала разная логика в зависимости от входящего запроса. Например, он предположил, что читатели могут расширить Действие и добавить ему функционал работы с разными HTTP-методами, поместив логику для разных HTTP-методов в одно и то же Действие.
Хотя по моему мнению паттерн сам по себе выражает идею о том, что каждое Действие должно выполнять всего лишь одну функцию, и это однозначно следует из сравнений Контроллера и Действия и паттернов ADR и RMR, я ещё раз уточню, уже в более явном виде: смысл заключается в том, что каждое Действие должно выполнять одно и только одно действие в ответ на любой входящий запрос.
Замена, а не доработка MVC
Нэйт Эйбель возражает, что ADR следует считать заменой MVC, используемой для серверных приложений:
Скажу, что чем больше я узнаю о паттерне MVC, тем менее он мне видится подходящим для серверной части веб-приложений. <...> Думаю, главный прорыв твоей идеи ADR — это отвязка от того, что кажется мне плохой абстракцией. Я бы советовал тебе избегать описания ADR в терминах MVC за исключением тех случаев, где это абсолютно необходимо.
Полный комментарий.
Другие комментарии
Оригинальный пост в блоге, описывающий эту идею, находится здесь.
Стефан Хохдёрфер ответил на мою идею в этом посте; в дальнейшем дискуссия продолжилась здесь и на реддите.
Джон Лейтон писал про Сфокусированный Контроллер (Focused Controller), который весьма похож на Действие из ADR, здесь.
Последующий пост, сравнивающий Представление и Ответчик, находится здесь, а комментарии к нему на реддите — здесь и здесь.
Акихито Коритама предлагает свои замечания здесь.
Преимущества и недостатки
Одним комплексным преимуществом является то, что паттерн более точно описывает самые типичные сценарии взаимодействия в вебе. Запрос приходит и перенаправляется к действию; действие взаимодействует с доменом, и после этого выстраивается ответ. Работа ответа — включая и заголовки, и контент — целиком и полностью отделена от работы действия.
Одним комплексным недостатком является то, что нам придётся иметь дело с возросшим количеством классов в приложении. Свой собственный класс получит не только каждое Действие, но ещё и каждый Ответчик.
Но этот недостаток, вероятно, не так ужасен в долгосрочной перспективе. Необходимость создания отдельных классов может привести к более ясной и менее глубокой иерархии наследования. Разделение Действия и Ответчика способствует лучшей тестируемости. В разных системах эти особенности могут проявиться по-разному. Кто-то заметил, что обрабатывать «много классов» в различных редакторах кода и IDE удобнее, чем «меньше классов, но больше методов», поскольку обзор класса обычно проще, чем обзор метода.
Благодарность
Выражаю свою благодарность всем, кто своими вопросами, комментариями, критикой или рекоммендациями помогал работать над этим предложением. Отдельно я бы хотел поблагодарить следующих людей, перечисленных без всякого порядка:
- Matthew Weier O'Phinney
- Hari KT
- Stephan Hochdorfer
- Adam Culp
- Dan Horrigan
- Josh Lockhart
- Beau Simensen
Fesor
Почему же? Модель предметной области. Никакой путаницы.
И это не есть хорошо, так как разные HTTP методы описывают разные действия. Ну во всяком случае обычно так происходит. Скажем в том же «Applying UML and patterns» контроллеры так же рекомендовалось делить по юзкейсам, с этой точки зрения все остается так же, так как контроллеры у вас всеравно по идее остаются, а действия стоит выносить в application layer сервисы, которые руководят общим флоу и реализуют уже юзкейсы. Правда частенько это избыточно.
Ну да, MVC чисто UI-ная архитектура, и никакого отношения к бэкэнду иметь оно не может. Это кое как работало во времена когда сервер плевался статикой до всяких там ajax-ов и то по случайности.
Вообще есть же гексагональная (луковая) архитектура, которая менее абстрактна и более четко дает представление о том что где.
JustRoo Автор
Полагаю, всё из-за тех же внутренних взаимодействий: модель «общается» с представлением, а домен — нет.
Именно. Но в MVC они все группируются в одном контроллере, а тут за такое бьют по рукам.
Которая из них?
Fesor
В зависимости от сложности проекта. В идеале модель ничего не знает о внешнем мире и общается с ним только через DTO (на выход и на выход), а представление уже из этого DTO формируется. Это в большинстве случаев избыточно, потому такие мысли и появляются.
Не знаю как там в MVC, но в «Applying UML And patterns» рекомендовалось дробить контроллеры по юзкейсам, один контроллер на юзкейс.
Трактовок хоть и много, но смысл почти все они передают один и тот же. Разница в количестве слоев, трактовках и т.д.
Fesor
Ну да, по сути вот на этой картинке — Framework Layer это совокупность V и C, это тот слой где формируется нужное представление для каждого компонента (взаимодействие с базой, по HTTP или еще как) а так же контроллеры этих компонентов. А далее уже внутрь идет именно модель. Если сравнивать с ADR — то тут у нас application layer это A, D внутри а R где-то внутри Framework layer. И опять же action-ы работают с DTO а не с HttpRequest. Это будет такие вот application layer сервисы.
В зависимости от потребностей можно какие-то слои объединяться, склеивать, они могут чуть чуть перетекать друг в дружку…
JustRoo Автор
То есть по факту ADR вполне себе нормально ложится на гексагональную структуру:
Fesor
Ну как бы да. Единственное что:
— Presentation Model не есть DTO, DTO это довольно тупой объект-структурка, который просто переносит данные из одного слоя в другой (у меня скажем для этого используются обычные массивчики и stdObject, я делаю классы только для отдельных сложных случаев). Далее потом отдельные штуки уже из этих данных формируют представление и т.д.
— Фронт контроллер так же часть framework/infrastructure layer. Вообще все контроллеры туда ложатся
— контроллеры там все равно есть, помимо экшенов. Они готовят DTO и дергают экшены. Опять же я к примеру далеко не всегда делаю отдельные сервисы-экшены так как обычно это оверхэд.