PHP, Архитектуры → Мнение: несколько типовых структур MVC в ZF
Предисловие
Все нижеизложенное является лишь субъективным мнением автора и не претендует на истину в последней инстанции.
Ниже пойдет речь о вещах, понятных практически каждому разработчику MVC-приложений в Zend Framework.
Цель же в том, чтобы систематизировать и описать подходы, опираясь на которые, можно сделать тот или иной выбор при проектировании программы.
Суть
В зависимости от каждой конкретной задачи, может быть выбран тот или иной подход в организации структуры MVC-приложения. По мнению автора, нельзя назвать те или иные конструкции правильными или нет, так как все они обладают своими преимуществами и недостатками, но могут быть выбраны в качестве рабочего решения в зависимости от требований и условий проекта. Наша цель — показать какие структуры организации возможны и что влияет на их выбор.
В принципе все приложения MVC можно разделить на несколько типов: однородные и разнородные; модульные и не модульные; смешанные.
Давайте рассмотрим подробнее о чем идет речь.
Однородные приложения
Речь идет о приложениях, функционирующих в одной среде, например, только веб. Большинство обычных простых веб-сайтов (построенных на MVC, конечно же) принадлежат именно к этой категории приложений. База данных и интерфейсы к ней в виде HTML-страниц. Оговорим, что даже если какие-то задачи для сайта и функционируют в другой среде, то они достаточно простые и решаются в обход MVC-шаблона, а значит нам не интересны.
В этой категории приложений можно выделить несколько типовых конструкций их организации.
Конструкция «разделение приложений»
Данный шаблон применим, если мы будем расслаивать наше приложение по принципу их ролевой принадлежности. Например, посетителям сайта — Frontend, администраторам сайта — Backend. Т.о., в рамках структуры MVC мы создадим два различных приложения (по сути — 2 набора контроллеров и представлений).
application/ frontend/ controllers/ vews/ backend/ controllers/ views/
Достоинства:
- более прозрачный и менее трудоемкий слой прав доступа, или его полное отсутствие;
- более простая внутренняя реализация представлений и контролеров.
Недостатки:
- частое дублирование кода (особенно неприятно — контроллеров);
- как следствие — сложность поддержки (для внесения изменений нужно изменять код в нескольких местах)
Конструкция «разделение ролей»
В данном случае вся функциональность реализуется в рамках одного приложения, доступ к интерфейсам и разделение функций которого базируются исключительно на политике прав доступа. В качестве примера можно описать веб-сайт, на котором те или иные возможности (кнопки управления содержимым, разделы сайта) показываются пользователю в зависимости от наличия у него прав доступа к этим функциям.
application/ default/ controllers/ views/
Достоинства:
- отсутствие дублирующего кода;
- более простая внешняя структура контроллеров и представлений;
- простота поддержки (все изменения осуществляются в одном месте).
Недостатки:
- необходимость наличия слоя разделения прав доступа и/или его более высокая сложность;
- более сложная внутренняя реализация контроллеров и представлений.
Конструкция «разделение приложений и ролей»
Комбинированный подход. Объединяет два предыдущих подхода, а также их достоинства и недостатки. Сначала идет разделение по основным группам ролей на приложения (админам — Backend, посетителям — Frontend), а далее каждое приложение использует разделение ролей (Backend: administrator, newsmaker, article-editor; Frontend: guest, registered-user, moderator и т.д.).
Разнородные приложения
Речь идет о разделении приложений по принципу среды их функционирования. Например, если ваш программный комплекс достаточно сложен и должен функционировать в различных средах (веб, консоль, самостоятельный сервисный сервер и т.п.), данные конструкции могут быть вполне оправданы.
Конструкция «один контроллер»
Эта конструкция вполне применима, если приложения хоть и функционируют в разных средах, но все же обладают одинаковой функциональностью. В этом случае могут быть использованы одни и те же контроллеры действий, но разные представления. Ваша задача — научить фронт-контроллер правильно определять тип приложения, механизм разбора аргументов и выбор требуемых представлений.
application/ default/ controllers/ run/ bootstrap-www.php bootstrap-console.php views/
В данном случае каждый контроллер будет существовать в единственном экземпляре в независимости от типа приложения.
Достоинства:
- нет дублирования кода;
- прозрачная внешняя структура контроллеров;
- легкость поддержки (изменения производятся в одном месте).
Недостатки:
- ограниченная гибкость и масштабируемость приложения;
- сложная внутренняя реализация контроллеров.
Следует сказать, что таке реализации в реальной жизни все-таки встречаются редко.
Конструкция «много контроллеров»
В данном случае каждый тип приложения имеет свой набор контроллеров. Это применимо в случае, когда ваши приложения в каждой среде решают, в принципе, разные задачи. По сути, данный шаблон попадает и под определение «разделения приложений», но по несколько иному принципу.
www-application/ default/ controllers/ run/ bootstrap.php views/ console-application/ default/ controllers/ run/ bootstrap.php views/
Достоинства:
- гибкость и масштабируемость приложений;
- простота внутренней реализации контроллеров.
Недостатки:
- возможное дублирование кода;
- как следствие сложность поддержки (необходимо вносить изменения в нескольких местах).
Безусловно, разделение по типам среды функционирования приложений может быть скомбинировано с разделением по ролям. В этом случае вы получите то многообразие гибридных структур, которое они могут организовать. Главное — понимать что и как вы делаете, и каких последствий данного выбора следует ожидать.
Роль модели в структуре MVC-приложений
В каждом вышеописанном случае речь шла только об организации структуры контроллеров и в некоторой мере — представлений. В данном же случае мы будем рассматривать приложение в разрезе организации структуры модели.
Приложения с однородной моделью
Наиболее любимые мной приложения. Если ваше приложение имеет модель (и возможно даже очень обширную и сложную), объекты которой довольно тесно связаны между собой, вынесение такой модели в одно отдельное место — хорошее решение.
Это применимо, если можно так сказать, — для стационарных приложений, в которых разбивка на модуле если и есть, то весьма условная, поэтому свалить всю нашу модель «в кучу» — не проблема.
Например, вы можете положить вашу модель в:
application/models/
или захотите вынести ее в:
library/Model/
это уже ваше решение. Я лично, для разнородного приложения с множеством контроллеров предпочту, вполне вероятно, — второй вариант, а для однородного с разделением ролей — первый. И все-таки — это всего-лишь субъективное предпочтение.
Приложения с раздельной моделью (модульные приложения)
Если ваше приложение разбито на модули, при этом, каждый модуль должен быть легко изымаем и добавляем в систему, то это как раз тот случай. Чаще всего такие потребности существуют, например, у коммерческих систем управления сайтом, функциональность которых расширяется за счет увеличения модульной базы, да и цена которой в конечном итоге формируется по такому же принципу. В этом случае, предполагается, что у каждого модуля есть своя модель, теоретически, не связанная с моделями других модулей. Хотя на самом деле такое встречается крайне редко. И в этом случае приходится заботиться о связях между самими модулями (другими словами — о необходимости структурирования модели по принципу «не обязательна/необходима для»).
Структура такой модели могла бы выглядеть примерно так:
application/ default/ module_1/ controllers/ models/ views/ module_2/ controllers/ models/ views ...
То, как вы будете организовывать структуру вашей модели зависит от задачи, которую вы решаете и от ваших личных предпочтений. В конечном итоге, выбор за вами.
Заключение
Я благодарен всем, кого заинтересовал данный материал. Вполне вероятно, что приведенный здесь перечень возможных типовых структур не полный или несколько недоработан. Я с радостью приму любые замечания и дополнения.
Удачи в девелопменте!

