Архітектура

Архітектурне мислення: межі, відповідальність і компроміси

У главах 08–11 ми перейшли від потреби користувача до вимог, критеріїв і характеристик якості. Тепер можна обговорити структуру рішення: які частини…

Глава 12Заморожене українське видання

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

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

Після цієї глави читач може пояснити різницю між логічною межею і окремим сервером; побачити зв’язність, відповідальність і джерело істини; порівняти моноліт, модульний моноліт, сервіси й подієві взаємодії за потребою та ціною; назвати припущення, які ще треба перевірити; і записати рішення так, щоб команда могла зрозуміти його пізніше.

Архітектурне мислення не вимагає заздалегідь накреслити всю систему або призначити «головного архітектора» для кожної функції. Воно вимагає побачити наслідки значущого вибору, назвати контекст і бути готовим переглянути рішення, коли зміняться факти.

Розділіть архітектуру, дизайн модуля, код і спосіб розгортання

Чотири шари проєктування програмної системи

У розмовах слово «архітектура» часто означає чотири різні речі. У цій главі ми розрізняємо їх, щоб не сперечатися про різні рівні одночасно:

Рівень Питання Приклад
Архітектура системи Які структурні рішення визначають межі, відповідальність, потоки даних і дорогі наслідки для кількох частин? Підтвердження бронювання та зайняття слота відбуваються в одній транзакції — операції, що застосовує всі пов’язані зміни разом або не застосовує жодної, — якою володіє модуль записів.
Дизайн модуля Як одна частина коду організовує свою відповідальність і відкриту для інших взаємодію? Модуль записів має операцію перенестиЗапис, а обчислення доступних часових слотів приховане всередині його межі.
Реалізація Яким кодом, бібліотекою і локальним алгоритмом виконана конкретна поведінка? Перевірка вільного часу використала індекс бази даних та транзакційне обмеження.
Топологія розгортання У яких процесах, машинах, регіонах або контейнерах запускаються частини? Вебзастосунок і фоновий працівник стартують як окремі процеси з одного програмного продукту.

Ці рівні пов’язані, але не тотожні. Окремий модуль не обов’язково працює на окремому сервері. Два процеси можуть запускати один і той самий код, але мати різні режими й масштаб. Система з одним розгортанням може бути модульною або заплутаною; сама кількість процесів цього не доводить.

Слово моноліт вживають неоднаково: ним можуть назвати один розгортаний застосунок, одну велику кодову базу або невідокремлені відповідальності. Тому завжди уточнюйте, про який рівень ідеться. Модульний моноліт у цій главі — один застосунок, який можна випустити як ціле, але його внутрішні модулі мають виразні обов’язки й контрольовані залежності. Мікросервіс — окремо розгортаний сервіс у власному процесі, який взаємодіє через мережевий контракт і часто має власну операційну відповідальність. Реальні команди можуть обирати проміжні форми.

Shopify показала, чому великій системі можуть бути потрібні сильніші внутрішні межі без переходу на мікросервіси. У 2019 році компанія писала, що її Rails-кодовою базою понад десять років займалися більш як тисяча розробників. Невелика команда почала проєкт модульності на початку 2017 року, обрала межі всередині моноліту й описала труднощі з крихкими залежностями, тестуванням і передачею контексту новим розробникам. Під час переміщення файлів частину Git-історії було втрачено, бо GitHub трактував їх як видалення та створення замість перейменувань. У самоописі за 2020 рік Shopify навела масштаб понад 2,8 мільйона рядків Ruby і 500 000 комітів та зазначила, що робота ще далека від завершення, хоча команда вже бачила проміжні вигоди й поширювала чіткіші межі між компонентами. Це корисний первинний кейс про контекст, команду, масштаб і ціну змін, але самооцінка Shopify не є незалежним порівнянням із мікросервісами й не доводить, що іншим командам потрібні ті самі інструменти чи правила Shopify, «Deconstructing the Monolith», 21 лютого 2019 року; Shopify, «Under Deconstruction: The State of Shopify’s Monolith», 16 вересня 2020 року.

