Изучая планы развития CMS Joomla, для написания одной из своих предыдущих статей, я наткнулся на аббревиатуру HMVC. Не сложно было понять, что это как-то связано со ставшим стандартом паттерном MVC. Найденная расшифровка: «HMVC — иерархические модель-вид-контроллер» — мало что объяснила. Дальнейшие поиски информации тоже дали не много, в основном теоретические рассуждения о паттерне и почти ничего о том, как же его использовать на практике. Однако немного поразмыслив, я понял, что уже использовал его в своем предыдущем проекте на Symfony 2. Более того, оказывается, что частично этот паттерн используют очень многие даже не догадываясь об этом.
Проблемы MVC
Выглядящий в теории идеальным, MVC на практике сталкивается с рядом проблем. Для начала вспомним какую основную проблему он должен разрешить: разделив приложение на три различных аспекта (контроллер, вид и модель) добиться минимальной зависимости между ними, а также между различными частями приложения. В примерах из учебников с этим все в порядке. Есть модель чего-либо, вид для отображения данных и контроллер для осуществления действий в ответ на действия пользователя.
Проблема первая
На практике обычно приходиться оперировать одновременно несколькими моделями, например: статьи, пользователи, комментарии. В принципе, это не критично, паттерн MVC это предусматривает, но это увеличивает количество зависимостей — вид и контроллер зависят более чем от одной модели, а от одной модели зависят более одного вида и контроллера.
Проблема вторая
Для отображения данных из разных моделей хотелось бы использовать виды созданные специально для них. Например, выглядит логичным отображение комментариев одинаково для статей и для товаров. Такое классический MVC не предусматривает, но это частично обходится использованием шаблонов. Т.е. используется один вид который получает данные из моделей, а для отображения их отображения используется комбинация нескольких шаблонов. И снова увеличивается количество зависимостей.
Проблема третья
Иногда действия надо выполнить не над одной моделью, а над несколькими одновременно. Например, при удалении пользователя надо удалить все его статьи и комментарии. В результате приходится создавать контроллер, который описывает операции не только над моделью к которой он относится (в примере — пользователи), но над моделями к которым непосредственного отношения он не имеет. Таким образом появляются не очевидные зависимости.
Основная идея HMVC
Как же устранить эти проблемы? Поскольку проблемы возникают из-за того, что вместо MVC получается что-то наподобие MMMVVVCC, где каждая модель вид и контроллер могут принадлежать разным подсистемам, ответ очевиден — вернуться к MVC в котором есть лишь по одной модели, виду и контроллеру.
Итак, первый принцип HMVC: в приложении используются только жестко фиксированные триады модель-вид-контроллер, которые взаимодействуют с остальными подсистемы исключительно через контроллер.
Из этого выплывает второй принцип: для организации более сложных комбинаций используются иерархии триад.
На первый взгляд, может показаться, что для возможности реализации HMVC достаточно возможности вызова контроллера из другого контроллера. Но в веб-приложении поведение зависит не просто от команды переданной контроллеру, а от http-запроса в целом. И модель и вид могут сами анализировать запрос и некоторым образом изменять свое поведение. Поэтому требуется возможность передать запрос другому контроллеру, причем не обязательно тот же, что был получен. Сделать это можно тремя различными методами.
Клиент-серверный HMVC
Самый простой способ отправить http-запрос — использовать для этого браузер. В этом случае даже не требуется какая-то особая поддержка фреймворком. И этот подход используется повсеместно — называется AJAX. Да-да, именно тот ajax. Мы может использовать одну базовую триаду модель-вид-контроллер для отображения основного содержимого страницы (например, текста статьи), которая остальные необходимые фрагменты (например, комментарии) получит при помощи ajax-запросов к таким-же триадам.
Такой подход позволяет для некоторых частей страницы использовать http-кеширование (кеширование данных в кеше браузера, прокси-сервера либо проксирующего http-сервера) а часть загружать в режиме real-time. Например страница статьи может быть загружена по частям следующим образом:
- сама страница с текстом статьи статична и по возможности извлекается из кеша браузера, в котором может храниться довольно долго;
- комментарии постоянно обновляются и поэтому не кешируются и каждый раз запрашиваются с веб-сервера;
- блок последних новостей обновляется время от времени, может извлекается из кеша, но храниться в нем не так долго как статья.
Плюсы:
- не нужна поддержка фреймворком (можно вообще без него обойтись);
- возможность гибкого http-кеширования;
- возможность отправить запрос на другой веб-сервер, распределив таким образом нагрузку между несколькими серверами.
Минусы:
- необходимо писать java-скрипты;
- увеличивается количество запросов от браузера серверу, что может увеличить время загрузки страницы и нагрузку на веб-сервер.
Сервер-серверный HMVC
Следующим по простоте реализации является отправка запроса веб-сервером самому себе. Для этого может использоваться curl, либо другая библиотека способная отправлять http-запросы. Этот подход похож на предыдущий, но с той разницей, что запросы отправляет не браузер пользователя, а сам веб-сервер в процессе формирования документа.
В качестве примера снова рассмотрим статью с комментариями. Основой страницы является статья, поэтому для ее отображения используются контроллер, вид и модель статьи. Но если в классическом MVC для отображения комментариев внутри вида статьи используется обращение к модели и виду комментариев, то HTMV предусматривает отправку запроса контроллеру комментариев. В данном случае посредством http-запроса, который инициирует запуск параллельного процесса, который выдаст готовый блок комментариев для статьи.
Как и в предыдущем случае, при таком подходе возможно использование http-кеширования, но для его использования необходимо использовать проксирующий http-сервер, например nginx.
Плюсы:
- клиенту отдается готовая страница;
- возможность гибкого http-кеширования;
- возможность отправить запрос на другой веб-сервер, распределив таким образом нагрузку между несколькими серверами.
Минусы:
- для формирования одной страницы запускается несколько параллельных процессов, что увеличивает нагрузку на веб-сервер.
Внутри-серверный HMVC
Под термином «внутри-серверный» я имею в виду, что все происходит внутри процесса веб-приложения. Как и в предыдущем случае, триады модель-вид-контроллер общаются между собой посредством запросов, и восприниматься они должны ими точно так же как обычные http-запросы, однако передачу этих запросов обеспечивает фреймворк. С точки зрения программиста, два последних варианта различаются между собой не существенно. В хорошем фреймворке разница должна быть лишь в одном параметре, который указывает должен ли подзапрос быть внутренним (внутри процесса), или внешним (вызывать настоящий http-запрос).
Плюсы:
- нет необходимости в параллельном запуске экземпляра веб-приложения.
Минусы:
- нет возможности использовать http-кеширование.
Масштабирование HMVC-приложения
Если веб-ресурс становиться достаточно популярным, может стать вопрос о том, что одного сервера для него недостаточно. И тогда встает вопрос о распределении нагрузки между несколькими серверами. Простейший вариант быстро распределить нагрузку — использовать несколько копий ресурса и репликацию базы данных. Но HMVC позволяет пойти другим путем — распределить между серверами задачи. Например, один сервер управляет статьями, один профилями пользователей, а третий комментариями. В случае достаточной изолированности модулей, для быстрой реализации этого достаточно лишь прописать в соответствующих запросах что они внешние, и указать адреса серверов для их обработки.
Напоследок
Создавая реальное веб-приложение вовсе не обязательно ограничиваться каким-то одним из вариантов реализации HMVC. В каждом конкретном случае можно выбирать тот, что лучше всего подходит именно для него. А варианты «сервер-серверный» и «внутри-серверный» вообще можно переключать «на ходу».
Автор: marenkov