Масштабирование веб-приложений с помощью HMVC

http://techportal.ibuildings.com/2010/02/22/scaling-web-applications-with-hmvc/
  • Перевод
Последние десять лет мы наблюдаем второй цикл веб-дизайна – сайты превращаются в приложения и уже практически не появляется новых проектов, не обладающих некой долей интерактивности. Увеличение сложности ПО, разрабатываемого для интернета, вызвало необходимость в структурированном и взвешенном проектировании приложений.

На сегодняшний день наиболее часто используемым паттерном проектирования сайтов является Модель-Вид-Контроллер (MVC). Повсеместное его использование отчасти вызвано успехом и популярностью фреймворка Ruby on Rails. Сейчас MVC является практически синонимом веб-разработки среди всех платформ.

При выполнении задач, активно нагружающих процессор, современные сайты все больше полагаются на выделенные ресурсы. Этому, в частности, поспособствовало открытие компаниями Amazon и Google облачных сервисов, которые позволяют разработчикам существенно уменьшить нагрузку на процессоры их собственных серверов. Каждый сервис обычно проектируется в виде отдельного элемента ПО, который запускается внутри своего домена и использует свои собственные ресурсы.

Когда имеешь дело со скромными бюджетами, обычно довольно сложно убедить клиентов в преимуществах финансирования более чем одного завершенного фрагмента программного обеспечения. Как показывает мой опыт, множество из них придерживаются мнения, что масштабируемость не является актуальной задачей. Они «с нетерпением ждут того дня, когда придется этим обеспокоиться».

Для уменьшения первоначальных вложений обычно принимают решение о том, что приложение должно быть спроектировано в виде целостной программы, содержащей все требуемые функции. Если сайт быстро обретет популярность, это станет проблемой. У меня остались не очень приятные впечатления от рефакторинга плохо масштабируемых кодовых баз. К тому же, это может потребовать большого количества ресурсов и денег. В идеале приложения должны расти по мере необходимости и не требовать в процессе этого крупных финансовых затрат.


Паттерн Иерархические-Модель-Вид-Контроллер



Паттерн Иерархические-Модель-Вид-Контроллер (HMVC) является расширением MVC, которое позволяет решить множество уже упомянутых проблем масштабируемости. Впервые он был описан в июле 2000 года в статье блога JavaWorld под названием «HMVC: многослойный паттерн для разработки клиентских уровней»; хотя существует мнение, что авторы на самом деле переосмыслили другой паттерн – Представление-Абстракция-Управление (PAC) – описанный в 1987 году. Цель статьи состояла в том, чтобы продемонстрировать, как HMVC может использоваться для создания масштабируемых сайтов и при проектировании настольных приложений с графическими интерфейсами.

HMVC – это набор традиционных триад MVC, работающих как одно приложение. Каждая триада совершенно независима и может выполняться при отсутствии любой другой. Все запросы к триадам должны использовать интерфейс контроллера, никогда не подключайте модели или библиотеки вне их домена. Физическое местонахождение триады на сервере не имеет значения, главное, чтобы она была доступна из всех других частей системы. Характерными особенностями HMVC являются поощрение повторного использования кода, упрощение тестирования отдельных частей системы и гарантия того, что приложение может быть усложнено и расширено без особых сложностей.

Успешное проектирование приложения на основе паттерна HMVC невозможно без разбиения его функций на отдельные системы. Каждая такая система должна представлять собой одну триаду MVC, независимо управляющую методами хранения и представлением внутри более крупного HMVC приложения. В настоящее время существует несколько фреймворков, поддерживающих HMVC без дополнительных модулей. Один из них – Kohana PHP третьей версии, который изначально создавался с ориентацией на HMVC. Далее во всех примерах я буду использовать этот фреймворк.

Kohana 3 использует встроенный объект Request для вызова других контроллеров. Запросы могут быть как внутренние – к контроллерам приложения, так и внешние – к веб-сервисам. В обоих случаях используется все тот же класс Request. Если триада MVC выносится на другой сервер, требуется изменить лишь один параметр.

<?php
class Controller_Default extends Controller {
 
