Как стать автором
Обновить
130.18
2ГИС
Главные по городской навигации

Слоистая архитектура на основе фреймворка yii

Время на прочтение8 мин
Количество просмотров28K

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

Хорошие примеры такой среды — это Яндекс (поиск, Директ, карты, почта, вертикальные и внутренние сервисы) или Google. Понятно, что у перечисленных гигантов технологии в каждом продукте свои, но если взять компанию поменьше и работающую в более узкой предметной области, то можно предположить, что веб-продукты будут выполнены на одних технологиях (языках программирования, фреймворках и.т.д).

Именно об опыте в организации архитектуры всей линейки продуктов у такой компании я и хочу рассказать.

Наши внешние веб-продукты — это онлайн-версия, открытый справочный API и картографический, сервис отзывов. Внутренние: CRM, биллинг, алгоритмические вычисления, системы статистики и
экспорта-импорта данных.

Выведем два понятия:

Веб-инфраструктура — совокупность веб-продуктов компании, связанных между собой и работающих в близких предметных областях.

Технологическая база — единая кодовая база для веб-инфраструктуры компании. Содержит набор программных блоков с высоким и низким уровнем абстракции. Высокоуровневые блоки — это уже наработанные командой разработчиков сущности в предметной области компании. Низкоуровневые блоки — это, например, набор библиотек или фреймворков.

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

Нашей целью было создание такой веб-инфраструктуры. Первое, с чего мы начали — подготовка технологической базы. Нами были выбраны следующие фреймворки и технологии:
  • PHP;
  • фреймворк для приложения — yii;
  • фреймворк для деплоя и развертывания приложения — phing;
  • фреймворк для написания unit-тестов — phpUnit;
  • система непрерывной интеграции — Jenkins;
  • функциональное и UI-тестирование — Selenium;
  • СУБД Postgres;
  • key-value хранилища memcached, redis;
  • веб-сервер nginx.

(Почему выбраны именно эти системы, обсуждать не будем, это тема для отдельной публикации.)

Для обеспечения критериев качества технологической базы мы решили заложить слоистую архитектуру.
Слоистая архитектура — это такая архитектура системы, при которой система состоит из некоторой упорядоченной совокупности программных подсистем, называемых слоями, такой, что:
  • на каждом слое ничего не известно о свойствах (и даже существовании) последующих (более высоких) слоев;
  • каждый слой может взаимодействовать по управлению (обращаться к компонентам) с непосредственно предшествующим (более низким) слоем через заранее определенный интерфейс, ничего не зная о внутреннем строении всех предшествующих слоев;
  • каждый слой располагает определенными ресурсами, которые он либо скрывает от других слоев, либо предоставляет непосредственно последующему слою (через указанный интерфейс) некоторые их абстракции.

Таким образом, в слоистой программной системе каждый слой может реализовать некоторую абстракцию данных. Связи между слоями ограничены передачей значений параметров обращения каждого слоя к смежному снизу слою и выдачей результатов этого обращения от нижнего слоя верхнему. Недопустимо использование глобальных данных несколькими слоями. Более полное определение можно найти в работах Фаулера.

Не рассматривая сеть и сервера, которые сами по себе являются отдельными слоями, рассмотрим устройство технологичекой платформы. В нёй можно выделить три слоя:


Рис. 1. Слои архитектуры технологической платформы

На рис. 1 выделены три слоя:
  • Core layer — слой ядра. Здесь располагаются общие низкоуровневые библиотеки и фреймфорки. Например, yii.
  • Shared layer — слой модулей, реализующих какой-то функционал системы, которые могут быть использованы в нескольких проектах.
  • Application layer — слой приложений. На этом уровне располагаются все приложения с их специфичными модулями.

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

Рассмотрим более детально файловую организацию приложения в Application layer с учетом специфики yii.

application
 	framework - сюда деплоится фреймворк
 	lib -   компоненты из core и shared.  Для этой папки в конфиге Yii заведен отдельный alias		
		components
		extensions
		...
	protected
		components
		extensions
		...
	public
	themes
	config

рис 2. Файловая организация yii приложения

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

Итак, мы задали гибкую основу для нашей веб-инфраструктуры, но достаточно ли этого? Нет. Не хватает двух важных вещей:
  • архитектура приложения должна быть со слабым зацеплением и разделяемой;
  • система деплоя и сборки продукта.

Теперь по каждому пункту подробней.

Архитектура приложения


В основу архитектуры приложения также положим слои. Выделим следующие уровни:

рис 3. Слои yii-приложения

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

Наибольшая степень связанности существует между приложением и «тонкими» контроллерами. Вероятность переиспользования минимальна. А вот связанность между слоем «тонких» контроллеров и слоем бизнес-логики нужно сделать как можно меньше, так как бизнес-логику мы можем и должны переиспользовать в других наших приложениях. И сделать мы это можем при помощи гибкости Yii и его конфига.

