Pull to refresh
104.95
2ГИС
Главные по городской навигации

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

Reading time8 min
Views28K

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

Хорошие примеры такой среды — это Яндекс (поиск, Директ, карты, почта, вертикальные и внутренние сервисы) или 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-разработчиков в Новосибирске и Киеве. Приходите! :-)
Tags:
Hubs:
+40
Comments8

Articles

Information

Website
2gis.ru
Registered
Founded
Employees
1,001–5,000 employees
Location
Россия