В eslint есть одно простое, но мощное правило, которое поможет вам в поддержании архитектуры приложения.
import/no-restricted-paths позволяет указать зону, по которой запрещено импортировать определенные пути:
"zones": [{
"target": ["./moduleA"],
"from": ["./moduleB"],
"message": 'Модуль A не может импортировать модуль B',
}]
Импорт модуля B в модуль A вызовет ошибку в IDE:
ESLint: Unexpected path "/moduleB" imported in restricted zone.
Модуль A не может импортировать модуль B(import/no-restricted-paths)
Импорты — это отражение связей между модулями в коде, а связи в хорошо спроектированной архитектуре должны контролироваться и быть прозрачными.
Далее рассмотрим, как одна простая настройка eslint-правила поможет при проектировании, разработке или рефакторинге сложных систем.
Влияние импортов на архитектуру
В любой архитектуре есть модули, которые взаимодействуют друг с другом.
Каждый такой модуль строится в первую очередь на принципе единственной ответственности (The Single Responsibility Principle или SRP) — «модуль должен иметь одну и только одну причину для изменения».
Правильные связи между этими модулями — один из важных аспектов построения масштабируемой архитектуры и развития проекта. Когда мы импортируем один модуль в другой, мы создаем между ними связь, следовательно, импорт = связь.
Как часто вы сталкиваетесь с архитектурными спорами в проекте? Бывает ли, что неудачные архитектурные решения пролезают в проект даже после код-ревью? Знаете ли вы, насколько связаны модули в вашем приложении?
Ответы на эти вопросы зависят от процессов, структуры вашего отдела, наличия в команде архитектора или людей, которые следят за архитектурой и т. п.
В целом, кажется, всё сводится к человеческому фактору. А человеческий фактор нужно автоматизировать.
Пример использования
Рассмотрим следующий пример. Стрелками на картинке обозначены импорты/связи между модулями
Кажется, ui‑библиотека не должна уметь ходить на сервер или оперировать сущностями, связанными с бэкэндом. Такая, на первый взгляд, некритичная ошибка может привести к проблемам с масштабированием и увеличению трудозатрат разработки.
Проблема утрированная, она, скорее всего, код‑ревью никогда не пройдет, но каждому человеку свойственно ошибаться.
Так как же это автоматизировать?
rules: {
'import/no-restricted-paths': ['error', {
"basePath": rootPath,
"zones": [{
"target": ["./lib/ui-kit/*"],
"from": ["./backend/rest-api"],
"message": 'Пакет ui-kit не может содержать импорт из rest-api',
}],
}],
}
Не правда ли, очень дешево написать пару строчек конфига и решить потенциальные проблемы архитектуры?
Заключение
Проектирование модуля, в котором соблюдается Single Responsibility Principle упрощает дальнейшие изменения и поддержку, так как это освобождает команду от распутывания сложных взаимосвязей между различными сущностями. Также несвязанные модули снижают риск возникновения проблем в других местах при изменениях.
Есть еще много способов снизить связность — микросервисы, монорепа, отдельные пакеты. Но даже там могут возникнуть ошибки, если один пакет будет использовать другой.
Итого import/no-restricted-paths:
Помогает контролировать связанность модулей.
Упрощает рефакторинг модуля.
Можно зафиксировать связи модуля и начать его доработку.Улучшает процесс.Можно построить назначение ревьюеров на изменение конфига, построить схему связей в приложении. Тогда о критично важных изменениях в проекте будет знать вся команда.
Теперь с помощью простого правила eslint можно быть уверенным, что принцип SRP соблюдается в полной мере. Нам это пригодилось при переходе от монолита к монорепе, помогло зафиксировать связи между сущностями и начать распил монолита.
А какие неожиданные решения архитектурных проблем встречали вы?
Комментарии (11)
vsviridov
00.00.0000 00:00Попробуйте https://feature-sliced.design
bogatyrev_vl
00.00.0000 00:00Кажется данный подход хорошо подходит для изоляции слоев в feature-sliced????
Или там есть своя автоматизированная изоляция?
vsviridov
00.00.0000 00:00Там есть
eslint
плагин, который ругается на неправильные импорты...bogatyrev_vl
00.00.0000 00:00Изучил немного)
Если там линтер настраивается как описано тут, то они используют как раз схожий подход???? Только с дополнительными апгрейдами)
Спасибо, возможно добавим ещё улучшений линтинга в наш проект!
ArthurG
00.00.0000 00:00Спасибо за статью!
Очень больная тема. Сам использую похожий механизм в Nx.
Хотелось бы инструмент, который бы умел в нескольких измерениях контролировать связи удобно.
bogatyrev_vl
00.00.0000 00:00Раньше не видел этой возможности nx, прикольно????
Нет проблем в проекте из-за index файлов?
Мы пока решили от них отказаться, так как с ними плохо отрабатывал tree shaking у webpack, и после отказа бандл сократился на ~30%)) Но в будущем попробуем вернуться к ним, когда наладим все связи между модулями, так как идея с публичным api пакета/модуля крайне верная)
ArthurG
00.00.0000 00:00Я занимаюсь серверной разработкой, поэтому с индекс-файлами не сражался.
Вебпак неправильно реэкспорта обрабатывает?
bogatyrev_vl
00.00.0000 00:00+1Не то чтобы неверно, но у него возникают сложности при оптимизации файлов с сайдэффектами https://webpack.js.org/guides/tree-shaking/
cry_san
Дополните заголовок словом "приложения"
fzkirablackwhy Автор
добавила, спасибо!