Словник Clean Architecture і Hexagonal Architecture

Очищення шарів архітектури, портів і адаптерів, правила залежностей, випадків використання і словника меж архітектури.

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


Основні терміни

** Чиста архітектура ** — багатошаровий архітектурний стиль, популяризований Робертом К. Мартін (Дядько Боб) у своїй 2017 році книзі з тією ж назвою. Він організовує код в концентричні кола, з найбільш стабільним, бізнес-критичним кодом в центрі і специфічними для фреймворку деталями на зовнішній стороні. Метою є забезпечення незалежності основної логіки від баз даних, структур користувацького інтерфейсу і зовнішніх служб.

«Ми повинні переробити цей модуль — бізнес-логіка безпосередньо пов’язана з базою даних. Давайте застосуємо чисту архітектуру і висунумо проблему персистентності назовні»

“У чистій архітектурі, внутрішні шари нічого не знають про зовнішні. Ваша модель домену ніколи не повинна імпортувати анотацію Spring»

Дядько Боб — прізвисько Роберта К. Мартін, відомий програміст і автор. Розробники зазвичай називають його цим ім’ям в розмові. Він також пов’язаний з принципами SOLID, які лежать в основі більшості чистого архітектурного мислення.

“Ти читав промову дядька Боба про кричущу архітектуру? Це повністю змінило те, як я думаю про структуру папок»

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

«Ми використовуємо гексагональну архітектуру — домен не знає, чи розмовляє він з реальною базою даних, чи з підробкою в пам’яті. Адаптер справляється з цим»

** Архітектура цибулини ** — тісно пов’ язаний з нею шаблон, описаний Джеффрі Палермо. Як і чиста архітектура, вона використовує концентричні шари з логікою домену в центрі. Назва відображає шарувату, кільцеву структуру. Команди іноді використовують «цибулю», «шестикутну» і «чисту» як грубі синонімії.

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


Смуги і межі

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

«Ця логіка перевірки не повинна знаходитися в шарі контролера — вона належить до шару випадків використання, або в самій сутності»

** Entity ** — найнижчий шар; у ньому містяться загальні для компанії бізнес- правила і об’ єкти домену. Сутності — це прості об’ єкти (або класи), які містять основні дані і поведінку вашого домену. Вони не мають розуміння баз даних, HTTP, або будь-якої структури.

“Суть Order забезпечує правило, що замовлення не може бути розміщено з нульовою кількістю. Це правило живе в сутності, тому що воно справедливе незалежно від того, через який канал прийшов запит»

** Випадок використання ** — другий шар всередині; він містить бізнес- правила, специфічні для програми. Випадок використання оркеструє об’єкти для виконання однієї цілі користувача (наприклад, «замовити», «скасувати підписку»). Приклади використання іноді називають interactors в оригінальному написанні Дядька Боба.

“Я пишу приклад використання для потоку оплати. Він викликає шлюз запасів для перевірки запасів, потім шлюз платежу, а потім зберігає замовлення. Жоден HTTP код не торкається його»

** Адапти інтерфейсу ** — третій шар; він перетворює дані між форматом, який зручно використовувати для випадків використання і об’ єктів, і форматом, який вимагають зовнішні системи (бази даних, веб- платформи, черги повідомлень). Контролери, презентатори і шлюзові мережі живуть тут.

«Шар адаптера інтерфейсу — це місце, де ми серіалізуємо об’єкт домену в відповідь JSON. Сам випадок використання повертає просту структуру даних — презентатор форматує її»

** Frameworks & Drivers ** — зовнішній шар; це місце, де знаходиться весь код, що стосується конкретної платформи і вводу/ виводу: маршрутизація Express або Django, налаштування ORM, клієнти message- broker. Це найнестійкіші частини системи — рамки змінюються, але ваша бізнес-логіка не повинна.

«Ми можемо обміняти нашу базу даних ORM в шарі рамок, не торкаючись жодної лінії бізнес-логіки. Це все, що я маю сказати»

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

«Ми накреслили жорстку архітектурну межу між шаром use-case і базою даних. Все, що перетинає цю межу, має проходити через інтерфейс»

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

“Ваш варіант використання — імпорт класу об’ єкта Hibernate. Це порушує правило залежності — типи персистентності ніколи не повинні витікати в домен»


Порти, адаптери і ролі підтримки

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

“Встановіть вторинний порт — інтерфейс сховища — потім напишіть два адаптери: один для Postgres, один для списку в пам’ яті. Ваші тести використовують адаптер пам’яті і ніколи не торкаються бази даних»