Частина системи — це відповідальність, а не просто папка

Межі та відповідальності частин системи

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

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

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

Відповідальність допомагає перевірити межу. Якщо модуль «бронювання» водночас вирішує колір кнопки, оплачує рахунки постачальника, надсилає маркетингові кампанії й зберігає політику паролів, назва не пояснює його роль. Якщо ж кожна маленька дія вимагає виклику п’яти різних модулів, межі можуть бути розрізані надто дрібно або обов’язки розміщені невдало.

Один бізнес-сценарій може пройти через кілька модулів. Це не автоматично помилка. Наприклад, «перенести консультацію» може перевірити доступність у календарі, записати зміну в модулі бронювання й запланувати повідомлення. Питання не в тому, чи торкнулася зміна більш ніж однієї частини, а в тому, чи ці переходи виразні, узгоджені й безпечно перевіряються.

Зв’язність і цілісність відповідальності

Зв’язність (coupling) — залежність однієї частини від іншої. Зв’язність не може дорівнювати нулю: частини мають співпрацювати, щоб створити продукт. Ризик зростає, коли зміна однієї відповідальності змушує непов’язані модулі знати внутрішні деталі один одного, координувати одночасне розгортання або копіювати однакові правила.

Цілісність відповідальності (cohesion) — наскільки обов’язки всередині частини мають спільну причину змінюватися. Усі функції модуля можуть бути пов’язані з розкладами, але якщо одні зберігають години роботи, інші ведуть розсилку, а треті змінюють зовнішній сайт, спільна назва не гарантує цілісності.

Для діагностики в цій главі розглянемо чотири форми зв’язності. Це навчальні категорії, а не універсальна офіційна класифікація кожної архітектурної проблеми.

Форма зв’язності Як проявляється Запитання для перевірки
Структурна Один модуль імпортує внутрішній клас іншого або покладається на його приховану структуру Чи можна змінити внутрішній спосіб зберігання, не змінюючи всі споживачі?
Часова Щоб одна операція спрацювала, інша обов’язково має виконатися раніше, пізніше або в певному вікні Чи знаємо ми, що робити, якщо перша дія затрималась або прийшла повторно?
Даних Кілька частин напряму змінюють ті самі таблиці, поля або бізнес-правила Хто володіє записом і хто має право його змінювати?
Операційна Частини мають спільно випускатися, масштабуватися, моніторитися або відновлюватися Чи справді їх потрібно обслуговувати й змінювати разом?

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

Модульність має відповідати способу змін. Якщо команда раз у раз змінює дві межі разом, це свідчить, що їх потрібно повторно перевірити: чи справді вони мають різних власників, темп змін або правила збереження? Це привід для аналізу, а не безумовний наказ злити модулі. Аналогічно, якщо компонент можна замінити незалежно без змін у його споживачах, ця незалежність може бути корисною, але лише якщо вона потрібна продукту й команді. У спільній статті James Lewis і Martin Fowler «Microservices» розглядають еволюційний поділ системи; автори опублікували її 25 березня 2014 року Lewis і Fowler, «Microservices».

Стан, джерело істини, команди й події

Джерело істини, команди та події в системі

Стан — збережені факти, від яких залежить наступна поведінка. У системі бронювання ним можуть бути часовий слот, клієнт, статус запису та час останнього оновлення. Архітектурне запитання не просто «в якій таблиці лежить значення?», а «хто має право вирішити, яким воно є?»

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

Для мислення про взаємодію корисно розрізняти три повідомлення:

  • Команда просить власника виконати дію: «перенеси запис на 14:00». Її можна прийняти, відхилити або відкласти; успіх не слід вважати фактом до підтвердження.
  • Запит просить повернути інформацію: «які часові слоти вільні завтра?». Сам запит не повинен змінювати стан.
  • Подія повідомляє про факт, що вже стався: «запис перенесено на 14:00». Споживачі можуть використати її для інших дій, але не мають переписувати факт так, ніби подія лише пропонувала команду.

Це не означає, що кожна система мусить мати окремі черги, брокери або моделі даних для команд, запитів і подій. Розрізнення допомагає уточнити власника й очікування.

Команда, запит і подія описують зміст повідомлення. Синхронна чи асинхронна взаємодія описує, чи чекає ініціатор на відповідь. Це різні виміри, хоча в реальній системі вони поєднуються в одному потоці.

Синхронна взаємодія — ініціатор чекає відповіді до продовження поточного сценарію. Локальний виклик функції в одному процесі зазвичай має спільну пам’ять і простіше передавання помилки. Мережевий виклик додає затримку, тайм-аут, можливість втрати зв’язку після фактичного виконання, повтори й часткову відмову: клієнт може не знати, чи сервер запис зберіг, коли його відповідь загубилася. Саме перетворення локального виклику на мережевий тому є архітектурним рішенням, а не зміною адреси.

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

Подієва архітектура — спосіб взаємодії, у якому частини повідомляють про події й реагують на них. «Відбувся ЗаписПеренесено» може запустити нотифікацію й оновити звіт. Події можуть передаватися всередині одного процесу або між системами через мережу; назва сама по собі не означає наявність центрального брокера. Справжня потреба може бути в одному повідомленні, таймері чи фоновій роботі, а не в платформі подій.

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

Моноліт, модульний моноліт, сервіси й подієва система

Архітектурні стилі та їхні компроміси

Жоден архітектурний стиль не усуває складність повністю; різняться складність, яку він зменшує, і нова ціна, яку додає. Software Architecture: The Hard Parts пропонує порівнювати наслідки меж, потоків, власності даних та розгортання, а не вибирати кількість сервісів за популярністю Neal Ford, Mark Richards, Pramod Sadalage, Zhamak Dehghani, Software Architecture: The Hard Parts, O’Reilly, 2021.

Варіант Що спрощує Яку складність додає або залишає
Монолітне розгортання без чітких внутрішніх меж Одне складання й одне розгортання можуть бути простими на початку Зміна однієї деталі може зачепити далекі частини; важко знати власника й безпечно змінювати внутрішні залежності
Модульний моноліт Залишає спільний процес і розгортання, але робить межі та дозволену взаємодію явними Внутрішні правила треба підтримувати; випуск все ще може потребувати сумісності для всієї системи
Окремо розгортані сервіси Незалежні частини можуть мати окремий темп випуску, масштаб і власника Мережа, тайм-аути, часткові відмови, версії контрактів, окремі журнали, сповіщення, доступ, розгортання та чергування
Подієва взаємодія Відправник може не знати всіх одержувачів; роботи можна відкладати та повторювати Стан стає розподіленим у часі; потрібні правила для повторів, порядку, застарілих читань, помилок та пошуку причини

Подієва система не є взаємовиключною з монолітом чи сервісами. Застосунок може мати модулі та події всередині одного процесу. Два сервіси можуть обмінюватися синхронними запитами. Не виводьте весь спосіб побудови системи з одного інструмента.

Шість сервісів не означають шість корисних меж

Архітектурний ескіз із сервісами «користувачі», «розклад», «бронювання», «сповіщення», «рахунки» та «аналітика» не пояснює, хто визначає стан бронювання і хто може підтвердити слот. Якщо одна дія потребує, щоб три сервіси змінили стан в одному атомарному кроці, видимі мережеві межі не зробили вимогу простішою. Якщо ж сповіщення не встигло надіслатися, це не має скасовувати запис, коли продукт вже підтвердив його.

