Ідеальна архітектура – міф чи реальність? Чим керуватись архітектору-початківцю на старті проєкту
«Не існує поганої архітектури. Є та, що задовольняє чи не задовольняє вимоги клієнта». Ця фраза з книги Software Architecture in Practice мені дуже близька. Я Андрій Трубіцин, співпрацюю з компанією ЕРАМ як Senior Solution Architect. У статті поділюся порадами щодо створення архітектури та вдалими й невдалими кейсами з реального життя. Сподіваюсь, вони стануть у пригоді спеціалістам, які розпочинають професійний шлях у сфері архітектури рішень.
Коли ми розробляємо унікальний продукт для клієнта, то фактично не обираємо архітектуру, а створюємо її дизайн з нуля. Адже коли є багато її варіацій, то наперед готового рішення не буде. Це дає певну свободу, але водночас передбачає і відповідальність.
Є те, що ми можемо обрати на початку — архітектурний стиль, що відповідатиме запиту, бізнес-вимогам клієнта та індустрії.
Обираємо архітектурний стиль (або стилі)
Загальний перелік стилів можна подивитися за посиланням. А от архітектурні стилі, які найчастіше застосовують на проєктах в моєму полі зору, такі:
Monolith. Якщо для клієнта найважливіше — швидка розробка рішення, то немає сенсу гаяти час на мікросервіси, їхню інфраструктуру, зв’язування, тестування надійності... Монолітна архітектура стане найоптимальнішим рішенням у цьому разі.
Layered.
Microservices/SOA. Якщо для клієнта вкрай важливо, щоб бізнес-вимоги ставали кодом на продакшені вже за два тижні, варто звернути увагу на цей стиль. Також мікросервісний або сервіс-орієнтований підхід чудово спрацьовує у ситуаціях, коли клієнт має багато вендорів і хоче, щоб усі вони вбудовували свої сервіси в одну платформу за спільними стандартами.
Event-driven. Коли через певні причини варто обрати хореографію замість оркестрації або для бізнесу клієнта дуже важливе масштабування, цей стиль буде доречним. Зважте, що, коли система виходить великою і розподіленою, там органічно з’являється eventual consistency.
Module-based. Оберіть, якщо треба мати уніфікований інтерфейс взаємодії модулів один з одним, але водночас зробити їх використання незалежним від деталей імплементації. До речі, яскравим прикладом такого архітектурного стилю є OSGi-платформа.
REST. Цей архітектурний стиль дуже популярний під час побудови вебсервісів, і я впевнений, що майже кожен розробник чи архітектор використовував його і розуміється на недоліках і перевагах.
Для кожного архітектурного стилю можна знайти еталонну архітектуру, яка описує основні частини конструювання, призначення та залежності між ними.
Зауважу, що в деяких сегментах бізнесу є історично сформована архітектура, в рамках якої експерти створюють рішення. Наприклад, у сфері електронної комерції є фіксований перелік варіантів архітектури. Він заснований на монолітних рішеннях від ATG, Hybris, Salesforce commerce cloud, але зараз спостерігаються спроби мігрувати ці рішення на мікросервісну архітектуру. Також ці гравці скуповують окремі сервіси та додають їх до свого портфоліо. Це природно створює сервіс-орієнтовану архітектуру.
Наступний важливий етап — обрання патернів для виконання вимог щодо якості, обмежень і деяких функціональних вимог. Іншими словами, Architecture Significant Requirements (ASRs). Загалом тут теж немає універсальних порад, багато залежить від досвіду експерта та його знань. Річ у тім, що можна дати одне завдання десятьом спеціалістам і кожен створить унікальну архітектуру. Обрати найкращу буде важко, адже всі вони можуть бути вдалими... і зовсім різними.
Проходимо всі етапи роботи архітектора на проєкті
Якщо ви із самого початку розумієте всі нижченаведені кроки, то розробляти нову систему буде простіше. Ви зекономите час і зможете оминути поширені пастки, в які потрапляють архітектори-початківці.
Крок 1: познайомитись зі стейкхолдерами
Передусім треба дізнатись, хто зацікавлений у проєкті. Якщо система велика, то варто знайти всіх стейкхолдерів, адже вони бачать її з різних боків, їм важливі різні аспекти цієї системи. Ви маєте почути потреби всіх людей, які дотичні до системи, адже комусь будуть вкрай важливі квартальні звіти, а комусь — документація на систему тощо. Якщо проігноруєте цей крок на старті, то є ризик пропустити частину вимог і зіткнутись з незадоволенням стейкхолдерів під час виходу в продакшн. У моїй практиці була схожа історія: в процесі розробки з ланцюжка стейкхолдерів випали безпосередні користувачі системи, яку ми розробляли (це були співробітники клієнта). Їхні потреби не вдалось задовольнити, тож вони не хотіли користуватись запропонованим рішенням. Звісно, клієнту це не сподобалось.
Крок 2: зібрати нефункціональні вимоги
Саме на їхній базі ви будете створювати архітектуру. Є багато способів збору нефункціональних вимог (вимог до якості): інтерв’ю та опитувальники для клієнтів, quality attributes workshops, реверс-інжинірінг систем, що вже існують. Найрезультативніший, на мою думку, — quality attributes workshops, але дуже рідко всі стейкхолдери мають час на цей спосіб. Тому є сенс використовувати всі підходи, які можливі у конкретній ситуації.
Інколи єдиним способом залишається листування, і тут опитувальники відіграють найважливішу роль. Зауважте, що, коли збір вимог буде завершено, ви можете стикнутись з розбіжностями в них.
До речі, в ЕРАМ є стандартизовані чек-листи запитань для збору нефункціональних вимог у клієнтів. Угода про нерозголошення такої інформації утримує мене від наведення прикладів. Проте можу порекомендувати створити щось схоже для вашої компанії, якщо ще не зробили цього. Керуйтесь різницею бізнес-доменів і тематикою, коли будете створювати групи запитань. Це спрощує роботу архітектора на старті проєкту.
Крок 3: передивитись функціональні вимоги
На цьому етапі необхідно бути дещо прискіпливим, адже у вимогах можуть бути фактори, що суттєво впливають на вибір архітектурного стилю та інструментів. Зверніть увагу на Architecturally significant requirements. Розуміння архітектурно важливих вимог приходить з досвідом. Наприклад, вимога «хочу створювати складні звіти» може бути архітектурно важливою, якщо навантаження на базу дуже велике і це заважає основним бізнес-процесам.
Крок 4: зібрати обмеження, які має клієнт
Можливо, ви зможете використовувати лише .NET-стек або будете змушені працювати з on-premise рішеннями. Таких обмежень може бути багато. Наприклад, сервіси, що зазвичай використовують у роботі з Amazon, як-от Kafka, Elastic Cloud on Kubernetes, вам не підходять, бо в замовника вони заборонені через певні причини. Або, наприклад, AWS Step Function — один з найвідоміших оркестраторів — можна використовувати, але з певними обмеженнями: не передавати конфіденційні дані між кроками бізнес-процесу, адже ці дані можна побачити в консолі AWS, а це буде порушенням режиму безпеки клієнта. Або ж замовник вже придбав ліцензію Oracle і таким чином обрав за вас базу даних. Про це треба дізнатися якомога раніше.
Крок 5: створити дизайн системи, зокрема за допомогою методу чорного ящика
Припустімо, що у нас є функціональні вимоги F1, F2, ..., Fn та вимоги до якості системи NFR1, NFR2, ..., NFRm, що відсортовані за важливістю (для спрощення в нас ASRs складаються з NFRs). На першому кроці ми маємо чорний ящик з всіма функціональними вимогами:
На другому кроці беремо NFR1 та обираємо патерн, яким можемо задовольнити цю вимогу якості системи. Наприклад, NFR1 вимагає високу доступність системи (high availability), тоді використаємо патерн надмірність, або redundancy. Маємо:
На третьому кроці беремо NFR2 і повторюємо процедуру. Припустимо, що NFR2 вимагає повідомляти команду підтримки, якщо один з функціональних компонентів припинив роботу. Тут можна використати патерн heartbeat так:
Далі почергово створюємо дизайн системи щодо кожної вимоги якості, щоб задовольнити її. Для цього процесу я, наприклад, використовую листки паперу А4 чи дошку, щоб зробити чернетку дизайну, і лише потім документую його у тій чи іншій програмі чи сервісі.
Процес здається простим, але насправді вимоги до якості іноді суперечать одні одним, клієнт може хотіти все й одразу (навіть real time обробку, коли це не потрібно тощо).
Крок 6: обрати технологічний стек
Ймовірно, архітектурно важливі вимоги обмежать вибір технологічного стеку. Наприклад, рішення в клауді з вимогою бути cloud native уже звужує вибір. Також варто звернути увагу на перелік технологій, які вже використовує клієнт, та оцінити, чи будуть йому до вподоби нові і чи потрібно додаткове навчання інженерів замовника для подальшої підтримки рішення. Те саме стосується ресурсів і талантів компанії: якщо в архітектурі доречно використовувати Scala, подумайте, чи маєте ви в команді достатньо людей з відповідною експертизою?
Крок 7: написати документацію архітектури та узгодити її з клієнтом і командою
Перенесіть у документацію все, що важливо: дизайн системи, опис компонентів, технологічний стек, залежності між компонентами, а також історію ухвалення рішень, щоб завжди можна було повернутися і зрозуміти, чому саме так. Для документування можна застосувати підхід Software Engineering Institute або інші варіанти. Ми в ЕРАМ, до речі, розробили шаблони документації для різних типів рішень та індустрій, базуючись на SEI-підході. Така уніфікована бібліотека шаблонів спрощує та прискорює рутинні процеси, які є в роботі кожного архітектора рішень.
Крок 8: розпочати імплементацію
На цьому етапі робота переходить у стадію architecture governance. Тобто архітектор:
- підтримує команду, пояснює деталі та одразу їх фіксує у документації;
- змінює вимоги за потреби;
- стежить за тими сервісами, що вже є у клієнта, та зв’язками між ними й новими продуктами;
- перевіряє нефункціональні вимоги на фактичну відповідність тому, що узгодили спочатку.
Зроблю акцент на першому пункті. З власного досвіду знаю, що своєчасне документування відповідей на запитання розробників заощадить купу часу. Адже надалі ви зможете надавати консультацію швидко — одним посиланням. Окремо треба сказати про роботу архітектора, якщо на проєкті використовують Agile. У цьому випадку на початку імплементації потрібно мати архітектуру високого рівня та детальний дизайн систем, що планують імплементувати протягом найближчих спринтів чи ітерацій планування.
Не забувайте робити огляд того, що створюють розробники щодо залежних сервісів. Дізнавайтесь, чи витримують вони початкові обмеження з безпеки тощо. Просіть команди інженерів документувати деталі рішення, які вони імплементують. Так ви матимете розуміння, як дизайн співвідноситься з фактичною імплементацією.
Крок 9: фінішна пряма
Здебільшого на проєкті, що добігає свого завершення, немає роботи для архітектора. І тоді він перемикається на старт іншого проєкту і консультує попередній.
Вчимося на досвіді інших
За свою кар’єру я стикнувся з низкою нетипових ситуацій і непростими замовниками. Ось кілька уроків, які я вивчив.
Завжди зважати на гнучкі навички. Збір функціональних і нефункціональних вимог — дуже відповідальне завдання для архітектора рішень. На старті роботи йому необхідні не лише технологічні знання, а й навички спілкування зі стейкхолдерами.
Неформальні розмови не менш важливі! Одного разу на discovery-фазі у клієнта я випадково дізнався про діру у вимогах до підсистем, які згодом наша команда мала моніторити. На той момент моє відрядження добігало кінця, я створив документ з вимогами, узгодив його зі стейкхолдерами, отримав позитивні відгуки. Аж раптом у розмові за чашкою кави один з інженерів клієнта поставив мені запитання про ще одну, п’яту внутрішню систему замовника. Мовляв, чому ти не береш її до уваги? Я був щиро здивований, бо вперше про неї чув: офіційними каналами ця інформація не проходила. Довелось терміново додавати систему до переліку вимог.
Бути готовим розбиратись самостійно. На іншій discovery-фазі мене познайомили з відповідальним інженером, який достеменно знав, як усе працює в одній із підсистем. Я в той час збирав нефункціональні вимоги, експерт мав поділитись зі мною інформацією. Але у відповідь на конкретні запитання, я почув, що американець не планує мені щось розповідати. Мовляв, наші інженери забирають роботу у фахівців зі Штатів, отож він не хоче допомагати мені. Розмова з менеджером цього пана не дала результату, тож довелось робити реверс-інжинірінг і глибоке дослідження системи, щоб зібрати дані.
Завжди перевіряти відповідність бізнес-потреб нефункціональним вимогам. Одна з цілком технологічних помилок трапилась на проєкті, де замовник вирішив мігрувати велику монолітну тревел-платформу у мікросервіси. Для цього її треба було спершу розділити на модулі, а потім спакувати у мікросервіси. Ми отримали необхідний розподіл за функціональними ролями від бізнесу та почали роботу. У той момент ніхто не подумав про модель і потоки даних, які є в моноліті.
Ми мали добірку модулів, які обмінювались даними в рамках одного Java-процесу. Коли виділяли один мікросервіс, то стикались з тим, що дані пересилались мережею. Тож треба було і серіалізували, і десеріалізувати їх. Через це бачили значні зниження показників перформансу. Та й це ще не все. Виявилось, що коли є великі масиви даних і продукт розвивається протягом
Висновок з цього такий: можливо, у деяких ситуаціях, щоб задовольнити нефункціональні вимоги до системи, треба жертвувати бізнес-розподілом мікросервісів. Пам’ятайте про потоки даних у старих великих системах. Продумайте та збудуйте спочатку потік даних між модулями, порахуйте їхню кількість, а вже потім спробуйте розділити моноліт на модулі так, щоб потік даних між мікросервісами був мінімальним. Тоді отримаєте high-cohesion та low coupling мікросервіси. Якщо це неможливо, то ще до міграції варто визначити, як розділяти дані. Це завжди складніше, ніж розділяти код. Потім на основі розподіленої моделі даних можна будувати мікросервіси з урахуванням бізнес-розподілу.
Економте гроші клієнта. Один замовник хотів інтеграцію внутрішньої системи та зовнішнього сервісу за допомогою контейнерів та Java. Але після збору нефункціональних вимог ми зрозуміли, що навантаження на таку інтеграцію буде дуже малим. Ми запропонували використовувати serverless-архітектуру за допомогою лямбд- і степ-функцій. Це допомогло зменшити вартість рішення для клієнта з кількох тисяч доларів на місяць до десятків, а це — завжди плюс для відносин із замовником.
Під час дизайну порівнюйте технології, які плануєте використовувати, з іншими варіантами. В одній з інтеграцій, де ми обрали serverless-підхід, треба було використовувати базу даних. Ми припускали, що будемо масштабуватись відносно навантажень у вигляді піків активності, та розуміли, що нам потрібна база даних, яка могла б це підтримувати. Спочатку обрали Dynamo DB, але після порівняння її з Aurora DB зрозуміли, що перша нас не влаштовує, попри всі переваги. Одиничний запис, який міг би проходити через нашу інтеграцію, сягав 500 кБ. А Dynamo DB має обмеження до 400 кБ. Так через одну нефункціональну вимогу ми обрали іншу базу даних, і це було правильне рішення.
У роботі архітектора вкрай важливий практичний досвід створення найрізноманітніших рішень. Складно вмістити всі ситуації, поради та уроки в один матеріал та зробити його універсально корисним. Тож якщо маєте що додати — пишіть у коментарях.
Корисні посилання:
- Software Architecture in Practice (SEI Series in Software Engineering) 3rd Edition
- Documenting Software Architectures: Views and Beyond 2nd Edition
- Designing Software Architectures: A Practical Approach (SEI Series in Software Engineering) 1st Edition
- Software Systems Architecture: Working With Stakeholders Using Viewpoints and Perspectives 2nd Edition
- 97 Things Every Software Architect Should Know: Collective Wisdom from the Experts 1st Edition