Перейти к основному содержимому

Кросс-импорты

WIP

Статья находится в процессе написания

Чтобы ускорить ее появление, можно:


🍰 Stay tuned!

Кросс-импорты появляются тогда, когда слой/абстракция начинает брать слишком много ответственности, чем должна. Именно поэтому методология выделяет новые слои, которые позволяют расцепить эти кросс-импорты.

Введение

Кросс-импорт - ситуация, при которой в рамках какого-либо слоя появилась необходимость импорта данных одного слайса в другой.

Важно

Речь в статье идёт только о конфликтных ситуациях при импортах между слайсами. В рамках одного слайса, фрагменты могут импортировать друг друга, такая ситуация не запрещена.

Представим, что разрабатываем приложение TODO List для хранения заметок. Необходимо хранить несколько карточек с задачами и отображать их на доске. Досок с карточками может быть несколько. Рассмотрим упрощённый пример структуры такого приложения:

  • 📂 entities
    • 📂 task
      • 📁 ui
        • 📄 TaskCard
    • 📁 board
      • 📁 ui
        • 📄 BoardTasks

В данном примере компоненту BoardTasks для отображения карточек необходим импорт компонента TaskCard. Импортировать компонент TaskCard напрямую нельзя, так как это нарушит принцип низкой связности и усложнит поддержку проекта в дальнейшем.

Варианты решения кросс-импортов

Объединение нескольких слайсов в один

Как видно из примера выше, доменная область компонентов TaskCard и BoardTasks довольно похожа, а значит существует возможность создания производного слайса из двух существующих, где оба метода находились бы в одной директории.

Результат объединения слайсов task и board:

  • 📂 entities
    • 📂 board
      • 📁 ui
        • 📄 TaskCard
        • 📄 BoardTasks

Как видно из примера, мы избавились от ситуации, когда один слайс импортирует данные из другого и достигли высокой сцепленности и низкой связности в рамках сегмента одного слайса.

Перемещение кода на более высокий слой

Данный вариант скорее подходит для тех случаев, для которых объединение нескольких слайсов в один затруднено или невозможно.

Вполне вероятно, что слайс, в который Вы импортируете данные другого слайса сильно насыщен логически, и его можно переместить на слой выше, дабы устранить связность слайсов в рамках одного слоя.

Представим, что слайс board более насыщен бизнес-логикой, чем на примере выше, тогда объединение его со слайсом task создаст дополнительный беспорядок, поэтому хорошим решением будет реструктуризовать логику слайса board, в более высоком слое.

Результат перемещения логики слайсов task и board:

  • 📂 entities
    • 📂 task
      • 📁 ui
        • 📄 TaskCard
  • 📂 features
    • 📁 view-board
      • 📁 ui
        • 📄 BoardTasks

Реструктуризовав логику слайса board на слое features (не обязательно на него, подойдёт любой слой выше по иерархии), его доменная область более конкретно уточнилась появилась возможность построить правильный с точки зрения иерархии слоёв механизм импортов.

Дублирование кода

Если объем данных, который Вы собираетесь импортировать из другого слайса невелик, возможно стоит будет продублировать его в текущий слайс. Тем не менее, такой вариант не должен являться приоритетным, среди всех вышеописанных.

А если кросс-импорт неизбежен?

Внимание

Подход, описанный в данном разделе является экспериментальным и ещё не стандартизирован. Тем не менее, в экзотических ситуациях, информация данного раздела может быть полезна.

Если в Вашем проекте необходим кросс-импорт и три вышеописанных способа не помогли, сообщество Feature Sliced Design предлагает следующий вариант решения проблемы.

  • 📂 entities
    • 📂 task
      • 📁 ui
        • 📄 TaskCard
    • @x
    • 📁 board
      • 📁 ui
        • 📄 BoardTasks
    • @x

@x

@x - это эксперементальный подход, когда внутри слайса можно создать явный публичный экспорт только тех модулей, которые можно использовать в рамках нескольких слайсов одного слоя.

Рассмотрим пример (разметка TSX):

entities/task/@x.ts
export { TaskCard } from './ui/TaskCard.tsx';

Компонент внутри слайса, которому необходим кросс-импорт:

entities/board/ui/BoardTasks.tsx
import { TaskCard } from '/entities/task/@x/TaskCard';

export const BoardTasks = () => {  return (
    <div>
      <TaskCard title="Task#1" description="Description..." />
      <TaskCard title="Task#2" description="Description..." />
      <TaskCard title="Task#3" description="Description..." />
    </div>
  )
}

Как видно по пути импорта в файле BoardTasks, благодаря экспорту из файла @x видно, что данный модуль не предназначен для публичного использования, а существует только для того, чтобы быть импортированным внутрь другого слайса.

Отмена запрета на кросс-импорты

Если Вы попробовали все способы в статье, но по каким-либо причинам всё равно не смогли избавиться от кросс-импортов, возможно, стоить задуматься о том, чтобы снять запрет на них в рамках конкретных слайсов, либо всего проекта в целом. Однако, важно понимать, что отмена запрета на кросс-импорты, потенциально может негативно сказываться на общей структуре проекта. 

  1. Команда Feature Sliced Design разработала инструмент, позволяющий автоматически проверить наличие кросс-импортов в проекте: Steiger 
  2. Больше информации о правиле кросс-импортов: Steiger-Forbidden-Imports 

См. также