Вопрос проверяет понимание принципов Feature-Sliced Design (FSD) и умение предотвращать циклические зависимости между слоями и слайсами архитектуры.
Feature-Sliced Design (FSD) — это архитектурная методология для фронтенд-приложений, которая организует код по бизнес-логике (слайсы) и техническим слоям. Циклические зависимости нарушают основное правило FSD — однонаправленный поток данных и зависимостей, что ведет к сложностям в поддержке, тестировании и переиспользовании кода.
Цикл возникает, когда модуль A импортирует модуль B, а модуль B, прямо или через цепочку других модулей, импортирует модуль A. В контексте FSD это может быть между слоями (например, widget импортирует из feature, а тот импортирует что-то из того же widget) или между слайсами одной фичи. Это делает код связанным (coupling), усложняет рефакторинг и может привести к ошибкам при инициализации.
shared <- entities <- features <- widgets <- pages <- app. Не позволяйте модулям из нижних слоев импортировать что-либо из верхних.index.ts (public API). Внутренняя реализация скрыта, а импорты из других слайсов делаются только через этот публичный интерфейс.shared или entities. Тогда оба модуля будут зависеть от этой абстракции, а не друг от друга.madge или dependency-cruiser могут автоматически обнаруживать циклические зависимости в проекте и останавливать сборку.Рассмотрим проблему: виджет UserCard (слой widgets) использует хук useUserData из фичи user-profile (слой features), а этот хук, в свою очередь, пытается использовать компонент Avatar из того же виджета UserCard — возникает цикл.
// НЕПРАВИЛЬНО: Циклическая зависимость
// widgets/user-card/ui/Avatar.tsx
export const Avatar = ({ src }) => ;
// widgets/user-card/ui/UserCard.tsx
import { useUserData } from 'features/user-profile';
export const UserCard = () => {
const user = useUserData(); // Импорт из features
return ;
};
// features/user-profile/lib/useUserData.ts
import { Avatar } from 'widgets/user-card/ui/Avatar'; // Обратный импорт!
export const useUserData = () => {
// ... логика, использующая Avatar
};Решение: вынести компонент Avatar в общий слой (например, shared/ui), чтобы оба модуля зависели от него, но не друг от друга.
// ПРАВИЛЬНО: Убираем цикл через общий shared слой
// shared/ui/Avatar/Avatar.tsx
export const Avatar = ({ src }) => ;
// widgets/user-card/ui/UserCard.tsx
import { useUserData } from 'features/user-profile';
import { Avatar } from 'shared/ui/Avatar';
export const UserCard = () => {
const user = useUserData();
return ;
};
// features/user-profile/lib/useUserData.ts
// Больше не импортирует Avatar из widgets
import { Avatar } from 'shared/ui/Avatar'; // Если нужно — импорт из shared
export const useUserData = () => {
// ...
};Избегание циклических зависимостей в FSD критически важно для поддержания чистой, масштабируемой архитектуры. Строгое соблюдение направленности импортов, использование публичных API и вынос общих абстракций в нижние слои позволяют сохранить код декомпозированным и тестируемым. Применяйте эти практики с самого начала проекта и автоматизируйте проверку зависимостей.