	public function action_index()
	{
		// Пример внутреннего запроса
		$internal_request = Request::factory('controller/action/param')
			->execute();
 
		// Пример внешнего запроса
		$external_request = Request::factory('http://www.ibuildings.com/controller/action/param')
			->execute();
	}
}

Внутренний запрос требует корректного пути с указанием контроллера и экшена, а создание запроса ко внешнему ресурсу сводится к предоставлению полного URL. Эта особенность делает внутренние и внешние запросы взаимозаменяемыми без каких-либо сложностей, поэтому вынесение триад за пределы основного сервера является довольно простой задачей.

Использование в Kohana класса Request для получения данных из внутренних контроллеров может напомнить вам перенаправление экшенов в других фреймворках, например, Zend Framework. На самом же деле, это два довольно разных метода. Объекты Request в Kohana могут работать в изоляции как уникальные запросы, а при перенаправлении экшенов каждый из них существует внутри создающегося запроса. Ниже пример для демонстрации этого.

Контроллер Default – /application/controllers/default.php
<?php
// Контроллер для первого запроса
class Controller_Default extends Controller
{
	public function action_index()
	{
		// Если метод запроса был GET
		if ($this->request->method === 'GET')
		{
			// Вывод POST, напечатает   array (0) { empty }
			var_dump($_POST);
 
			// Создание нового запроса к другому ресурсу
			$log = Request::factory('/log/access/'.$page_id);
 
			// Указание POST в качестве метода запроса
			$log->method = 'POST';
 
			// Занесение каких-то данных для отправки
			$log->post = array(
				'uid'      => $this->user->id,
				'ua'       => Request::user_agent('browser'),
				'protocol' => Request::$protocol,
			);
 
			// Выполнение запроса
			$log->execute();
 
			// Вывод POST, опять напечатает  array (0) { empty }
			var_dump($_POST);
		}
	}
}

Контроллер Log – /application/controllers/log.php
<?php
// Контроллер для второго запроса
class Controller_Log extends Controller
{
	public function action_access($page_id)
	{
		// Когда запрашивается из экшена index (который выше)
		// выводит отправленные переменные из второго запроса
		// array(3){string (3) 'uid' => int (1) 1, string (2) 'ua' => string(10) 'Mozilla...
		var_dump($_POST);
 
		// Создание новой модели Log
		$log = new Log_Model;
 
		// Присвоение значений и сохранение
		$log->set_values($_POST)
			->save();
	}
}

Пример выше показывает независимость, доступную объекту Request. Первоначально GET-запрос вызывает экшен index контроллера Default, в ответ на что создается POST-запрос к экшену access контроллера Log. Экшен index назначает значения трем переменным, которые не доступны глобальному массиву $_POST из первого контроллера. Когда выполняется второй запрос, в $_POST уже содержатся переменные, которые мы для него сделали доступными. Заметьте, что после завершения $log->execute() в контроллере Index данные из $_POST исчезают. В других фреймворках реализация такого взаимодействия требует создания нового запроса с помощью, например, Curl.

Gazouillement, сервис микроблогинга


Продемонстрировать мощь HMVC поможет вымышленный сервис коротких сообщений (статусов) под названием Gazouillement, который по сути является аналогом Twitter. Предположим, что он был спроектирован на основе сервис-ориентированной архитектуры (SOA), чтобы веб-интерфейс и компоненты для работы с сообщениями и отношениями были разделены.

В первое время трафик будет относительно небольшим, ведь сервис новый и люди о нем еще не знают. Поэтому ничего страшного не случится, если пока что вся логика приложения будет выполняться на одном сервере.

Давайте напишем контроллер для отображения домашней страницы пользователя. На ней будут его самые свежие статусы и список людей, которых пользователь зафолловил.

Контроллер Index – /application/controllers/index.php
<?php
// Обрабатывает запрос к http://gazouillement.com/samsoir/
class Controller_Index extends Controller {
 
