Метрики для улучшения архитектуры ПО

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

Вместе с командой мы задались вопросом: как же так получилось? Мы следовали общим рекомендациям, проводили тестирование и внедряли хорошие практики, но все же оказались в положении, когда после изменения одной строки требуется три дня на исправление.

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

Вероятно, у вас есть бизнес-логика, поэтому вы создаете модуль, который ее оборачивает. Скорее всего, вам также понадобится API, поэтому вы добавляете модуль API. Помимо этого, требуется безопасность, поэтому вы добавляете модуль безопасности. А если вы работаете с DDD, используете CQRS или шестиугольную архитектуру, то, вероятно, в конечном счете получите множество различных модулей с непонятным назначением.

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

Оказывается, важно не только создавать разные модули и разделять ответственности, но и правильным образом соединять эти модули и вводить между ними зависимости. Роберт Мартин упоминал об этой проблеме в своей книге “Чистая архитектура” и предложил вариант ее решения.

Мартин предлагает: ввести своего рода метрики, полностью основанные на том, как модули системы связаны между собой. Цель этих метрик  —  определить, насколько масштабируема и удобна в обслуживании система.

Прежде чем перейти к практической части, кратко рассмотрим теорию, лежащую в основе метрик, которую Роберт Мартин объяснил гораздо более подробно в своей книге.


Метрики управления зависимостями

Показатели по управлению зависимостями опираются на два принципа:

  • принцип стабильных зависимостей;
  • принцип стабильных абстракций.

В следующей части мы будем ссылаться на термин “компонент”, который практически эквивалентен модулю Maven в экосистеме Java и Maven.

Принцип стабильных зависимостей

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

Что делает компонент неспособным к изменениям (т.е. “стабильным”)? На это могут повлиять многие факторы: размер, сложность, четкость, количество компонентов, которые от него зависят и т. д.

Нас особенно интересует последний случай, когда компонент имеет ряд зависимых от него компонентов. Компонент с большим количеством входящих зависимостей очень трудно изменить, отсюда и название  —  “стабильный”.

“Нестабильный” компонент  —  нечто прямо противоположное. Он зависит от других компонентов, но при этом никакие другие компоненты от него не зависят.

Мы можем определить нестабильность компонента следующим образом:

Теперь можно определить фактический принцип стабильных зависимостей:

“Компонент всегда должен зависеть от более стабильного компонента”.

На следующем изображении мы видим явное нарушение принципа:

Здесь стабильный компонент (зависит от двух компонентов) сам зависит от нестабильного компонента (а тот зависит только от одного  —  стабильного компонента).

Принцип стабильных абстракций

“Абстрактный компонент  —  это тот, который содержит только интерфейсы или абстрактные классы”.

Абстрактные компоненты очень стабильны и идеально подходят в качестве зависимости для менее стабильных компонентов.

Мы можем определить уровень абстрактности отдельного компонента следующим образом:

Теперь можно определить принцип стабильных абстракций:

“Компонент всегда должен зависеть от более абстрактного компонента”.


Главная последовательность

Мы определили два принципа. Теперь попробуем определить связь между показателями нестабильности и абстрактности по оси x-y:

Координаты X-Y, где ось X  —  нестабильность, ось Y  —  абстрактность компонента

Если провести прямую между точками (0,1) и (1,0) на оси x-y, указанной выше, то мы получим линию, которая называется главной последовательностью:

Эта линия представляет собой область, где должны находиться все компоненты. Компонент, который находится на главной последовательности, не будет ни слишком абстрактным, ни слишком нестабильным.

Теперь, когда мы определили, где именно должны располагаться компоненты, пришло время обозначить, где они не должны располагаться, а именно в так называемых зонах исключений:

Как видно, есть две области, в которых компоненты не должны присутствовать.

Зона боли

Очень стабильные и конкретные компоненты. Это состояние нежелательно, поскольку такие компоненты крайне негибкие. Их нельзя расширить, потому что им не хватает абстрактности, и их очень трудно изменить из-за их высокой стабильности.

Зона бесполезности

Очень абстрактные компоненты без каких-либо зависимостей, то есть бесполезные.

Расстояние от главной последовательности

Настал момент определить метрику, соответствующую тому, насколько далеко находится компонент от главной последовательности:

Любой компонент, значение D которого не близко к нулю, может быть дополнительно пересмотрен и реструктурирован.

Статистический анализ проекта также возможен путем вычисления среднего значения и дисперсии всех показателей D для компонентов.
Соответствующий проект должен иметь средние и дисперсионные значения, близкие к нулю.


Dependency Management Metrics

Теоретическая часть окончена. Теперь посмотрим, как извлечь выгоду из этих показателей на более практическом уровне  —  в частности, для проектов, использующих Java и Maven.

Dependency Management Metrics  —  это плагин Maven, который вычисляет и выводит показатели управления зависимостями для каждого модуля Maven в рамках многомодульного проекта на Java. Плагин учитывает только те зависимости, которые являются внутренними и специфичными для проекта, без внешних (например, Spring).

Для каждого модуля Maven учитываются следующие показатели:

  • показатель стабильности;
  • показатель абстрактности;
  • расстояние от главной последовательности.

Плагин также выводит модули, присутствующие в “зоне боли” и “зоне бесполезности”, в отдельный раздел “зоны исключений”.

Кроме того, плагин может заблокировать сборку через Maven, если будет нарушен принцип стабильных зависимостей или стабильных абстракций.

Результаты

Запуск плагина в многомодульном Java-проекте приводит к следующему результату:

Из выходных данных мы видим, что некоторые из модулей, которые больше всего отклонились от среднего, относятся к “зоне боли”: они чересчур стабильны и им явно не хватает абстракций.

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


Заключение

Сам по себе плагин не решает проблем. Его цель  —  указать на то, что можно усовершенствовать.

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

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Ignatij Gichevski: Using metrics for crafting a better software architecture — the Java and Maven approach

Предыдущая статьяКак повысить производительность бэкенд-приложений
Следующая статья7 способов создать приложение React