Конфиг осуществляет подключение нужного нам набора компонент и расширений. Пример подключение расширения:

'geoip' => array('class' => 'application.extensions.GeoIP.CGeoIP'),


К инициализируемому расширению в приложении можно будет обращаться по ключу geoip. Например:

Yii::$app->geoip;


При первом обращении к компоненту произойдет инициализация класса CGeoIP. Гибкость заключается в наличии ключа :-) Мы можем в любой момент через конфиг подменить реализацию, а приложение будет работать по прежнему по ключу geoip.

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

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

Расширения — это дополнительный уровень абстракции для нас, за которым мы можем скрыть источники данных, необходимые для внутренней работы тех или иных методов API расширения.

Давайте немного пофантазируем и придумаем приложение:


рис 4. Пример архитектуры yii-приложения

Наше приложение работает с несколькими расширениями. Расширения оперируют набором данных и тесно связаны с слоем моделей. Важный момент: степень связанности меду слоями растет сверху вниз. Тесней всего связаны между собой модели данных.

Теперь давайте представим, что возникла необходимость в создании единого сервиса авторизации пользователей для всех продуктов. Мы можем легко реализовать такой проект в виде отдельного приложения. Для работы с новым приложением пишем расширение — rest-клиента UserServiceRestClient. Клиент будет иметь такое же API, что и UserExtension API. Смотрим рисунок для наглядности:


рис. 5

UserServiceRestClient помещаем в shared-уровень и меняем в конфиге приложения класс, который нужно использовать. Так как API одинаковое, подмена реализации прошла без изменения кода. Очень гибко! Все остальные приложения так же работают с сервисом пользователей, используя UserServiceRestClient.

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

Система деплоя и сборки


Что должна делать система деплоя:
  • осуществлять доставку кода в указанную директорию на сервере;
  • подтягивать все зависимости приложения (расширения уровня shared и фреймворк);
  • генерировать конфиг приложения;
  • выполнять sql-миграции баз данных.

А ещё — задачи запуска unit-тестов, сборка debug-версий проекта и т.д Все не перечисляю, это дело вкуса, да и внимания всем этим операциям уделено уже достаточно много в интернете. К примеру, у нас система деплоя конфигурирует сопутствующее ПО типа nginx, Sphinx, RabbitMQ и создаетдокументацию. Удобно!

В целом же задачи обычные: файловые операции, работа с системами контроля версий, запуск сторонних утилит (PHPUnit, например, или Doxygen) и т.д Внимание стоит обратить на генерацию конфига. Мы сделали её по шаблону с мета-конфигурационным файлом:

application
	protected
		config
		main.php.template - шаблон конфига yii
	public
	themes
	build.prop - мета-конфигурационный файл
	build.xml - конфиг phing’a

рис. 6

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

## List of extensions, components and etc. to install ##
EXTENSIONS = myExt, myExt2
COMPONENTS = component1, component2
HELPERS    = myHeper, myTextHelper
COMMANDS   =


Так же в мета-конфиге прописаны базы данных, пути, хосты — в общем, все. Дополнительное удобство для автоматизации опять же в том, что все эти параметры можно передать через командную строку Phing’у и переопределить. А в будущем, например, наладить сборку пакетов под используемую вами *nix ОС.

Таким образом, мета-конфигурационный файл — это слой абстракции от формата и количества конфигов в нашем приложении.

В итоге гибкость конфигурации Yii + система сборки = легко конфигурируемые и собираемые продукты.

Итог


Итак, что нам показал опыт использования такой схемы работы в течение года?
  • Если возникнет необходимость переиспользовать какие-то расширения, то это не станет проблемой, процесс непрерывной интеграции выстроился тоже без особых проблем.
  • Есть необходимый запас гибкости, позволяющий легко развивать и наращивать функционал приложений. Функциональные блоки можно делать крайне быстро, не задумываясь о производительности и качестве кода.
  • Если интерфейс продуман, всегда можно безбоязненно доделывать функционал, улучшать производительность, менять хранилище данных и проводить рефакторинг.
  • Продуманная система деплоя приложения позволяет очень быстро разворачивать систему для тестирования и минимизирует ошибки при развертывании на боевых серверах, связанные с невнимательностью человека.
  • Благодаря изолированности отдельных функциональных модулей всегда можно выделить отдельную группу разработки для работы с ним. Это позволяет решать проблемы роста отдела разработки и не превращаться в большой неуправляемый колхоз, где все работают над всем, и тем самым только мешают друг другу.


P.S. И да, мы ищем yii-разработчиков в Новосибирске и Киеве. Приходите! :-)
Теги:
Хабы:
Всего голосов 48: ↑44 и ↓4+40
Комментарии8

Публикации

Информация

Сайт
2gis.ru
Дата регистрации
Дата основания
Численность
1 001–5 000 человек
Местоположение
Россия