	public function action_index()
	{
		// Загрузка пользователя (samsoir) в модель
		$user = new Model_User($this->request->param('user'));
 
		// Если пользователь не загружен, выкидывает исключение 404
		if ( ! $user->loaded)
			throw new Controller_Exception_404('Unable to load user :user', array(':user' => $this->request->param('user')));
 
		// Загрузка пользовательских сообщений в формате xhtml
		$messages = Request::factory('messages/find/'.$user->name.'.xhtml')
			->execute()
			->response;
 
		// Загрузка пользовательских отношений в формате xhtml
		$relations = Request::factory($user->name.'/following.xhtml')
			->execute()
			->response;
 
		// Вывод в браузер вида домашней страницы пользователя
		// и привязка к нему пользователя, сообщений и отношений
		$this->request->response = View::factory('user/home', array(
			'user'      => $user,
			'messages'  => $messages,
			'relations' => $relations,
		));
	}
}

Теперь рассмотрим, что же делает Controller_Index::action_index(). Вначале экшен пытается загрузить пользователя на основе параметра user из URL. Если это не удается, выводится страница 404. Затем, с целью получения сообщений в формате xhtml, создается новый запрос, который в URI запроса использует имя пользователя из соответствующего свойства класса. Аналогичным образом и в том же формате запрашиваются пользовательские отношения. В итоге создается объект класса View с привязкой к нему пользователя, его сообщений и отношений.

При загрузке существующих сообщений и отношений через новый запрос, вся логика приложения для каждого сервиса остается абстрагированной от веб-сайта. Такая архитектура дает два значительных преимущества по сравнению с привычным выполнением контроллеров:
  1. Контроллера не касается никакая часть логики работы сервиса сообщений. Единственное требование – результат должен быть в формате xhtml. Во время выполнения контроллера ни одна дополнительная библиотека или расширение не загружались.
  2. Каждый контроллер отвечает лишь за одно конкретное задание, заметно упрощая таким образом написание юнит-тестов для них.

Из-за только что продемонстрированного уровня абстракции невозможно увидеть, чем занимаются сервисы. Поэтому давайте обратим внимание на контроллер сервиса сообщений. Начнем с роута в загрузочном файле, который будет отвечать за внутреннее управление запросами сообщений. Класс Route в Kohana занимается парсингом внутренних URL, связывая переданные элементы URI с контроллерами, экшенами и параметрами.

Настройка роута – /application/bootstrap.php
Route::set('messages', 'messages/<action>/<user>(<format>)', array('format' => '\.\w+'))
->defaults(array(
	'format'     => '.json',
	'controller' => 'messages',
));

Это создает роут для сервиса сообщений, который пока что находится внутри домена основного приложения. Запрос через адрес messages/find/samsoir.xhtml будет перенаправлен контроллеру messages, экшену find() которого передадутся в виде параметров 'user' => 'samsoir' и 'format => '.json'.

Контроллер Messages – /application/controllers/messages.php
<?php
class Controller_Messages extends Controller {
 
	// Форматы вывода, поддерживаемые этим контроллером
	protected $supported_formats = array(
		'.xhtml',
		'.json',
		'.xml',
		'.rss',
	);
 
	// Контекст пользователя в этом запросе
	protected $user;
 
	// Код ниже выполнится перед экшеном
	// Мы проверяем корректность формата и пользователя
	public function before()
	{
		// Проверка на наличие формата в списке поддерживаемых
		if ( ! in_array($this->request->param('format'), $this->supported_formats))
			throw new Controller_Exception_404('File not found');
 
		// Проверка корректности имени пользователя
		$this->user = new Model_User($this->request->param('user'));
 
		if ( ! $this->user->loaded())
			throw new Controller_Exception_404('File not found');
 
		return parent::before();
	}
 
	// Этот метод ищет сообщения пользователя
	public function find()
	{
		// Загрузка сообщений со связью пользователя и сообщений как 1:M
		$messages = $this->user->messages;
 
		// Назначаем ответ на запрос, используя метод prepare для корректного вывода
		$this->request->response = $this->_prepare_response($messages);
	}
 
