Шарувата архітектура на основі фреймворку yii

програми
Уявімо компанію, яка реалізує цілу низку власних продуктів — як зовнішніх користувачів, так внутрішніх. Швидше за все, кожен продукт зможе існувати окремо, як сферичний кінь у вакуумі, а певною мірою буде інтегрований коїться з іншими. У результаті всі вони разом утворюють якийсь шар взаємопов'язаних між собою і при цьому організмів, що самостійно розвиваються. І, швидше за все, їх розвиток здійснюється зовсім різними командами.

Саме про досвід в організації архітектури всієї лінійки продуктів такої компанії я й хочу розповісти. Наші зовнішні веб-продукти – це онлайн-версія, відкритий довідковий API та картографічний, сервіс відгуків. Внутрішні: CRM, білінг, алгоритмічні обчислення, системи статистики та експорту-імпорту даних.

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

Веб-інфраструктура — сукупність веб-продуктів компанії, пов'язаних між собою та працюючих у близьких предметних галузях.

Технологічна база - єдина кодова база для веб-інфраструктури компанії. Містить набір програмних блоків із високим та низьким рівнем абстракції. Високоврівневі блоки — це вже напрацьовані командою розробників сутності у предметній галузі компанії. Низькорівневі блоки – це, наприклад, набір бібліотек чи фреймворків.

Для забезпечення критеріїв якості технологічної бази ми вирішили закласти шарувату архітектуру.Шарувата архітектура — це така архітектура системи, при якій система складається з деякої впорядкованої сукупності програмних підсистем, які називають шарами, такою, що:

  • на кожному шарі нічого не відомо про властивості (і навіть існування) наступних (вищих) шарів;
  • кожен шар може взаємодіяти з управління(звертатися до компонентів) з безпосередньо попереднім (нижчим) шаром через заздалегідь визначений інтерфейс, нічого не знаючи про внутрішню будову всіх попередніх шарів;
  • кожен шар має певні ресурси, які він або приховує від інших шарів, або надає безпосередньо наступному шару (через зазначений інтерфейс) деякі їх абстракції.
Таким чином, у шаруватій програмній системі кожен шар може реалізувати деяку абстракцію даних. Зв'язки між шарами обмежені передачею значень параметрів звернення кожного шару до суміжного знизу шару та видачею результатів звернення від нижнього шару верхньому. Неприпустиме використання глобальних даних кількома шарами. Більше повне визначення можна знайти у роботах Фаулера.

Не розглядаючи мережу і сервери, які самі по собі є окремими шарами, розглянемо пристрій технологічної платформи. У ній можна виділити три шари:

програми
Мал. 1. Шари архітектури технологічної платформи

На рис. 1 виділено три шари:

  • Core layer - шар ядра. Тут розміщуються загальні низькорівневі бібліотеки та фреймфорки. Наприклад, yii.
  • Shared layer — шар модулів, які реалізують якийсь функціонал системи, які можна використовувати у кількох проектах.
  • Application layer - шар додатків. На цьому рівні розташовуються всі програми з їх специфічними модулями.
При цьому важливим моментом є те, що модулі повинні мати можливість легко переміщатися із шару програми до рівня загальних модулів. Пояснюється це дуже просто: коли стартує новий проект, зазвичай реалізується найнеобхідніший функціонал. У міру розвитку проекту він починає обростати зв'язками з іншими продуктами (вирішили,наприклад, підтягувати відгуки з одного проекту до кав'ярень на іншому нашому проекті), і, можливо, частина модулів може знадобитися для інших проектів. Або проект поділяється на частини, і модулі поділяються за кількома програмами.

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

рис 2. Файлова організація yii додатка

Як ми бачимо, компоненти та розширення є як на рівні програми, так і на загальному рівні (shared). Тут представлено структуру вже зібраного з репозитарію додатку. Звісно, ​​у системі контролю версій організувати можна все інакше. У нас, наприклад, є два режими збирання, коли все заливається в папку програми і коли робляться симлінки на бібліотеку розширень. Перший необхідний для ізоляції різних програм, коли ми не можемо окремо оновити розширення без оновлення всіх програм, і для розгортання кількох інстансів на одній машині. Або розгортання різних гілок на одному сервері. Другий зручний розробникам під час роботи з кодом.

Отже, ми поставили гнучку основу для нашої веб-інфраструктури, але чи цього достатньо? Ні. Бракує двох важливих речей:

  • архітектура додатка має бути зі слабким зачепленням і поділяється;
  • система деплою та складання продукту.
Тепер по кожному пункту детальніше.

Архітектура програми

В основу архітектури програми також покладемо шари. Виділимо такі рівні:

архітектура
рис 3. Шари yii-додатки

Шар «тонких» контролерів містить мінімум логіки та оперує API розширень. Шар бізнес-логіки складається з рівня розширень та рівня моделей даних. Шар Yii-розширень та моделі даних мають дуже сильний ступінь зв'язаності, на діаграмі це показано більшежирна стрілка.

Найбільший ступінь зв'язаності існує між додатком та «тонкими» контролерами. Імовірність перевикористання мінімальна. А ось зв'язаність між шаром «тонких» контролерів та шаром бізнес-логіки потрібно зробити якнайменше, оскільки бізнес-логіку ми можемо і повинні перевикористовувати в інших наших додатках. І зробити ми це можемо за допомогою гнучкості Yii та його конфігу.

Конфіг здійснює підключення необхідного нам набору компонентів і розширень. Приклад підключення розширення:

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

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

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

Грунтуючись на цій гнучкості, ми зможемо легко керувати пов'язаністю між додатком і шаром бізнес-логіки.

Групуючи логіку роботи з якоюсь сутністю докладання в розширення, ми робимо її відокремлюваною. Всі моделі таким чином інкапсульовані в розширенні і контролер програми працює з даними через розширення API.

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

Давайте трохи пофантазуємо і придумаємо додаток:

програми
рис 4. Приклад архітектури yii-додатки

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

основі
рис. 5

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

Отже, з архітектурою розібралися, але з таким підходом конфіг програми буде чималеньким. Руками прописувати всі залежності для програми - зовсім не по-джедайськи. Тим більше, коли проект живе та з'являються ще й міграції баз даних. Всі ці питання можна вирішити за допомогою своєї системи збирання та деплею продуктів.

Система деплою та складання

Що має робити система деплою:

  • здійснювати доставку коду у вказану директорію на сервері;
  • підтягувати всі залежності програми (розширення рівня shared та фреймворк);
  • генерувати конфіг програми;
  • виконувати SQL-міграції баз даних.
А ще - завдання запуску unit-тестів, складання debug-версій проекту і т.д. Все не перераховую, це справа смаку, та й уваги всім цим операціям приділено вже досить багато в інтернеті. Наприклад, у нас система деплою конфігурує супутнє програмне забезпечення типу nginx, Sphinx, RabbitMQ і створює документацію. Зручно!

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

рис. 6

При генерації конфігу Phing пропарсує шаблон і замінює всі placeholder'и на відповідні параметри, задані у мета-конфігураційному файлі. Вийшло дуже зручно. УсеЗалежно програми задаємо мета-конфіг:

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

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

Таким чином, мета-конфігураційний файл – це шар абстракції від формату та кількості конфігів у нашому додатку.

У результаті гнучкість конфігурації Yii + система складання = легко конфігуровані та продукти, що збираються.

Тож, що нам показав досвід використання такої схеми роботи протягом року?

  • Якщо виникне необхідність перевикористовувати якісь розширення, це не стане проблемою, процес безперервної інтеграції вибудувався теж без особливих проблем.
  • Є необхідний запас гнучкості, що дозволяє легко розвивати та нарощувати функціонал додатків. Функціональні блоки можна робити вкрай швидко, не замислюючись про продуктивність та якість коду.
  • Якщо інтерфейс продуманий, завжди можна безбоязно доробляти функціонал, покращувати продуктивність, змінювати сховище даних та проводити рефакторинг.
  • Продумана система деплою програми дозволяє дуже швидко розгортати систему для тестування та мінімізує помилки при розгортанні на бойових серверах, пов'язані з неуважністю людини.
  • Завдяки ізольованості окремих функціональних модулів можна виділити окрему групу розробки до роботи з ним. Це дозволяє вирішувати проблеми зростання відділу розробки та не перетворюватися навеликий некерований колгосп, де всі працюють над усім, і цим лише заважають один одному.