** Адаптер ** — конкретна реалізація порту. Адаптер знає, як перекладати між інтерфейсом, що звернений до домену, і певною технологією. HTTP- адаптер перетворює вхідний веб- запит на виклик випадка використання. Адаптер бази даних реалізує інтерфейс сховища за допомогою SQL або ORM.

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

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

“Шлюз інкапсулює всі SQL-запити. Випадок використання просто викликає userGateway.findById(id) і повертає об’єкт домену.”

** Presenter ** — адаптер інтерфейсу, відповідальний за форматування виводу випадка використання у модель перегляду (JSON, HTML або будь- який інший формат). Випадок використання викликає презентатор з необробленими вихідними даними; презентатор перетворює їх для механізму доставки. Цей шаблон зберігає логіку форматування поза бізнес- шаром.

“Презентатор перетворює домен типу Money на локалізований рядок валюти. Випадок використання не має уявлення, як буде відображатися сума»

Screaming Architecture — концепція дядька Боба: структура верхнього рівня кодової бази повинна кричати про те, що система (наприклад, «система охорони здоров’я», «управління кредитами»), а не про те, яку фреймворк вона використовує. Якщо відкриваючи проект і бачать тільки controllers/, models/, views/ не говорить вам нічого про бізнес-домен, архітектура не кричить.

“Коли я клонував репозиторій, структура папки просто казала ‘MVC’. Это не сказало мне ничего о бизнесе. Дядько Боб сказав би, що це не кричаща архітектура»


Співвідношення, когерентність і якість дизайну

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

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

** Сплощеність ** — ступінь, у якому елементи всередині модуля належать до одного. Висока сплоченість означає, що клас або модуль виконує одну річ добре; низька сплоченість означає, що він змішує неспоріднені обов’ язки. Хороша архітектура має на меті високу когерентність всередині шарів і низьку зв’язність між ними.

“Ця UserService має низьку когерентність — вона обробляє автентифікацію, оновлення профілю і повідомлення електронної пошти. Розділимо його на три фокусовані випадки використання»


Як використовувати ці терміни в розмові

Архітектурний словник є найбільш корисним, коли він точний і спільний. Ось деякі природні шаблони для звичайних ситуацій.

В обзоре кода:

«Цей контролер робить занадто багато — він отримує дані, виконує обчислення і форматує відповідь. Обчислення належить до випадку використання, а форматування належить до презентатора»

** Під час сеансу розробки: **

“Перед тим, як ми почнемо кодування, давайте погодимося на наші порти. Нам потрібен первинний порт для виклику REST API, і два вторинних порти — один для бази даних і один для служби попереджень»

** Під час пояснення архітектури новому члену команди: **

“Ми слідуємо гексагональній архітектурі. Модель домену у центрі нічого не знає про Spring або Postgres. Все, що торкається зовнішнього світу, проходить через адаптер, який реалізує один з наших портових інтерфейсів»

** Під час відкидання скорочення: **

“Я знаю, що швидше викликати сховище безпосередньо з сутності, але це порушує правило залежності. Давайте зберемо проблему бази даних в шарі інтерфейс-адаптера»


Швидка реакція

TermLayer / RolePlain English Definition
Clean ArchitectureArchitectural styleConcentric layers; business logic at the centre, frameworks on the outside
Dependency RuleCore principleDependencies always point inward; inner layers never import outer ones
EntityInnermost layerDomain object with enterprise-wide business rules
Use CaseSecond layerApplication-specific logic that orchestrates entities for one user goal
PortBoundary interfaceInterface defining how the app communicates with the outside world
AdapterOuter layerConcrete implementation of a port; translates between domain and technology
GatewayInterface adapterAdapter wrapping an external data store or service
PresenterInterface adapterFormats use-case output into a view model for the delivery mechanism
CouplingDesign qualityDegree of dependency between modules (lower is better)
CohesionDesign qualityDegree to which a module’s elements belong together (higher is better)

Поширені запитання

Про що ця стаття "Словник Clean Architecture і Hexagonal Architecture"?

Очищення шарів архітектури, портів і адаптерів, правила залежностей, випадків використання і словника меж архітектури.

Чи безкоштовна ця стаття?

Так. Усі статті на CoderSlingo, включно з цією, доступні безкоштовно без реєстрації.

Скільки часу займає читання "Словник Clean Architecture і Hexagonal Architecture"?

Приблизно 9 min.