	// Метод для подготовки выводимых данных
	protected function _prepare_response(Model_Iterator $messages)
	{
		// Возвращает сообщения, отформатированные в соответствии с форматом
		switch ($this->request->param('format') {
			case '.json' : {
				$this->request->headers['Content-Type'] = 'application/json';
				$messages = $messages->as_array();
				return json_encode($messages);
			}
			case '.xhtml' : {
				return View::factory('messages/xhtml', $messages);
			}
			default : {
				throw new Controller_Exception_404('File not found!');
			}
		}
	}
}

Процесс получения сообщений пользователя в деталях показан в контроллере Controller_Messages. Все методы и свойства привязаны только к контексту сообщений, включая пользовательские отношения. Давайте рассмотрим код этого контроллера, чтобы понять происходящее.

Объект Request всегда перед вызовом указанного экшена обращается к методу before(). Это позволяет выполнять перед основной работой различные рутинные операции. Метод before() вначале проверяет запрашиваемый формат на предмет поддерживаемости, а затем имя пользователя на корректность. Если before() завершается без выкинутых исключений, дальше объект Request вызовет экшен find(). Он загружает сообщения пользователя в виде объекта Model_Iterator. Необходимо заметить, что итератор будет пуст, если в пользовательском отношении не будут найдены сообщения. Завершается все передачей итератора сообщений в метод _prepare_response(), который корректно форматирует данные для вывода и выставляет все нужные заголовки.

Контроллеры Controller_Index и Controller_Messages были оба выполнены единым запросом к приложению. О существовании друг друга они, тем не менее, не знали. Каждый разработчик способен прочитать текущую кодовую базу и понять, что и где выполняется. Это еще одна отличная особенность HMVC – легкость в обслуживании.

После окончания работы над кодом остальных сервисов руководители компании выразили удовлетворение первым этапом разработки и разрешили развертывание на открытом сервере для ограниченного бета-тестирования. После пары недель приложение готово к массовому использованию. В течение следующих месяцев аудитория будет постоянно расти, а вся архитектура сайта будет улучшаться и оптимизироваться.

Эффект Стивена Фрая


Стивен Фрай (@stephenfry) – один из самых известных пользователей Twitter с более чем 2 миллионами фолловеров. Он способен отключать сайты одной лишь публикацией URL в своем микроблоге, создавая DDOS-атаку на сервер.

Gazouillement последние несколько месяцев активно набирал пользователей. Время отклика в силу этого возросло, но оно все еще в рамках допустимого. Вдруг некий пользователь Twitter с огромным количеством фолловеров, вроде Стивена Фрая, размещает у себя сообщение со ссылкой на Gazouillement. И тут у сервиса начинаются настоящие проблемы.

Первой станет большой объем трафика. Приложению вместо обычных нескольких сотен запросов в секунду придется обрабатывать тысячи. При таком сценарии Gazouillement начнет испытывать перегрузку и возможно “положит” сервер. Станет очевидным, что приложению требуется некоторая оптимизация и улучшение для того чтобы справляться с заметно увеличивающимся трафиком.

Анализ кода с целью улучшения производительности – занятие не из простых. В предыдущих статьях на TechPortal рассматривались утилиты вроде XHProf, предназначенные для глубокого анализа памяти и процессора во время выполнения кода. Kohana PHP имеет встроенную в ядро программу для анализа производительности под названием Profiler. Но она является дополнением, а не заменой XHProf и подобных ей утилит. Profiler позволяет разработчикам обнаружить медленные места в выполняющемся коде, которые затем могут быть в деталях исследованы с помощью XHProf или еще одного встроенного во фреймворк модуля – Codebench.

Включить Profiler можно изменением лишь одного параметра в загрузчике.

Загрузчик – /application/bootstrap.php
//-- Environment setup --------------------------------------------------------
Kohana::$profiling = TRUE;

И наконец, добавьте вид Profiler’а в конец выводимого контроллером ответа. Это лучше всего сделать через метод after(), тогда профайлер будет показываться для всех экшенов.

Контроллер Index – /application/controllers/index.php
public function after()
{
	// Добавление в контроллер статистики от профайлера
	$this->request->response .= View::factory('profiler/stats');
}

После включения профайлера его информация начинает появляться внизу каждого экшена из этого контроллера. Лучше всего создать класс, наследующий Controller, и вставить вид профайлера в метод after() этого класса. Теперь все системные контроллеры выводят отладочную информацию профайлера во время разработки.



Профайлер сообщает о работе фреймворка, группируя вместе различные контексты (инициализация, запросы, обращения к базе данных, и т.д.) и выводя соответствующее им время работы процессора и объем выделенной памяти. Видя перед собой информацию по каждому созданному запросу, гораздо проще вычислить те, которые занимают слишком много времени. И когда это происходит, уже можно применять утилиты вроде XHProf для более детального анализа.

Масштабируем Gazouillement


Глобальный анализ производительности Gazouillement показал, что большие трудности возникают при получении сообщений. Хотя команда разработчиков произвела рефакторинг и оптимизацию соответствующей триады MVC, нужные показатели производительности так и не были получены. После не особо результативного улучшения процессоров и памяти на сервере, руководители компании соглашаются на горизонтальное масштабирование приложения, начиная с системы сообщений.

При архитектуре MVC новый сервис обычно должен пройти стадии проектирования, разработки, приемочного тестирования и внедрения. Этот процесс может занять месяцы и потребовать существенных вложений. Но в Gazouillement, по сути, будет интегрироваться новый фрагмент программного обеспечения, а не новая программа.

В приложениях на базе HMVC новые сервисы на основе существующей кодовой базы можно создавать в кратчайшие промежутки времени. Поскольку все взаимодействие с сервисом сообщений осуществлялось через главный контроллер, изменить нужно лишь запросы сообщений.

Сервис сообщений был перенесен на другой сервер и оптимизирован для работы с базой данных. Этот новый сервер выполняет только действия, связанные с сообщениями, таким образом заметно повышая скорость всех подобных операций.

Контроллер Index – /application/controllers/index.php
<?php
// Обрабатывает запрос к http://gazouillement.com/samsoir/
class Controller_Index extends Controller {
 