17:32:20
Огромное спасибо за потрясающие идеи!!! Буду следить за блогом, много всего интересного. А мой блог о науке, надеюсь, тоже понравится
19:02:25
Вот по поводу последнего «Приложения с раздельной моделью (модульные приложения)» я использую следуюшую структуру:
application/
default/
controllers/
models/
views/
news/
controllers/
models/
views/
page/
controllers/
models/
views/
и т.д.
что и есть более «удобно» для модульного построения.
23:49:45
В целом все размышления могут быть применимы и не только для ZF
Похожу структуру используют и другие фреймворки.
Касательно «однородных» приложений и конструкции «разделение приложений» отмечу, что это пожалуй самый простой вариант реализации не только с точки зрения прав доступа, а и с других сторон, как то маршрутизации, подгрузки моделей.
Кстати, почему в этом варианте следует частое дублирование кода, ведь основная суть приложения должна быть сосредоточена в модели, которая будет общая для бэка и фронта. Или вы имеете ввиду, что бэкенд части решаются сходные задачи с фронтендом? например вывод статей и т.д.
Но по моему мнению любое приложение в идеале должно стремится к модульной структуре, так как независимые, транспортабельные модули это всегда хорошо. Кстати реализация модульной структуры в рамках ZF весьма интересная задача. С размышлениями на эту тему можно познакомиться по ссылке zendframework.ru/articles...n-zend-framework
18:44:58
Модульность предполагает универсальность. А универсальность имеет побочный эффект — сниженную производительность. Поэтому не всегда нужно писать модульно. Нужно искать баланс.
Но при разработке платформ, таких как CMS, особенно с коммерческим уклоном — абсолютно согласен, независимые легко присоединяемые/отсоединяемые модули — самое оно. В случае же с хайлоадами — все сложнее. Это лишь субъективное мнение.
> Кстати, почему в этом варианте следует частое дублирование кода, ведь основная суть приложения должна быть сосредоточена в модели, которая будет общая для бэка и фронта.
Так как код сосредоточен не только в модели. но и в контроллерах (представления тоже код, кстати). Простой пример — список пользователей сайта — для администратора и для публичного просмотра — в одном случае будет сосредоточен в одном контроллере и предствалении в другом случае — в разных. По сути — дублирование. Сам код в одном случае будет более сложным в другом более простым.
19:32:12
Модульность предполагает универсальность. А универсальность имеет побочный эффект — сниженную производительность. Поэтому не всегда нужно писать модульно. Нужно искать баланс.
Согласен в целом, но если говорить о ZF то его использование это уже значительное снижение производительности. Нагрузка вызванная реализацией модульной структуры значительно меньше. А вот на саму хорошую реализацию можно потратить не мало человекочасов.
06:42:52
Если брать ZF в чистом виде — соглашусь со словом «значительное». Если же немного заняться его оптимизацией — он даже очень быстр.
14:07:19
Если брать ZF в чистом виде — соглашусь со словом «значительное». Если же немного заняться его оптимизацией — он даже очень быстр
Вы имеет ввиду вырезать require_once и сливать классы в один файл? Или возможно у вас есть свои не такие широкоизвестные рецепты? Было бы весьма интересно услышать
Но в любом случае универсальность вызванная модульной структурой будет существенно меньше влиять на производительность нежели сам фреймворк.
09:50:05
> Вы имеет ввиду вырезать require_once и сливать классы в один файл? Или возможно у вас есть свои не такие широкоизвестные рецепты? Было бы весьма интересно услышать
акселератор+вышеперечисленное+кеширование
> Но в любом случае универсальность вызванная модульной структурой будет существенно меньше влиять на производительность нежели сам фреймворк.
Это все теория. И предположение. А вот в каждом конкретном случае выбор реализации того или иного компонента в виде модуля может СУЩЕСТВЕННО снизить производительность приложения. Все зависит от архитектуры самого модуля, приложения и путей интеграции этого модуля с другими компонентами системы. И бывает так, что проще отказаться от модульности данного компонента в угоду производительности. Повторюсь — В ОТДЕЛЬНЫХ СЛУЧАЯХ. Это не значит, что не нужно писать модульно — это значит — не всегда нужно писать модульно.
Да и еще я должен наверное оговорить, что означает использовать фреймворк. В частности Zend. Я, как правило, пользуюсь только его реализацией MVC и некоторыми отдельно-взятыми компонентами. Если же сконструировать страницу которая заюзает 90% пакетов из всего фреймворка — я с вами соглашусь на все 100% безоговорочно — ничто уже не повлияет на производительность так как сам фреймворк
З.Ы, В целом я не оспариваю ваше мнение, а подтверждаю его. Все вы правильно говорите. Как правило. Я лишь напоминаю о том, что бывают и исключения
02:22:44
Благодарен. Появилась еще одна мысль, но она нуждается в поверхностной реорганизации старой мысли, займусь в выходные. Позже поделюсь с читателями блога!
17:33:56
Только-только начинаю постигать процесс разработки приложений, отходя от написания простейших скриптов. Знаний катастроически не хватает. Статья показала одну из проблем и подсказала варианты решений и дальнейших поисков. Спасибо, было интересно.
05:37:51
Buy:Synthroid.Mega Hoodia.Zyban.100% Pure Okinawan Coral Calcium.Lumigan.Zovirax.Nexium.Accutane.Human Growth Hormone.Retin-A.Prevacid.Prednisolone.Actos.Valtrex.Petcam (Metacam) Oral Suspension.Arimidex...