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

Слои

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

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

Дерево файловой системы с одной корневой папкой под названием src и семью подпапками: app, processes, pages, widgets, features, entities, shared. Папка processes слегка выцвечена. Дерево файловой системы с одной корневой папкой под названием src и семью подпапками: app, processes, pages, widgets, features, entities, shared. Папка processes слегка выцвечена.
  1. App (Эпп)
  2. Processes (Процессы, устаревший слой)
  3. Pages (Страницы)
  4. Widgets (Виджеты)
  5. Features (Фичи/функции)
  6. Entities (Сущности)
  7. Shared (Шэред)

Вы не обязаны использовать все слои в своем проекте - добавляйте только те, что приносят пользу вашему проекту. Как правило, в большинстве фронтенд-проектов будут как минимум слои Shared, Pages и App.

На практике слои представляют собой папки с названиями в нижнем регистре (например, 📁 shared, 📁 pages, 📁 app). Добавление новых слоев не рекомендуется, так как их семантика стандартизирована.

Правило импорта слоев

Слои состоят из слайсов — высокосвязанных групп модулей. Зависимости между слайсами регулируются правилом импорта слоев:

Модуль (файл) в слайсе может импортировать другие слайсы только если они находятся на слоях строго ниже.

Например, папка 📁 ~/features/aaa является слайсом с именем "aaa". Файл внутри нее, ~/features/aaa/api/request.ts, не может импортировать код из любого файла в 📁 ~/features/bbb, но может импортировать код из 📁 ~/entities и 📁 ~/shared, а также любой код из 📁 ~/features/aaa, например, ~/features/aaa/lib/cache.ts.

Слои App и Shared являются исключениями из этого правила — они одновременно являются и слоем, и слайсом. Слайсы разделяют код по бизнес-доменам, и эти два слоя являются исключениями, потому что в Shared нет бизнес-доменов, а App объединяет все бизнес-домены.

На практике это означает, что слои App и Shared состоят из сегментов, и сегменты могут свободно импортировать друг друга.

Определения слоёв

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

Shared

Этот слой формирует фундамент для остальной части приложения. Это место для создания связей с внешним миром, например, бэкенды, сторонние библиотеки, среда выполнения приложения (environment). Также это место для ваших собственных библиотек, сконцентрированных на конкретной задаче.

Этот слой, как и слой App, не содержит слайсов. Слайсы предназначены для разделения слоя на предметные области, но предметные области не существуют в Shared. Это означает, что все файлы в Shared могут ссылаться и импортировать друг друга.

Вот сегменты, которые вы обычно можете найти в этом слое:

  • 📁 api — API-клиент и, возможно, функции для выполнения запросов к конкретным эндпоинтам бэкенда.
  • 📁 ui — UI-кит приложения.
    Компоненты на этом слое не должны содержать бизнес-логику, но могут быть тематически связаны с бизнесом. Например, здесь можно разместить логотип компании и макет страницы. Компоненты с UI-логикой также допустимы (например, автозаполнение или строка поиска).
  • 📁 lib — коллекция внутренних библиотек.
    Эта папка не должна рассматриваться как хелперы или утилиты (прочитайте здесь, почему эти папки часто превращаются в свалку). Вместо этого каждая библиотека в этой папке должна иметь одну область фокуса, например, даты, цвета, манипуляции с текстом и т.д. Эта область фокуса должна быть задокументирована в файле README. Разработчики в вашей команде должны знать, что можно и что нельзя добавлять в эти библиотеки.
  • 📁 config — переменные окружения, глобальные фиче-флаги и другая глобальная конфигурация для вашего приложения.
  • 📁 routes — константы маршрутов или шаблоны для сопоставления маршрутов.
  • 📁 i18n — код, настраивающий систему переводов, а также глобальные строки перевода.

Вы можете добавлять ещё сегментов, но убедитесь, что название этих сегментов описывает цель содержимого, а не его суть. Например, components, hooks и types — плохие имена сегментов, поскольку они не очень полезны при поиске кода.

Entities

Слайсы на этом слое представляют концепции из реального мира, с которыми работает проект. Обычно это термины, которые бизнес использует для описания продукта. Например, социальная сеть может работать с бизнес-сущностями, такими как Пользователь, Публикация и Группа.