Окремі сервіси мають сенс, коли конкретна незалежність цінна: окрема команда безпечно випускає компонент своїм графіком; один компонент має інше навантаження; збій його залежності можна локалізувати; його потрібно замінювати або масштабувати незалежно; і команда здатна діагностувати та обслуговувати розподілений шлях. Якщо ж частини змінюються разом, ними володіє одна команда і їх треба випускати як одне ціле, сервісний контракт може стати зайвою операційною межею. Fowler наголошує на обох сторонах компромісу й описує випадок, де збільшення команди зробило декомпозицію кориснішою для темпу, але все одно коштувало більше людино-годин, ніж монолітний варіант Martin Fowler, «Microservice Trade-Offs». Це приклад для міркування, а не контрольоване порівняльне вимірювання, результат якого можна переносити на будь-який продукт.

Книжкові ідеї: цілісна модель і межа людської складності

Software Architecture: The Hard Parts розглядає компроміси гранулярності компонентів, координування процесів, контрактів, даних і операційної складності. Тут ці виміри допомагають порівняти варіанти для невеликої системи; книжка не дає універсального рецепта кількості сервісів Neal Ford, Mark Richards, Pramod Sadalage, Zhamak Dehghani, Software Architecture: The Hard Parts, O’Reilly, 2021.

Друга редакція Team Topologies розглядає когнітивне навантаження як фактор організаційного й технічного дизайну. Визначаючи межу, команда може запитати, чи відповідає вона тому, що її учасники здатні зрозуміти й безпечно змінювати, і чи підтримує потрібну взаємодію між командами. Водночас назва команди або коробка на організаційній схемі сама собою не задає архітектуру; власники, потік роботи й комунікація змінюються. Тому структуру треба перевіряти на реальних змінах і переглядати, коли її ціна перевищує користь. Про робочі моделі команд докладніше йдеться в главі 41 Team Topologies, 2nd ed., 23 вересня 2025 року.

