open atlas
↑ К треку
Архитектурные паттерны ARCH · 05 · 01

Концентрическое правило зависимостей

Clean и Onion Architecture располагают слои концентрическими кольцами. Единственное жёсткое правило: source-code зависимости указывают только внутрь — к более высокоуровневой политике.

ARCH Middle ◷ 22 min
Уровень
ОсновыJuniorMiddleSenior

На B2B-платформе заказов вырос четырёхслойный стек: presentation, application, domain, infrastructure. Команда изучила hexagonal architecture и тщательно расставила интерфейсы на каждой границе. Но спустя шесть месяцев обнаружилась проблема: domain layer импортировал из infrastructure layer утилиту форматирования денежных значений. Утилита жила в infrastructure, потому что изначально была помещена туда рядом с кодом конвертации валют для базы данных. Границы интерфейсов были целы — domain вызывал интерфейс, а не конкретный класс, — но сам интерфейс был определён внутри пакета infrastructure. Это значило, что domain зависел от пакета infrastructure ради определения интерфейса. Инверсия зависимостей была неполной. Когда в 2017 году вышла книга Clean Architecture, Robert Martin предлагал не что-то совершенно новое. Он брал ключевое наблюдение hexagonal architecture — порты, принадлежащие приложению — и систематизировал его в правило, применимое к каждой границе между слоями. Формулировка была точной: source-code зависимости могут указывать только внутрь — к более высокоуровневой политике.

Модель концентрических колец

Как Clean Architecture (Robert Martin, 2012 blog post, 2017 книга), так и Onion Architecture (Jeffrey Palermo, 2008) организуют систему в виде набора концентрических окружностей — колец, вложенных одно в другое. В центре — наиболее важный и стабильный код, на внешнем крае — наиболее изменчивый, специфичный для инфраструктуры.

Кольца немного различаются между двумя моделями, но логика одна. Канонические четыре кольца Мартина:

  1. Entities (самое внутреннее) — общеприкладные бизнес-правила; наиболее стабильный код системы.
  2. Use Cases — бизнес-правила конкретного приложения; оркестрируют entities для достижения целей.
  3. Interface Adapters — переводят между форматом use case и внешним миром (controllers, presenters, gateways).
  4. Frameworks and Drivers (самое внешнее) — веб-фреймворки, базы данных, внешние библиотеки; наиболее изменчивый код.

В Onion Палермо используются немного другие названия, но та же логика: Domain Model в центре, затем Domain Services, затем Application Services, затем внешнее кольцо Infrastructure. Обе модели приходят к одному структурному ограничению.

Правило зависимостей — почему это правило, а не рекомендация

Единственное принудительно соблюдаемое ограничение: source-code зависимость (import, reference, compilation dependency) может указывать только внутрь. Ничто во внутреннем кольце не может ссылаться на что-либо во внешнем. Entity не может импортировать use case. Use case не может импортировать controller. Interface adapter не может импортировать веб-фреймворк — веб-фреймворк импортирует adapter.

Это не принцип стиля. Это структурное ограничение с конкретной отдачей: если ничто во внутренних кольцах не знает о внешних, внутренние кольца можно компилировать, тестировать и анализировать в полной изоляции от инфраструктуры. Все тесты entity и use case можно запустить без базы данных, веб-сервера или ORM.

Нарушение из вступительной истории — domain, импортирующий утилиту форматирования денег из пакета infrastructure — нарушало это правило. Даже при наличии интерфейса domain должен был импортировать его из пакета infrastructure. Путь импорта пересекал границу в неправильном направлении. Исправление то же, что и в hexagonal: перенести интерфейс во внутреннее кольцо (domain его определяет), а внешнее кольцо реализует его.

Почему это работает

Почему стабильность возрастает к центру? Внутренние кольца содержат чистую бизнес-логику: правила, которые меняются только при изменении самой бизнес-модели. Внешние кольца содержат технологические решения: базы данных, фреймворки, протоколы — они меняются по технологическим причинам (новая версия ORM, смена облачного провайдера, deprecation стороннего API), не связанным с бизнес-логикой. Структурируя зависимости к стабильному центру, вы гарантируете, что технологические изменения не могут вызвать изменения бизнес-правил.

Как Clean Architecture обобщает hexagonal

В unit 04 была представлена hexagonal architecture: порты, принадлежащие приложению, адаптеры с обеих сторон. Clean и Onion Architecture обобщают это с одной границы (domain/infrastructure) до N концентрических границ.

В hexagonal инверсия происходит один раз: domain определяет интерфейсы портов; infrastructure реализует их. Clean и Onion применяют ту же инверсию на каждой границе кольца. Кольцо Interface Adapters определяет интерфейсы, которые должны реализовывать Frameworks & Drivers. Кольцо Use Cases определяет интерфейсы (output ports, presenter interfaces), которые должно реализовывать кольцо Interface Adapters. Инверсия распространяется по всей цепочке внутрь.

Clean vs Onion — сходство и различие

Clean Architecture и Onion Architecture созданы независимо, но структурно сходятся. Обе пришли к одному правилу зависимостей. Практические различия:

Onion Architecture (Palermo, 2008) акцентирует Domain Model как абсолютный центр — чистые domain objects без каких-либо зависимостей. Domain Services в следующем кольце, Application Services далее, Infrastructure снаружи.

Clean Architecture (Martin, 2012/2017) вводит явное кольцо Interface Adapters, ответственное за перевод всех форматов данных между миром use case и внешним миром. Также разграничивает Entities (общеприкладные правила) и Use Cases (прикладные правила).

На практике команды часто смешивают оба подхода. Важно не то, какие названия колец вы используете — важно, соблюдается ли правило зависимостей.

lesson.inset.note

Концентрическая модель — это не фиксированное число колец. Мартин прямо говорит: диаграмма с четырьмя кольцами иллюстративна; реальные системы могут иметь больше. Что не обсуждается — это направление зависимости: только внутрь. Можно добавить кольцо Anti-Corruption Layer, кольцо Reporting, кольцо External Services — если зависимости каждого нового кольца указывают внутрь, архитектура корректна.

Викторина

Кольцо Use Cases платформы импортирует интерфейс CurrencyFormatter из кольца Infrastructure, где изначально была написана утилита форматирования денег. Интерфейс не содержит инфраструктурно-специфичных методов — он чистый. Является ли это нарушением правила зависимостей, и почему?

Викторина

Команда хочет добавить кольцо генерации PDF-отчётов снаружи кольца Interface Adapters, между Interface Adapters и Frameworks. Библиотека PDF — сторонний пакет. Где должны быть определены интерфейсы кольца PDF, и какое кольцо их реализует?

Викторина

Кодовая база Onion Architecture имеет структуру: Domain Model → Domain Services → Application Services → Infrastructure. Разработчик говорит: «Onion и hexagonal — это одно и то же: оба просто помещают интерфейс между domain и базой данных». Чего не хватает в этом описании?

Вспомните перед уходом
  1. 01
    Что такое dependency rule в Clean Architecture, и почему оно обеспечивает независимую тестируемость внутренних колец?
  2. 02
    Как Clean Architecture обобщает hexagonal architecture?
  3. 03
    В чём структурное различие между Onion Architecture и Clean Architecture?
Итог

Clean и Onion Architecture — концентрические кольцевые модели, обобщающие ключевое наблюдение hexagonal architecture на каждую границу слоёв. Центр архитектуры содержит наиболее стабильный, абстрактный код — чистые бизнес-правила, меняющиеся только при изменении самого бизнеса. Внешние кольца содержат технологические решения — базы данных, фреймворки, протоколы, — меняющиеся по технологическим причинам, не связанным с бизнес-логикой.

Dependency rule формулирует это точно: source-code зависимости могут указывать только внутрь. Внутреннее кольцо никогда не импортирует из внешнего. Внутреннее кольцо определяет интерфейс; внешнее реализует его. Каждая граница кольца применяет эту инверсию.

Нарушение из вступительной истории — кольцо Use Cases, импортирующее интерфейс из пакета Infrastructure — было технически тонким: интерфейс существовал, конкретный тип не импортировался. Но определение интерфейса жило в неправильном кольце, и у Use Cases был путь импорта, указывающий наружу. Исправление — владение: перенести интерфейс в кольцо, которое его потребляет.

Clean Architecture (Martin) и Onion Architecture (Palermo) различаются в основном названиями колец и акцентами. Обе приходят к одному структурному правилу. Число колец не фиксировано на четырёх. Что не обсуждается — направление: только внутрь.

Следующий урок рассматривает, что живёт во втором кольце — кольце Use Cases — и как паттерн interactor фиксирует «что делает приложение» как первоклассную структурную единицу.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

Примени это

Примени этот урок в реальном проекте.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.