Слайс сущности может содержать хранилище данных (📁 model), схемы валидации данных (📁 model), функции запросов API, связанные с сущностями (📁 api), а также визуальное представление этой сущности в интерфейсе (📁 ui). Это визуальное представление не обязательно должно создавать полный блок пользовательского интерфейса — оно в первую очередь предназначено для переиспользования одного и того же внешнего вида на нескольких страницах приложения, и к нему может быть присоединена различная бизнес-логика через пропы или слоты.

Связи между сущностями

Сущности в FSD являются слайсами, и по умолчанию слайсы не могут знать друг о друге. Однако в реальной жизни сущности часто взаимодействуют друг с другом, и иногда одна сущность владеет или содержит другие сущности. Из-за этого бизнес-логику этих взаимодействий лучше всего хранить на более высоких уровнях, таких как Features или Pages.

Когда объект данных одной сущности содержит другие объекты данных, обычно хорошей идеей является сделать связь между сущностями явной и обойти изоляцию слайсов, создав API для кросс-ссылок через @x-нотацию. Причина в том, что связанные сущности должны рефакториться вместе, поэтому лучше сделать так, чтоб связь было невозможно не заметить.

Например:

entities/artist/model/artist.ts
import type { Song } from "entities/song/@x/artist";

export interface Artist {
name: string;
songs: Array<Song>;
}
entities/song/@x/artist.ts
export type { Song } from "../model/song.ts";

Вы можете узнать больше о @x-нотации в разделе Публичный API для кросс-импортов.

Features

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

Важный принцип эффективного использования слоя Features: не все должно быть фичей. Хорошим показателем того, что что-то должно быть фичей, является тот факт, что оно переиспользуется. Например, если в приложении есть несколько редакторов, и у всех них есть комментарии, то комментарии являются переиспользуемой фичей. Помните, что слайсы — это механизм для быстрого поиска кода, и если фич слишком много, важные фичи теряются.

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

Слайс фичи может содержать UI для выполнения действия, например, форму (📁 ui), вызовы API, необходимые для выполнения действия (📁 api), валидацию и внутреннее состояние (📁 model), фиче-флаги (📁 config).

Widgets

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

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

подсказка

Если вы используете систему вложенного роутинга (например, роутер Remix), может быть полезно использовать слой Widgets так же, как плоская система роутинга использует слой Pages — для создания полных блоков роутинга, включая связанные запросы данных, состояния загрузки и границы ошибок.

Таким же образом, вы можете хранить лейауты страниц на этом слое.

Pages

Страницы — это то, из чего состоят веб-сайты и приложения (также называются "экраны" или "активности"). Одна страница обычно соответствует одному слайсу, однако, если есть несколько очень похожих страниц, их можно сгруппировать в один слайс, например, формы регистрации и входа.

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

В слайсе страницы вы обычно найдете интерфейс страницы, а также состояния загрузки и границы ошибок (📁 ui). Также вы можете найти там запросы на получение и изменение данных (📁 api). Обычно у страницы нет выделенной модели данных, и небольшие части состояния могут храниться в самих компонентах.

Processes

предупреждение

Этот слой считается устаревшим. Текущая версия спецификации рекомендует избегать его и перемещать его содержимое в features и app.

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

Этот уровень намеренно оставлен не очень определенным. Большинству приложений этот слой не пригодится, логику на уровне роутера и сервера следует оставить на уровне App. Используйте этот слой только тогда, когда слой App вырастет настолько, что станет неподдерживаемым и потребует разгрузки.

App

Всё, что касается приложения целиком, как в техническом смысле (например, провайдеры контекста), так и в бизнес-смысле (например, аналитика).

Этот слой обычно не содержит слайсов, как и Shared, и вместо этого внутри него сразу находятся сегменты.

Вот сегменты, которые вы обычно можете найти в этом слое:

  • 📁 routes — конфигурация роутера
  • 📁 store — глобальная конфигурация хранилища
  • 📁 styles — глобальные стили
  • 📁 entrypoint — точка входа в код приложения, специфичная для вашего фреймворка