Порочные связи между компонентами

Думаю, многие пытались оценивать качество своей архитектуры, анализируя входящие и исходящие связи между элементами. В этом контексте принято говорить о cohesion и coupling. А что насчёт качества связей?

Принцип каскадного снижения связанности, предложенный Русланом Сафиным, контролирует баланс между cohesion и coupling на разных уровнях декомпозиции. Однако данный принцип, как и сами понятия cohesion и coupling, не отвечает на вопрос, насколько оправданы существующие связи, насколько они здоровы. Что если наличие связи между элементами не связывает их, а привязывает один к другому, сдерживая развитие обоих? Такие связи можно назвать обременительными, нездоровыми, порочными.

Большую попытку категоризировать виды связей предпринял Meilir Page-Jones в своей книге “What Every Programmer Should Know About Object-Oriented Design” (1996), введя понятие connascence (от лат. “рождённые вместе”). В русскоязычной литературе можно встретить прямую транслитерацию — коннасценция, но я предпочитаю слово взаимозависимость (interdependence), т.к. сам автор сказал, что это точный синоним.

Два элемента взаимозависимы, если для поддержания общей работоспособности они должны изменяться одновременно.

Вместе с определением было предложено несколько степеней взаимозависимости, которые подразделяются на две группы: статические (в программном коде) и динамические (во время исполнения).

Статические делятся по имени, типу, смыслу, порядку и алгоритму. Например, вызов метода и его определение (в классе) связаны по имени; функционально зависимые операторы связаны порядком; код клиента и сервера связан общим алгоритмом шифрования. Динамические делятся по порядку и времени выполнения, значению, ссылочной идентичности и способу взаимодействия. Например, отправить письмо невозможно до его создания; без синхронизации возникнет состояние гонки; синхронное или асинхронное взаимодействие. Подробные примеры см. на https://connascence.io/

Как это можно привязать к практике?

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

Всё равно непонятно, давай рассмотрим пример!

Короче, во-первых, нужно убедиться, что декомпозиция сделана правильно. Это уже решит большую часть проблем со связями или, наоборот, выявит недостатки. Во-вторых, выявленные взаимозависимости нужно удалить или свести их влияние к минимуму. Например, вы заметили, как модель одного ограниченного контекста передается без изменений в другой. Что будет, если эта модель поменяется в одном из контекстов? Возможно, стоит задуматься об инкапсуляции транспортного слоя между двумя контекстами? Самый банальный вариант — использовать DTO.

Идея оценки связей путём их категоризации вполне разумна, но в оригинальном подходе есть недостатки. Во-первых, весь фокус смещается с архитектуры, которая должна определять правила игры, на детали реализации, качество и чистоту кода. Во-вторых, многие предложенные категории устарели и требуют пересмотра. Например, формы статической взаимозависимости уже давно неактуальны.

Возьму на себя смелость сформулировать категории связей, актуальные на сегодняшний день:

  • Явная/неявная. Например, АОП провоцирует множество неявных связей и взаимополаганий.
  • Статическая/динамическая. Связь между компонентами предопределена или определяется во время исполнения.
  • Асинхронная/синхронная. Есть ли жесткая синхронизация взаимодействия компонентов, управляемая оркеструющим алгоритмом/механизмом.
  • Надёжная/ненадёжная. Насколько связь устойчива к различным сбоям и сверхнагрузкам, способна ли она восстанавливаться после такого.

Список можно и нужно уточнять. Однако, как мне кажется, такая категоризация хорошо укладывается в концепцию архитектурных свойств и позволяет прорабатывать каждую категорию в отдельности. Так например, если нужна надёжная связь, то она сопряжена с такими эксплуатационными свойствами как надёжность, прочность, устойчивость и адаптивность.


Желаю всем здоровых связей! А в следующий раз разберём, что с этим делать дальше.



Понравилась статья?

Посмею напомнить, что у меня есть Telegram-канал Архитектоника в ИТ, где я публикую материал на похожие темы примерно раз в неделю. Подписчики меня мотивируют, но ещё больше мотивируют живые дискуссии, ведь именно в них рождается истина. Поэтому подписывайтесь на канал и будем оставаться на связи! ;-)

Статьи из той же категории: