Как решал проблему кросс-импортов в Feature-Sliced Design

Ответ

В FSD кросс-импорты между слоями - это антипаттерн. Решаю проблему так:

  1. Выношу общую логику на уровень выше - если модуль из одного слоя нужен в другом, поднимаю его в shared или переношу в нужный слой.

  2. Использую Public API - в каждом слое есть index.ts, который экспортирует только разрешенные модули:

// features/profile/public-api.ts
export { ProfileCard } from './ui/ProfileCard';
  1. Применяю Dependency Injection - если нужно взаимодействие между слоями, передаю зависимости через props/context:
// widgets/UserList
<UserList profileCard={<ProfileCard />} />
  1. Использую паттерны Event Bus или State Management для связи между несвязанными модулями.

  2. Настраиваю линтеры с правилами no-relative-imports и restricted-imports для автоматической проверки.

Ответ 18+ 🔞

Ах ты ж ёпта, ну и тема у нас сегодня — про эти ваши кросс-импорты между слоями! Это ж классический пиздец, когда всё начинает друг на друга ссылаться, как мартышки в зоопарке. Короче, это антипаттерн, и если его не остановить, проект превращается в спагетти, где хуй разберёшь, кто от кого зависит.

Вот как я с этим воюю, чтобы не охуеть с утра от ошибок сборки:

  1. Вынос общей логики нахуй повыше. Если модуль из features вдруг понадобился в entities, а entities лезет в widgets — это пизда. Значит, надо эту общую хуйню вынести либо в shared, либо вообще перетащить в тот слой, где она теперь по праву живёт. Нечего шастать по чужим квартирам!

  2. Public API — наш бро. В каждом слое заводим файлик public-api.ts (или index.ts), который работает как швейцар в дорогом отеле. Он решает, кого пускать на выход. Всё внутреннее говно остаётся внутри, а наружу торчит только красивая, разрешённая морда.

// features/profile/public-api.ts
export { ProfileCard } from './ui/ProfileCard'; // Можно
// А вот сюда всякие internal/private-модули — нихуя!
  1. Dependency Injection, ёбана! Если один слой реально должен работать с другим, не надо лезть к нему в окно. Передаём зависимость явно, как порядочные люди — через пропсы или контекст.
// widgets/UserList
<UserList profileCard={<ProfileCard />} /> // Вот, держи, что тебе нужно. Больше ни хуя не проси.
  1. Event Bus или State Management для особо ебучих случаев. Когда модули в разных концах архитектуры должны общаться, но видеть друг друга им западло — пусть общаются через события или глобальное состояние. Как два соседа через забор: крикнул — и всё, не надо в гости ходить.

  2. Линтеры — наш надёжный ёбарь. Настраиваем правила no-relative-imports и restricted-imports, чтобы эта гребанная проверка была автоматической. Запушил хуйню — получи ошибку в CI, и иди, мудак, исправляй. Волшебно, пиздец!

Короче, суть в том, чтобы зависимости текли в одну сторону, как вода с горы. Сверху вниз: apppageswidgetsfeaturesentitiesshared. Как только пошло обратно — это пизда, alarm bells, надо решать. Иначе потом разгребать этот пиздец будет овердохуища больно.