Пол М. Джонс однажды завёл интересное обсуждение на тему применения шаблона MVC в веб, основные моменты которой он описал в статье Action-Domain-Responder. Предлагаю вам познакомиться с ней, после чего вы всегда сможете вернуться и прочитать эту статью.
Хочу отметить, что я почти полностью разделяю мнение автора и, более того, я смог даже внести свою лепту при общении с Полом. Но всегда оставался один момент, на счет которого я имел свою точку зрения, и, наконец, я собрался с мыслями и решил изложить её. Контроллеры — действия в ADR — можно описать при помощи шаблона проектирования фасад.
Определения
Первое упоминание данного шаблона проектирования была замечено в книге “Шаблоны проектирования: элементы многоразового применения программного обеспечения на базе ООП”, автором которой являются: Эрик Гамма, Ральф Джонсон, Ричард Хелм и Джон Влисседс. С течением времени книга получила второе название — “Банда Четырех” (Gang of Four). Под этим названием подразумевались авторы книги, а само название часто заменяют аббревиатурой “GoF”.
Шаблон Фасад был одним из семи структурных шаблонов проектирования описанных в книге. Под определением структурных шаблонов скрываются такие шаблоны, которые предоставляют механизм определения отношений между классами или объектами системы.
Шаблон фасад — упрощенный интерфейс к сложным системам.
На Вакипедии вы найдете точное описание шаблона и другие его важные характеристики:
- Фасад задает более удобный метод доступа к операциям системы, при этом упрощая работу с ними.
- Фасад уменьшает количество зависимостей, которые пользователю необходимо загрузить до вызова метода. Вы вызываете метод фасада, а не самой системы.
Примеры фасада
В качестве примера рассмотрим следующие варианты хода операций в системе:
- Преобразуем вид объектов
- Внесем изменения в данные этих объектов
- Проверим данные
- Если данные не прошли проверку, то вызываем исключение
- Начнем транзакцию
- Передадим данные в несколько таблиц
- Завершим транзакцию
- Отметим информацию о внесенных изменений в журнале
- Разошлем оповещения при помощи email
Вот так наша системе выглядит в коде:
<?php
$db = new Db($connectionConfig);
$log = new Logger($loggerConfig);
$mailer = new Mailer($mailerConfig);
$data = array_merge_recursive($_POST, $_FILES);
$inputFilter = new InputFilter();
$inputFilter->setData($data);
if (! $inputFilter->isValid()) {
throw new DomainException();
}
$db->transactionStart();
$db->insertInto(/* ... */);
$db->insertInto(/* ... */);
$db->insertInto(/* ... */);
$db->transactionStop();
$log->info('Finished a transaction');
$mailer->send('New transaction')
Все довольно наглядно и понятно. А теперь представьте, что вам требуется выполнять набор этих операций снова и снова. Или вы просто хотите воспользоваться этим участком кода в несколько местах своего приложения. Здесь-то и могут начаться проблемы, разработчику придется обратиться к операции копировать-вставить.
А фасад может нам помочь обернуть эту логику в удобную обертку:
<?php
class DataTransaction
{
protected $db;
protected $logger;
protected $mailer;
public function __construct(Db $db, Logger $logger, Mailer $mailer)
{
$this->db = $db;
$this->logger = $logger;
$this->mailer = $mailer;
}
public function execute($data)
{
$inputFilter = new InputFilter();
$inputFilter->setData($data);
if (! $inputFilter->isValid()) {
throw new DomainException();
}
$this->db->transactionStart();
$this->db->insertInto(/* ... */);
$this->db->insertInto(/* ... */);
$this->db->insertInto(/* ... */);
$this->db->transactionStop();
$this->logger->info('Finished a transaction');
$this->mailer->send('New transaction')
}
}
Следующий шаг будет заключаться в создании фасада, наверняка при помощи инверсии контроля контейнера, чтобы добавить необходимые зависимости, и выполнения самой операции шаблона:
<?php
$dataTransaction->execute(array_merge_recursive($_POST, $_FILES));
Этот код полностью попадает под описание шаблона проектирования фасад. Мы не только не имеем прямого дела с зависимостями, но и также упростили сложный набор операций до одной.
Контролеры и действия
Думаю, вы уже догадываетесь к чему я клоню.
Контроллеры в MVC и действия в ADR — живой пример Фасадов.
Вы можете довольно легко преобразовать контроллеры и действия в фасады при работе со следующими операциями:
- преобразование аргументов запроса
- вызов логики домена/моделей при помощи преобразованных аргументов запроса
- Преобразование и возврат ответа
Как мне кажется, преобразование Контроллеров и Действий в фасады несет в себе довольно большую выгоду. Что в PHP, что в Rails мы зачастую сталкиваемся с так называемыми “жирными контроллерами” — контроллеры, несущие в себе большой объем логики, в силу чего они становятся нестабильными в работе, да и, как правило, использовать какие-то участки кода из них становится практически невозможно. Если же мы начнем принимать их за фасады, особенно в описанных ваше ситуациях, то мы сможем сфокусироваться на задаче, выполнение которой они несут в себе, что нам даст следующее:
- соблюдение принципа единой ответственности
- соблюдение принципа инверсии зависимостей
- соблюдение закона Деметры
- возможность юнит тестирования контроллеров и действий (вместо необходимости интегрирования сложных тестов в настройку приложения)
- возможность реализации наследования в MVC (аспект, который чаще всего довольно плохо реализуется)
- однозначность цели контроллера или действия (выполняется только одна задача)
Разделив логику на фасады для наших трех задач, мы разделяем логику приложения, при этом мы получим четкое разделение обязанностей. Все, что не попадает под определенные задачи, должно будет находится на другом участке приложения:
- Модели/домены вызываются с аргументами, полученными из запроса. Если вы поймаете себя на мысли, что вызываете слишком много моделей или обрабатываете данные полученные из моделей, то вам следует создать фасад на уровне модели/домена.
- Если вы начинаете осознавать, что выполняете слишком много работы по генерации ответа, вам также следует создать фасад для преобразования ответа (в ADR такой подход будет означать, что вам придется вложить больше логики в ответчика (Responder)).
Я же нахожу в таком подходе еще один положительный момент: получается, что вы описываете сложные операции при помощи базовых шаблонов проектирования. Я считаю, что чем проще определение проблемы, тем больше шансов на успешное её решение.
Заключение
Считайте эту статью логическим развитием шаблонов MVC и ADR — способ описать их при помощи базовых понятий.
Как видите, в статье полно ссылок. Я настоятельно рекомендую ознакомиться с ними. Шаблоны проектирования образовались по мере того, как разработчики замечали определенные последовательности в своей работе и стали давать этим последовательностям имена. Старайтесь не только изучить их, но и научиться их применять в пределах своих приложений.