Брукс називав концептуальну цілісність важливою властивістю системи: її частини мають складатися в узгоджений задум із зрозумілими користувачеві правилами. Він пов’язував це з єдністю плану й розрізненням задуму та реалізації. Для бронювання це означає, наприклад, що всі частини мають однаково трактувати момент, коли слот стає зайнятим, і що відмова пошти не повинна непомітно скасовувати вже підтверджений запис [Frederick P. Brooks Jr., The Mythical Man-Month, розділ 4 «Aristocracy, Democracy, and System Design»](https://www.oreilly.com/library/view/mythical-man-month-the/0201835959/ch04.xhtml).

В іншому есе Брукс розрізняв сутнісну складність задачі та випадкову складність її реалізації. Сутнісна складність походить від самої задачі — правил бронювання, часу, конкурентних запитів і потреби не втрачати стан. Випадкова складність виникає через інструменти, мови й спосіб реалізації; кращі інструменти можуть її зменшити, але не скасовують продуктове правило. Його аргумент 1980-х років полягав у тому, що один інструмент не прибере всю складність розробки на порядок; це не твердження про сучасні мовні моделі [Frederick P. Brooks Jr., The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition, 2nd ed., 1995, розділ 16](https://www.oreilly.com/library/view/mythical-man-month-the/0201835959/ch16.xhtml).

Застосування цих ідей до агентів штучного інтелекту (ШІ) — наша адаптація, не висновок Брукса про ШІ. Агент може допомогти втілити узгоджений задум, але не визначає його замість власника продукту й не доводить, що транзакція захищена від дублювання. Якщо правила, джерело істини та наслідки відмови ще невідомі, новий інструмент може лише швидше реалізувати хибне припущення.

Розібраний приклад 1: сервіс бронювання для двох людей

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

Спершу визначте власність і критичні правила

  • Модуль Розклад описує години роботи й доступні вікна консультацій.
  • Модуль Бронювання володіє підтвердженим записом і зміною його стану.
  • Модуль Повідомлення відповідає за підготовку та повторну спробу доставки підтвердження.
  • Один запис бронювання містить обраний слот і клієнта; система має не підтвердити два активні записи для одного слота.
  • Перед перенесенням новий час перевіряється й резервується до зняття старого. Якщо новий слот зайняв інший запит, попередній запис клієнта залишається чинним.

Ці модулі можна побудувати як модульний моноліт з однією реляційною базою. Модуль Бронювання перевіряє й записує бронювання в одній транзакції — операції, що фіксує всі пов’язані зміни разом або не фіксує жодної. Точний механізм захисту від дублювання залежить від обраної бази й має бути перевірений за її документацією. У цій самій транзакції система зберігає завдання для Модуля Повідомлень. Збій до її фіксації не залишає бронювання без завдання; після фіксації фоновий обробник може повторити доставку.

Якщо завдання зберігається в іншому сховищі, атомарність уже не гарантована й потрібне окреме рішення для надійної передачі або компенсації. Недоступність зовнішньої пошти не скасовує підтверджений запис: клієнт бачить «Запис підтверджено; повідомлення ще надсилається», а не помилкове «запис не створено».

Порівняйте дві версії структури

Варіант А — модульний моноліт: один репозиторій, один основний випуск і внутрішні межі; Бронювання володіє підтвердженим записом. Варіант Б — шість окремо розгортаних сервісів: облікові записи, розклад, бронювання, платежі, повідомлення та аналітика. Це навмисно надмірний варіант для порівняння: у вимогах сценарію немає платежів чи аналітики, тож окремі служби для цих напрямів не розв’язують тут жодної заявленої потреби. Таблиці нижче оцінюють саме вартість такого розбиття, а не пропонують додати ці можливості.

Спочатку позначмо вимогу «не підтвердити подвійний слот» як умову проходження, а не бал, який можна компенсувати зручним розгортанням. Обидва варіанти мають показати, як її забезпечують і перевіряють. Решту порівняймо за поточними умовами: команді з двох людей не потрібні незалежні випуски, а збій пошти не має скасовувати підтверджене бронювання.

Питання Модульний моноліт Шість окремих сервісів
Як підтримувати систему команді з двох людей? Один основний випуск і спільна база; внутрішні межі треба берегти Окремі випуски можливі, але кожна служба додає розгортання й діагностику
Хто захищає бронювання від подвійного слота? Один власник може перевірити й записати слот у межах однієї транзакції Потрібно визначити власника й механізм узгодження між межами розкладу та бронювання
Що станеться, якщо не працює пошта? Завдання зберігається разом із бронюванням і може бути повторене Відмову можна ізолювати, але потрібні мережевий контракт, повтори й спостереження за доставкою

Та сама умова «не підтвердити подвійний слот» лишається обов’язковим проходженням, а не балом, який можна компенсувати зручнішим випуском. Щоб зробити підставу вибору явною, оцінимо решту критеріїв за шкалою від 1 до 5. Ваги походять лише з меж навчального сценарію: команді з двох людей потрібна обслуговувана простота й немає окремого оператора; незалежних команд і графіків випуску поки немає; збій пошти не має ламати бронювання.

Критерій Вага й підстава Модульний моноліт, 1–5 Шість сервісів, 1–5
Зрозуміле обслуговування двома розробниками 4 — межа впливає на щоденну зміну й відновлення, окремої платформи немає 5 2
Незалежний випуск окремих доменів 2 — зараз немає окремих команд або різних графіків 2 5
Ізоляція збою сервісу повідомлень 3 — повідомлення може затриматися, але бронювання вже підтверджене 4 4
Прозорість основної транзакції бронювання 5 — помилка створює подвійний запис або втрачає чинне бронювання 5 3
Зважена сума для орієнтації Вага × оцінка; умову подвійного слота перевіряють окремо 61 45

Це не виміряні властивості платформ і не математичний доказ переможця. Це впорядковані судження за навчальними припущеннями, щоб було видно, чому поточна потреба віддає перевагу модульному моноліту. Якщо через рік працюватимуть окремі команди розкладу й бронювання з різними графіками випуску, вага незалежності зміниться; якщо кількість запитів і дані покажуть окреме вузьке місце, потрібно буде перевірити масштабування конкретної межі. Зараз команда записує рішення «модульний моноліт із одним власником стану бронювання», його причини й сигнали для перегляду. Вона також зберігає альтернативу сервісів, якщо контекст зміниться.

Пояснювальна таблиця й зважена оцінка ведуть до одного тимчасового вибору: модульний моноліт тримає критичний запис в одній межі та не додає платформу, яку ця команда поки не може виправдати. Оцінки — умовні судження для цього прикладу; самі числа не є вимірюванням платформ. Якщо з’являться окремі команди з різними графіками випуску або вимірювання покажуть вузьке місце, треба перевірити, чи змінилася цінність сервісної межі, і переглянути ADR.

Розібраний приклад 2: каталог і порада агента про Kafka

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

Синхронне читання може повертати найсвіжішу ціну, але кожен перегляд залежить від затримки й доступності постачальника. Локальна копія захищає перегляди від повільної зовнішньої системи, але може застаріти. Команда має показувати час останнього успішного імпорту й не видавати старі дані за свіжі.

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

Тут є три різні джерела фактів: постачальник володіє актуальною ціною, магазин — останнім повністю прийнятим знімком, а замовлення — власним підтвердженим станом. Їх не можна зливати в одну «поточну ціну».

Агент, прочитавши короткий опис, пропонує: «Для масштабування додайте Kafka й шість мікросервісів». Це рекомендація, а не доказ потреби. Вона не пояснює, що саме зростає, де вузьке місце і які гарантії потрібні під час збою. Попросіть агента відповісти на конкретні запитання:

  1. Результат: коли має починатися імпорт, якої свіжості потребує каталог і чи може неповний файл замінити останню перевірену копію?
  2. Факти: скільки є постачальників і споживачів; які розмір файла, час імпорту, частота помилок і найдовші прогони виміряні? Якщо даних немає, позначте це, а не вигадуйте навантаження.
  3. Варіанти: запланована робота в застосунку, окремий фоновий працівник або брокер для кількох незалежних споживачів і повторного програвання історії.
  4. Ціна: скільки додається залежностей, операційної роботи, правил повтору, історії, діагностики та навичок, яких потребує команда?
  5. Перевірка: прототип має пройти типовий і найбільший файл, повтор після тимчасового збою, невідповідність схеми й збереження попередньої повної копії.

За заданих умов — один постачальник, один споживач і до 70 хвилин допустимого відставання — запланованої фонової роботи з лімітом повторів досить; дані про мільйони товарів відсутні, тому така місткість не доведена. Брокер варто оцінити знову, якщо виникнуть незалежні споживачі, потреба програвати історію або виміряне вузьке місце.

Це приклад роботи з агентом, а не оцінка конкретного продукту чи постачальника. OpenAI радить нарощувати складність поступово й заздалегідь визначати вимоги якості; Anthropic нагадує, що автоматична перевірка функції не замінює людську оцінку системних вимог OpenAI, «A practical guide to building agents», переглянуто 23 вересня 2026 року, Anthropic, «Building effective agents», 19 грудня 2024 року. Агент може порівняти варіанти й перевірити факти, але рішення про продуктову політику залишається за її власником.

Коли рішення записати, а коли його можна відкласти

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

Порівняйте два рішення:

  • «Для форматування дати використаємо бібліотеку X» — часто локальна реалізація, якщо її легко замінити.
  • «Історичні бронювання зберігають локальний час консультації та ідентифікатор зони, а не лише зміщення від UTC (Coordinated Universal Time — координований всесвітній час), спільної опорної шкали для зіставлення моментів незалежно від місцевого часового поясу» — впливає на сховище, перенесення, відображення й міграції; змінити це пізніше може бути дорого.

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

Технічний борг — не універсальний дозвіл відкласти безпечність. Якщо команда тимчасово обирає простіший варіант, фіксує ризик, власника й умову перегляду. Наприклад: «до 100 000 позицій і одного фонового імпорту використовуємо одну таблицю з часовою міткою; якщо найбільший імпорт перевищить погоджене вікно двічі за тиждень, вимірюємо вузьке місце та окремо оцінюємо пакетне завантаження». Усі числа в цьому записі мають мати джерело; якщо це домовлене припущення, його так і маркують.

Архітектурний запис: що сталося, чому і що може змінити рішення

ADR (Architectural Decision Record — запис архітектурного рішення) — короткий текст про одне значуще рішення. Формат не є магічним стандартом і може змінюватися між командами. Michael Nygard популяризував легкий запис зі статусом, контекстом, рішенням і наслідками; він також підкреслює, що пізніші рішення мають посилатися на те, яке вони замінили, щоб зберегти історію причин Michael Nygard, «Documenting Architecture Decisions», 15 листопада 2011 року.

Практичний запис може містити:

ADR-001: Зберігати підтвердження бронювання в одному модулі

Статус: прийнято

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

Рішення:
Модуль Бронювання володіє підтвердженим записом і резервує слот у межах
однієї транзакції. У тій самій транзакції воно зберігає завдання для Модуля
Повідомлень у спільній базі; після підтвердження фоновий обробник може
повторити доставку. Для окремого сховища потрібен окремий механізм надійної
передачі або компенсації.

Альтернативи:
Розподілити бронювання та розклад між окремо розгортаними сервісами;
залишити моноліт без чітких внутрішніх меж.

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

Докази та припущення:
Умова одного активного запису на слот взята з погоджених вимог.
Навантаження й майбутній поділ команд не вимірювалися.

Умови перегляду:
Переглянути рішення, якщо з’являться незалежні команди з різним графіком
випуску або вимірювання покажуть окреме обґрунтоване вузьке місце.

Це навчальний приклад, а не справжня архітектура компанії. ADR не замінює інструкцію користувача, контракт програмного інтерфейсу застосунку, план роботи, вимогу чи повну архітектурну документацію. Його мета — зберегти підставу рішення й відкрити двері до свідомого перегляду. Martin Fowler описує ADR як короткий запис одного рішення з контекстом, наслідками й статусом, який допомагає майбутній команді зрозуміти, чому система склалася саме так Martin Fowler, «Architecture Decision Record», 24 березня 2026 року.

Порядок роботи: від потреби до рішення, яке можна перевірити

Як порівнювати архітектурні варіанти

Перед значущим архітектурним вибором пройдіть цей порядок разом із власниками вимог і людей, які супроводжуватимуть систему:

  1. Назвіть потребу й межі. Яку поведінку та які характеристики якості система мусить забезпечити? Що точно не входить до рішення зараз?
  2. Запишіть джерело й невідомі. Який факт підтверджений? Яке число є припущенням? Чи можна провести мале дослідження замість сперечатися про здогади?
  3. Знайдіть власника даних і дій. Хто може підтвердити чи скасувати стан? Хто лише читає похідну копію?
  4. Побудуйте кілька реальних варіантів. У списку має бути найпростіший достатній варіант і, за потреби, варіант із більшою ізоляцією. «Поставимо сучасну платформу» не є описом альтернативи.
  5. Порівняйте наслідки. Як зміниться реліз, затримка, відмова, відновлення, узгодженість, витрати, власність і навантаження команди? Який висновок спирається на цифри, а який — на домовлені припущення?
  6. Перевірте найризикованішу невідомість. Знайдіть офіційний документ, звірте існуючий код, проведіть прототип або попросіть рішення відповідного власника.
  7. Виберіть і зафіксуйте рішення. Запишіть статус, контекст, вибір, альтернативи, позитивні й негативні наслідки, джерела, невирішені умови й сигнал для перегляду.
  8. Перевірте результат. Під час реалізації вимірюйте чи тестуйте саме ті властивості, які стали причиною рішення. Якщо дані суперечать припущенню, не захищайте вибір лише тому, що вже написали код.

Якщо агент стверджує «так роблять усі», попросіть назвати потребу, джерело твердження, мінімум дві реалістичні альтернативи, наслідки й доказ саме для цього середовища. Відповідь «це найкраща практика» без контексту — сигнал продовжити аналіз, а не наказ прийняти рішення.

Короткий підсумок

Архітектура допомагає пояснити межі й наслідки, а не прикрасити список технологій. Логічний модуль не обов’язково є сервісом; один випуск може містити сильні внутрішні межі; шина подій не є синонімом масштабованості. Визначте джерело істини й відповідальність за стан. Розділяйте звичайні виклики, мережеві запити та повідомлення про факти. Порівнюйте варіанти за підтвердженою потребою, ціною помилки й здатністю команди підтримувати структуру.

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

Джерела та подальше читання

Усі онлайн-джерела перевірено 10 жовтня 2026 року. Для книг вказано використане видання; тут переказано ідеї й описано власні навчальні приклади, а не відтворено текст книжок.

  • Neal Ford, Mark Richards, Pramod Sadalage, Zhamak Dehghani. Software Architecture: The Hard Parts, O’Reilly Media, 2021. Сторінка видавця і авторська сторінка Neal Ford. Використано як основу для аналізу компромісів навколо гранулярності, контрактів, даних і операційної складності; авторська сторінка підтверджує книжку й авторство, а не замінює її читання.
  • Matthew Skelton, Manuel Pais. Team Topologies: Organizing Business and Technology Teams for Fast Flow, 2nd ed., IT Revolution, 23 вересня 2025 року. Сторінка авторів. Використано ідею когнітивного навантаження як одного з факторів меж команд і систем; не перетворено типи команд на обов’язкові модулі.
  • Frederick P. Brooks Jr. The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition, 2nd ed., Addison-Wesley, 1995. [Розділ 4 «Aristocracy, Democracy, and System Design»](https://www.oreilly.com/library/view/mythical-man-month-the/0201835959/ch04.xhtml) — для концептуальної цілісності; [розділ 16 «No Silver Bullet—Essence and Accident in Software Engineering»](https://www.oreilly.com/library/view/mythical-man-month-the/0201835959/ch16.xhtml) — для сутнісної й випадкової складності. Сучасне застосування до агентів позначено як наша адаптація.
  • Shopify Engineering. «Deconstructing the Monolith: Designing Software that Maximizes Developer Productivity», 21 лютого 2019 року; «Under Deconstruction: The State of Shopify’s Monolith», 16 вересня 2020 року. Первинний звіт компанії про контекст, цілі, проміжні результати й витрати модульного моноліту; самоопис компанії, а не незалежне порівняльне дослідження.
  • James Lewis і Martin Fowler. «Microservices», 25 березня 2014 року. Спільна стаття про межі сервісів та еволюцію архітектури.
  • Martin Fowler. «Microservice Trade-Offs», 1 липня 2015 року. Авторський аналіз переваг і ціни розподіленої системи; окремі наведені приклади — повідомлення автору практиків.
  • Michael Nygard. «Documenting Architecture Decisions», 15 листопада 2011 року. Джерело короткого запису контексту, рішення, статусу та наслідків.
  • Martin Fowler. «Architecture Decision Record», 24 березня 2026 року. Опис мети, форми та використання коротких записів архітектурних рішень.
  • OpenAI. A practical guide to building agents, офіційну сторінку перевірено 10 жовтня 2026 року. Використано як загальне джерело поступового нарощування агентної складності, вимог до якості та контрольованого нагляду; не як специфікацію конкретного Codex-клієнта.
  • Anthropic. «Building effective agents», 19 грудня 2024 року. Авторський матеріал про простіші композиційні підходи, критерії оцінювання, зворотний зв’язок і потребу в людському перегляді системних вимог; самі автори попереджають, що частина інструментального ландшафту з часу публікації змінилася.