	protected $_messages_uri = 'http://messages.gazouillement.com';
 
	public function action_index()
	{
		// Загрузка пользователя (samsoir) в модель
		$user = new Model_User($this->request->param('user'));
 
		// Если пользователь не загружен, выкидывается исключение 404
		if ( ! $user->loaded)
			throw new Controller_Exception_404('Unable to load user :user', array(':user' => $this->request->param('user')));
 
		// -- НАЧАЛО ИСПРАВЛЕННОГО КОДА --
 
		// Новый URI сообщений ко внешнему серверу
		$messages_uri = $this->_messages_uri.'/find/'.$user->name'.xhtml';
 
		// Загрузка сообщений для пользователя в формате xhtml
		$messages = Request::factory($messages_uri)
			->execute()
			->response;
 
		// -- КОНЕЦ ИСПРАВЛЕННОГО КОДА --
 
		// Загрузка пользовательских отношений в формате xhtml
		$relations = Request::factory($user->name.'/following.xhtml')
			->execute()
			->response;
 
		// Вывод в браузер вида домашней страницы пользователя
		// и привязка к нему пользователя, сообщений и отношений
		$this->request->response = View::factory('user/home', array(
			'user'      => $user,
			'messages'  => $messages,
			'relations' => $relations,
		));
	}
}

В коде выделено крохотное изменение в контроллере Controller_Index::action_index(). Запрос сообщений теперь идет не к экшену внутреннего контроллера, а к сервису сообщений, который работает на поддомене messages.gazouillement.com. То, что обычно привело бы к пересмотру всей архитектуры проекта, стало небольшим ее дополнением. И поскольку изменилось так мало кода, ассоциативное и приемочное тестирования будут гораздо короче.

Это лишь один пример того, как паттерн Иерархические-Модель-Вид-Контроллер позволяет разработчикам проектировать веб-приложения, которые с самого начала могут расширяться и вертикально, и горизонтально. Мы увидели, как объект Request позволяет контроллерам возвращать корректные данные для каждого контекста, используя простой интерфейс. Видели, как он же делает вынесение части приложения за пределы сервера весьма легкой задачей. И стоимость масштабирования была относительно мала в силу небольшого количества изменений, чему руководители компании были очень рады.

Больше информации о Kohana вы можете найти на сайте фреймворка, а файлы Kohana PHP 3 можно скачать с Github. Еще есть документация по api: http://v3.kohanaphp.com/guide.

Класс для запросов, использованный в этом примере, в данный момент доступен в ветке разработки ядра Kohana на моем личном аккаунте в github – http://github.com/samsoir/core. Если используется официальный дистрибутив Kohana PHP 3.0, необходимо видоизмененное расширение класса для запросов.
Метки:
Поделиться публикацией
Похожие публикации
Комментарии 41
  • –27
    Гнусно воспользуюсь авторской возможностью оставить первый комментарий и попрошу инвайт на Автокадабру. danneutron@gmail.com. Предварительное спасибо.
    • +6
      Вот только не понятно, почему все подзапросы, судя по схеме, предлагается делать из контроллеров. Помоему самый смак как раз в том, что можно из вьюшки вызвать фрагмент страницы с баннерами, не засоряя каждый контроллер логикой связанной с ротацией. Или еще какие-то повторяющиеся, требующие своего контроллера, блоки страницы.
      • 0
        Эм. Всегда для этих целей использовал хуки.
      • +3
        Неужели запрос к другому серверу будет быстрее, чем вызов внутри запущенного приложения? Если узкое место — это база данных сообщений, то можно перенести только базу и перенастроить модель.
        • 0
          Насколько я понял, изначально был вызов к локальному сервису. Просто после оптимизации обращения ушли на соседний сервер, а затраты на соединение остались без изменений.
          • 0
            Я тоже так понял. Только или пример неудачный или я не вижу смысла в такой оптимизации. Делать последовательные вызовы к другим серверам будет намного накладней, чем локальный код. В большинстве случаев оптимизации требует именно база данных, а это происходит другими способами. Если вам нужно оптимизировать именно код, то скорее всего вы одна из ста компаний мира с многомиллионной аудиторией, и вы не используете Kohana.
            А если уж оптимизировать таким образом, то чтобы избежать задержек, вызовы к серверу сообщений делать аяксом на клиенте.
            • 0
              Безусловно, преждевременна оптимизация — зло. Несмотря на это я, к примеру, при разработке каждого проекта планирую его масштабирумость. И пусть генерация обычной страницы из-за внутренних подзапросов будет на 0,2 секунды больше, зато перенос части данных или логики за пределы приложения будет безболезненным и практически не повлияет на быстродействие.
              • 0
                Нифига себе оптимизация, нифига себе не повлияет. 0.2 секунды.
          • 0
            Как я понял схема такая: два контроллера занимают по 50% cpu и не успевают обрабатывать запросы (БД пускай уже на другом хосте), переносим один контроллер на другой хост и отдаем весь цпу первому.
          • –3
            Оказывается, первый же написанный мной веб-фреймворк, по нелепой случайности обладал чудо-свойством масштабируемости, и всё за счёт возможности вызывать один контроллер из другого! Поразительно.
            • +2
              Если бы вы внимательно прочитали статью, вы бы увидели, что HMVC — это не «возможность вызывать один контроллер из другого». Точнее, это лишь малая его часть. Вызвать один контроллер из другого дело нехитрое и ничем не отличается от обычного создания экземпляра класса:

              $controller = new Controller;
              $controller->action();
              

              Вся прелесть HMVC как раз в том, что тут создаются полноценные запросы к каким-либо частям приложения, вероятно даже внешнего, которое никак не зависит от вызывающего, и поэтому (если говорить в контексте данной статьи) может быть в частности перенесено на другой сервер, что сильно упрощает горизонтальное масштабирования приложения
              • 0
                Большое спасибо за материал — понятно и просто. И перевод хороший :)
                Хоть пока что сижу на CodeIniter ( в котором тоже можно прикрутить HMVC), но я так и н понял — почему я не могу штатными средствами, без применения иерархичности триад MVC масштабировать свое приложение горизонтально?
                Ведь вызвать другой контроллер можно из любой точки приложения вышеприведенным примером:
                $controller = new Controller;
                $controller->action();
                

                Разъясните, пожалуйста — хочу понять разницу.
                • +1
                  Вообще-то не я автор перевода, но раз уж вы адресовали вопрос мне — попробую ответить :)
                  Ведь вызвать другой контроллер можно из любой точки приложения вышеприведенным примером
                  Несомненно, это можно сделать. Однако, это не всегда возможно, потому что парадигма HMVC предполагает изоляцию параметров запроса, чего невозможно добиться «обычными» средствами.

                  Если сказать проще, то основной запрос работает с одними параметрами ($_GET, $_POST), а подзапрос с другими. При этом обращение к параметрам идёт не напрямую, а с помощью специальных методов, например вместо $_POST['param'] нужно писать $this->request->post('param');

                  Плюс возможность сделать запрос на сторонний сервер (хотя по большей части это просто обёртка над CURLом или HTTP-классами)
                  • 0
                    Спасибо. Так вот использование того же curl'а полностью может заменить HMVC? (да, это некошерно, но вопрос о возможности).
                    Можете привести пример приложения, когда штатными стредствами HMC-парадигмы нельзя обойтись, а нужно использовать иерархическую ее структуру?
                    • +1
                      В части внешних запросов да, может полностью заменить. Но внутри приложения — нет. Во-первых, не будете же вы curl'ом обращаться из приложения к самому себе, когда грубо говоря можно просто подключить файл через require. А во-вторых, вы тогда не сможете отличить начальный запрос от текущего (внутреннего) запроса, разве что по IP-адресу, но это костыли. Тем более, что «нормальный» HMVC умеет работать как с текущим запросом, так и с начальным.

                      Можете привести пример приложения, когда штатными стредствами HMC-парадигмы нельзя обойтись, а нужно использовать иерархическую ее структуру?
                      Нет, не могу :) Если не учитывать вышесказанное, то HMVC — это просто ещё один способ повторного использования кода и его можно реализовать другими способами
                      • 0
                        В общей сложности понятно. На счет curl'а — конечно костыли — вообще никому не советую заменять одно другим (ну разве уж в очень исключительных случаях).
                        В общем случае пытаюсь провести параллель между KO3 и CI21 (+HMVC). Пока не вижу особых преимуществ в пользу первой, основывающих сделать переход на нее.
                        Спасибо.
                        • 0
                          С вашего позволения немного пооффтоплю: я на Кохану ушёл с CI ещё задолго до появления в ней HMVC и совсем по другим причинам. О чём не жалею
                          • 0
                            Извольте тогда спросить — по каких? (Исключая то, что доступно в CI)
                            • 0
                              За CI 2.x не скажу, так как вообще с ним не знаком. В то время когда я поменял его на Kohana были версии CI 1.6, Kohana 2.2. Что меня привлекло тогда:
                              — PHP5 (против PHP4 у CI)
                              — это был форк CI, что означало с одной стороны простоту перехода, а с другой — новые фичи (бóльшая гибкость, проще расширяемость и т.д.)

                              KO3 — это уже отдельный разговор, но по моим субъективным ощущениям переход KO2 → KO3, это всё равно что пересесть с мопеда на мотоцикл
                              • 0
                                Кроме HMVC, ORM, изменений i18n и другой разрабатываемой команды что то отличает 2 от 3?
                                А то сейчас CI2 дальше ушел, чем тогдашняя KO2.2
                                • 0
                                  Конечно, прогресс не стоит на месте. Все (живые) фреймворки развиваются. Если сравнивать KO2 и KO3 — можно сказать, что это абсолютно разные фреймворки и поэтому ставить вопрос в ключе «что отличается» не совсем корректно.
                                  • 0
                                    Вопрос «чем отличается А от Б» применим ко всему :) Ну, не хотите — как хотите.
                                    • 0
                                      Ну если вы настаиваете — я тогда немного изменю вопрос со «что отличается?» на «что общего?» и отвечу — название :)
          • 0
            C недавнего применяю HMVC в CodeIgniter. Каждая такая триада — это отдельный независимый модуль. Есть часть стадартных модулей, которые можно использовать в последующих проектах + специфический функционал под конкретную задачу…
            • 0
              Давно применяю HMVC еще даже со временем модуля Wick, удобно особенно при создании CMS было.
            • 0
              Достаточно подробно, спасибо за перевод.
              • 0
                Спасибо за интересный перевод отличной статьи. Не поленились даже картинки перевести :)
                От себя могу добавить, что полная поддержка HMVC (внешние запросы и передача отдельных данных (POST, GET) другому запросу) появилась в версии 3.1, которая сейчас находится в стадии RC2 и должна в скором времени увидеть свет
                • 0
                  Простите за непонимание, но можете на этом примере показать как будет осуществляться разграничение прав в разных Триадах одновременно?
                  • 0
                    Не знаю как в Kohana, но можно ли например шарить Модели между триадами, чтобы например модель User, Page были доступны всем триадам?
                    • 0
                      Конкретная модель (как экземпляр класса) не будет доступна, потому что триады независимы друг от друга. Однако данные, из которых наша модель строится могут быть доступны, поэтому сформировать эту модель внутри отдельно взятой триады можно.
                      • 0
                        Жаль, в CodeIgniter если положить модель в общую папку app/models то эта модель будет доступна любому модулю (триаде) из папки models
                        А если ложить модели уже в папки самих модулей, триад, например modules/news/model/usermodel.php
                        то эта модель может быть доступна например из Модуля (триады) контроллера Articles например так
                        $this->news->usermodel->…
                        В реальных проектах всё равно без зависимостей не обойтись или DI использовать или просто контроллировать их.
                        • 0
                          Подождите. Физически модель (в виде файла) легко может быть доступна, если система работает в рамках одного физического сервера. Говоря о недоступности модели как экземпляра класса, я имел ввиду недоступность той самой модели, именно того экземпляра. Под моделью мы понимаем не только набор методов в файле, а также еще и данные, которые в этой модели инкапсулированы.
                          Словом, модель легко может быть доступна, но смысл в максимальной изоляции. Если же изоляция неизбежна, то триада А, делая запрос к триаде Б, может передать ей необходимые данные для формирования этой самой модели самостоятельно. Потому что сама триада А для получения этой модели обращалась к модели В :)
                          • 0
                            Поправка: Потому что сама триада А для получения этой модели обращалась к триаде В :)
                            • 0
                              Только через посредника, которым выступает всё тот же контроллер?
                            • 0
                              Похоже не совсем донес мысль, не в физическом плане, а разумеется результаты выполнения, инкапсулированные данные модели одной триады, доступны ли они контроллеру из другой триады?
                              Например, есть модуль
                              News
                              у него контроллер News и модель NewsModel

                              и модуль Users
                              у него контроллер Users и модель Users

                              можно ли из контроллера News вызвать модель Users чтобы получить например список всех пользователей кто связан с новостью например чтобы это потом отобразить во вью самой новости?
                              • 0
                                Так в этом и смысл!

                                Мы делаем запрос из News в Users и говорим: эй, Users, дружище, дай-ка нам список всех пользователей, которые связаны со статьей N.
                                Вы саму суть раскрыли и затронули :) В этом и суть: список пользователей формируется не внтури News, а запрашивается извне, из модуля Users. Причем Users не знает зачем эти данные нужны, он их просто выбирает и отдает. А News уже использует.
                                • 0
                                  Теперь понял, да, в целом так же как и в CodeIgniter, просто в CI гибче, можно обходить контроллер (не подгружая класс) и запрашивать чужие модели напрямую и иметь общие модели для всех модулей модели.
                                • 0
                                  А теперь представьте, что у нас есть сервер, на котором хранятся только пользователи, а также есть другой, на котором хранятся только новости. Для быстродействия.
                                  А третий сервер у нас фронтенд, который оба вышеуказанных теребит и выдает новости вместе с пользователями по запросу :)
                                  Красота!
                        • +3
                          Вся нагрузка при описанной архитектуре остается на сервере приложений, который обрабатывает входящие запросы. Так как ответы сервисов будут теперь генерироваться дольше, за счет выноса оных на отдельные сервера, это несомненно создаст дополнительную нагрузку на основной сервер. Решением в таком случае было бы использовать какой-нибудь «железный» контент-свитчер, перенаправляющий запросы на разные сервера приложений. С таким подходом, использовать HMVC не обязательно.
                          • 0
                            Увеличит нагрузку одним, но полностью уберёт другой. Увеличится ли итоговая нагрузка специфично для приложения.

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.