Stop Circular Dependencies With SDP
Циклические зависимости — это тихие убийцы. Они не вызывают ошибок сборки. Они не вызывают исключений при импорте во время выполнения. Вместо этого они приводят к появлению едва заметных значений undefined, которые всплывают в продакшене спустя недели.
Цикл возникает, когда модуль A импортирует что-то из B, а B импортирует что-то обратно из A.
JavaScript разрешает такие импорты незаметно. Если модуль A всё еще загружается в тот момент, когда модуль B запрашивает его, JavaScript возвращает пустой объект. Ваш код ломается позже, а стек вызовов указывает не на то место.
Это можно исправить с помощью dependency-cruiser. Этот инструмент сканирует ваши файлы и строит граф зависимостей. Он работает с TypeScript и монорепозиториями.
How to set it up:
- Добавьте его в dev dependencies:
yarn add -D dependency-cruiser - Добавьте скрипт в
package.json:"depcruise": "depcruise packages --config .dependency-cruiser.js" - Создайте файл
.dependency-cruiser.js, чтобы отлавливать циклы.
Но найти цикл — это только половина дела. Вы должны не дать им возникнуть.
Используйте принцип стабильных зависимостей (Stable Dependencies Principle, SDP). Это правило гласит: вы должны зависеть в направлении стабильности.
Стабильность — это структурное свойство. Модуль является стабильным, когда от него зависят многие другие модули. Если вы измените стабильный модуль, это затронет многих людей. Эта цена делает его стабильным.
Используйте формулу нестабильности: I = Fan-Out / (Fan-In + Fan-Out)
• I = 0 означает, что модуль максимально стабилен. Все зависят от него, а он не зависит ни от чего. • I = 1 означает, что модуль максимально нестабилен. Он зависит от многих вещей, а от него никто не зависит.
The rule: Показатель нестабильности модуля должен быть выше показателя каждого модуля, который он импортирует. Ваши стрелки должны указывать в сторону меньшей нестабильности.
Ваши низкоуровневые утилиты (I ≈ 0) никогда не должны импортировать что-либо из высокоуровневых страниц (I ≈ 1).
Вы можете превратить этот принцип в автоматизированное правило с помощью dependency-cruiser. Вместо того чтобы просто искать циклы, проверяйте направление:
- Запретите утилитам импортировать что-либо из фич (features).
- Запретите фичам импортировать что-либо из страниц (pages).
Это устраняет структурные условия, создающие циклы. Вы перестаете реагировать на баги и начинаете их предотвращать.
