Архитектура как код: тестируем зависимости, чтобы не утонуть
Довольно давно я сталкиваюсь с одной и той же болью: архитектурные решения, согласованные в начале проекта, со временем размываются. Высокоуровневые слои начинают тащить зависимости из низкоуровневых, параллельные модули сплетаются в циклический граф, а в handlers вдруг появляется прямой импорт repositories.
Рефакторинг превращается в квест на несколько спринтов, а документация в виде PlantUML живёт своей жизнью, отдельно от кода.
💡 На одном из докладов я услышал простую мысль: граф зависимостей — это такой же артефакт, как и бизнес-логика, и его тоже можно тестировать. И если мы пишем юнит-тесты на функции, почему бы не написать тесты на архитектуру?
🔧 Недавно нашёл библиотеку ArchUnitPython — это адаптация известной Java-библиотеки ArchUnit. Она позволяет: - Проверять зависимости между слоями (например, Models не должен знать о Flows); - Обнаруживать циклические импорты; - Сравнивать реальную структуру пакетов с диаграммой в PlantUML; - Писать кастомные правила для файлов (например, что классы в папке flows должны оканчиваться на UseCase).
Пример теста можно увидеть на скриншоте.
🚀 Дополнительно я добавил: - Контроль легаси-импортов. Завел белый список модулей, которые пока можно импортировать из старых мест. Новые импорты — запрещены. Чтобы рефакторинг проходил плавно. - Проверка суффиксов классов. В папке flows все классы должны заканчиваться на UseCase, а в repositories — на Repository. - Сравнение с PlantUML. Теперь у нас есть тест, который сверяет реальную структуру пакетов с диаграммой, приведенной в документации. Если схема устарела или кто-то создал пакет не по плану — тест подсветит это.
📌 В итоге: тесты по архитектуре — это не магия, а пара десятков строк кода, которые спасают дни. Они не заменят человеческий взгля, но станут надёжным щитом от случайных (и не очень) нарушений в CI.