Разработка масштабируемых фронтендов с помощью Feature-Sliced Design

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

Скорее всего, созданные в проекте файлы и компоненты настолько зависели друг от друга, что поддержка приложения становилась сплошным мучением!

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

Если хотите погрузиться в код, посмотрите Bit Scope.

Что такое Feature Sliced Design?

Feature Sliced Design  —  это паттерн фронтенд-архитектуры, который используется для построения фронтенд-приложений. Проще говоря, это сборник правил и соглашений для организации кода.

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

Это достигается путем разбиения фронтенд-приложения на три компонента:

При работе с Feature Sliced Design приложение включает три вида компонентов: Layers (слои), Slices (фрагменты) и Segments (сегменты).

1. Слои. Это компоненты, стандартизированные для всех проектов. Они организованы вертикально, то есть взаимодействие между ними может происходить только сверху вниз. Например, слой pages может взаимодействовать со слоем widgets, а не наоборот. В приложении может быть до 6 слоев.

  • Shared (абстракции)  —  многократно используемая функциональность, оторванная от специфики проекта/бизнеса (например, UIKit, libs, API).
  • Entities (сущности)  —  конкретные бизнес-сущности (например, пользователь, продукт, заказ).
  • Features (функции)  —  компоненты, определяющие взаимодействие пользователя с бизнес-логикой, действия, которые приносят ему бизнес-ценность (например, SendComment, AddToCart, UsersSearch).
  • Widgets (виджеты)  —  композиционный слой для объединения сущностей и функций в значимые блоки (например, IssuesList, UserProfile).
  • Pages (страницы)  —  композиционный слой для создания полноценных страниц из сущностей, функций и виджетов.
  • App (приложение)  —  слой настроек, стилей и провайдеров, общих для всего приложения.

2. Фрагменты. Каждый слой состоит из фрагментов. Фрагменты разделяют код внутри слоя по бизнес-домену. Это облегчает навигацию по коду и позволяет держать логически связанные модули рядом друг с другом. Однако важно отметить, что фрагменты не могут взаимодействовать внутри одного слоя  —  только с фрагментами слоя, расположенного ниже.

3. Сегменты. Каждый фрагмент состоит из сегментов. Сегмент  —  это крошечный модуль, который помогает разделить код внутри фрагмента в зависимости от его технического назначения. Например, могут быть разные сегменты для ui, api и lib. Каждый сегмент хранит код на основе различных технических целей.

Такое структурирование кода обеспечиваете следующие преимущества:

  1. Единообразие. Стандартизированная архитектура, основанная на разделении фронтенд-приложения на слои, фрагменты и сегменты, обязывает всех разработчиков придерживаться определенных правил и соглашений.
  2. Ориентация на домены. Приложение организовано таким образом, что в большей степени ориентировано на бизнес, а не на технологии. Это позволяет легче ориентироваться в проекте и глубже понимать его особенности.
  3. Повышение степени поддерживаемости. Поскольку модуль не может взаимодействовать с модулем на том же уровне или на уровне выше, приложение меньше подвержено сбоям после рефакторинга.

Стоит ли использовать Feature Sliced Design?

Очевидно, что Feature Sliced Design требует значительных усилий для внедрения во фронтенд-приложения независимо от того, начинаете ли вы с нуля или переходите на эту методологию.

Поэтому важно понимать, что Feature Sliced Design подходит не всем. Рекомендую использовать FSD в следующих случаях:

  1. Вы создаете фронтенд-приложение. Не пытайтесь смоделировать бэкенд-приложение, используя FSD.
  2. Вы создаете клиентское приложение, а не библиотеку пользовательского интерфейса. У библиотек пользовательского интерфейса нет ни бизнес-доменов, ни вызовов API, которые нужно обрабатывать. Только приложение, ориентированное на пользователя, может быть разделено доменами.
  3. Вы создаете крупномасштабный проект, а не простое приложение. Трудно оценить все преимущества FSD при создании простого приложения для выполнения задач. Но если вы создаете приложение вроде WriterGate или Medium, FSD может пригодиться.

Если фронтенд-приложение отвечает этим трем требованиям, переходите на FSD!

Создание приложения с использованием Feature Sliced Design и Bit

Если вы дошли до этого момента, значит, ваше приложение, скорее всего, является кандидатом на использование FSD. Итак, посмотрим, как создать приложение с помощью FSD!

Будем использовать Bit для создания приложения с помощью FSD. Bit  —  это система сборки нового поколения для компонируемого программного обеспечения.

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

Как видите, показанная выше область логично упорядочивает компоненты в различных пространствах имен  —  brand, elements и fonts. Используя эти пространства имен, можно легко упорядочить FSD с помощью Bit.

Итак, приступим к сборке.

Шаг 1. Предварительные условия

Сначала с помощью менеджера версий Bit (BVMM) установите Bit глобально, а затем инициализируйте рабочее пространство с React. Это позволит работать над FSD с пространством React.

# Установка Bit
npx @teambit/bvm install

# Инициалзиация рабочего пространства
bit new react workspace --default-scope dummyorg.fsd --aspect teambit.react/react-env

Обязательно замените dummyorg.fsd на ваше Bit Username (имя пользователя Bit) и Scope Name (имя области).

Обратите внимание: рабочие пространства не привязаны к языку. Используя Bit Environments, можно создавать различные компоненты, такие как Node.js, Angular, Next.js и React, в одном рабочем пространстве.

Если вы успешно создали рабочее пространство, должны увидеть результат, показанный ниже:

Теперь запустите bit start, чтобы запустить локальный сервер. Вы должны увидеть следующий вывод:

Шаг 2. Определение приложения React с помощью Feature Sliced Design

Приступим к созданию React-приложения на основе Feature Sliced Design, используя Bit Components. В данном примере будет показано, как создать React-приложение для получения списка блогов.

В этом приложении будет следующее:

  1. React App, содержащее все приложение.
  2. Страница Blog, на которой отображаются элементы блогов.
  3. Компонент Card, который отображает один элемент блога.
  4. Сущность, представляющая блог.
  5. API-вызов, который получает список блогов.

Если структурировать это в FSD, должно получиться следующее логическое отображение:

  1. app: в этом каталоге будет храниться компонент React app для blog-list (списка блогов).
  2. pages: в этом каталоге будет находиться компонент для blog-list-page (страницы со списком блогов).
  3. widgets: будет создан один фрагмент, определяющий blog-list (список блогов).
  4. features: в качестве первой функции определяется фрагмент под названием get-blog-posts, а в нем определим один сегмент model, определяющий механизмы получения данных для работы функции.
  5. entities: определяется фрагмент под названием blog, внутри которого будет два сегмента  —  model и ui. При этом model будет определять форму данных для Blog Item (элемента блога), а ui будет содержать компонент React под названием blog-card, определяющий карту статьи в блоге.

Шаг 3. Создание компонентов с помощью Bit

Далее создадим все необходимые компоненты с помощью Bit.

  1. Создание слоя Entity

Сначала создадим слой Entity. Для этого создадим фрагмент blog с помощью команды:

bit create react-hook entities/blog/model && bit create react entities/blog/ui/blog-item

Вы должны увидеть новые каталоги, как показано ниже:

Работая с Bit, вы наверняка заметили, что у вас есть файл spec.tsx, а также файл composition.tsx. Вы можете осуществлять сборку с помощью Test Driven Development (разработки через тестирование), создавая тест-кейсы для отдельного компонента. Кроме того, можете использовать файл compositions для создания различных выводов для компонента, чтобы показать потребителям, как его можно использовать.

Для простоты я не буду писать тест-кейсы, поэтому удалю тестовые файлы из своих компонентов.

Теперь определим Blog model и пользовательский интерфейс Blog Card, обновив файлы model.ts и blog-item.tsx, как показано ниже:

// blog-item.tsx

import type { ReactNode } from 'react';
export type BlogItemProps = {
id: string,
title: string,
description: string
tags: string[]
};
const cardStyle: React.CSSProperties = {
border: '1px solid #ddd',
borderRadius: '8px',
padding: '16px',
margin: '16px',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
backgroundColor: '#fff',
};
const titleStyle: React.CSSProperties = {
fontSize: '1.5rem',
marginBottom: '8px',
};
const tagsStyle: React.CSSProperties = {
marginTop: '8px',
color: '#555',
};
export function BlogItem({ description, id, title, tags }: BlogItemProps) {
return (
<div className="blog-card"
key={id}
style={cardStyle}>
<h2 style={titleStyle}>{title}</h2>
<p>{description}</p>
<div className="tags" style={tagsStyle}>
<strong>Tags:</strong> {tags.join(', ')}
</div>
</div>
);
}

Как видите, мы определили Blog entity и Blog Card, которые можем использовать для создания функции просмотра блогов. Чтобы увидеть полную реализацию, посмотрите эти компоненты на Bit Cloud.

А вот как должен выглядеть локальный сервер после этого:

2. Создание слоя features

Создадим функцию get-blog-posts. Для этого понадобится один сегмент:

  1. model: определим хук, который будет получать данные.

Для этого создадим React-компоненты:

bit create react-hook features/get-blog-posts/model

После этого вы должны увидеть результат, показанный ниже:

Откройте файл model.ts и включите в него приведенный ниже код для получения информации о блогах:

import { useBlogStore } from '@dummyorg/fsd.entities.blog.model';
import { useEffect } from 'react';

export function useGetBlogs() {
const { blogs, getBlogs, loading } = useBlogStore();
useEffect(() => {
getBlogs();
});
return { blogs, loading };
}

3. Создание слоя widgets

Теперь создадим виджет blog-list, который будет содержать коллекцию блогов. Создадим фрагмент под названием blog-list и сегмент ui внутри этого фрагмента. Это можно сделать с помощью команды:

bit create react widgets/blog-list/ui

Вы увидите, что добавился новый каталог:

Откройте файл ui.tsx и добавьте в него приведенный ниже код:

import React from 'react';
import { Blog } from '@dummyorg/fsd.entities.blog.model';
import { BlogItem } from '@dummyorg/fsd.entities.blog.ui.blog-item';

export type UiProps = {
blogs: Blog[]
};
export function Ui({ blogs = [] }: UiProps) {
return blogs.map((blog) => (<BlogItem
key={blog.id}
description={blog.description}
id={blog.id}
tags={blog.tags}
title={blog.title}
/>))
}

Перейдя на свой локальный сервер, увидите результат:

Можете изучить полную реализацию на Bit Cloud.

4. Создание слоя pages

Далее давайте создадим страницу, на которой будет храниться список блогов. Для этого создадим фрагмент blog-list и сегмент ui с помощью команды:

bit create react pages/blog-list/ui

Это приведет к следующему результату:

Затем откройте файл ui.tsx и включите в него сниппет:

import React from 'react';
import { useGetBlogs } from '@dummyorg/fsd.features.get-blog-posts.model';
import { BlogList } from '@dummyorg/fsd.widgets.blog-list.ui';

export function Ui() {
const { blogs, loading } = useGetBlogs();
if (loading) {
return <p>
Posts are loading...
</p>
}
return (
<BlogList
blogs={blogs}
/>
);
}

На локальном сервере вы должны увидеть результат, показанный ниже:

Вот так мы создали слой pages! Чтобы изучить все это в деталях, перейдите на Bit Cloud.

5. Создание слоя app

Наконец, создадим слой app для использования всего вышеперечисленного. Выполните следующую команду:

bit create react-app app

Это создаст компонент app и сгенерирует вывод:

Чтобы сделать это приложение загружаемым вне сервера Bit, выполните команду:

bit use app

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

bit app list

Если приложение можно загружать, вы должны увидеть результат:

Теперь откройте файл app.tsx и включите в него сниппет:

import React from "react";
import { BlogListPage } from "@dummyorg/fsd.pages.blog-list.ui";

export function App() {
return <BlogListPage />
}

Затем запустите приложение, выполнив команду:

bit run app

Вы должны увидеть приложение, запущенное на localhost, с таким результатом:

Полную реализацию приложения можно увидеть на Bit Cloud.

Подведение итогов

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

Если вам интересно ознакомиться с приложением, которое мы создали, посмотрите его на Bit Cloud.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Lakindu Hewawasam: Developing Scalable Frontends with Feature-Sliced Design (FSD)

Предыдущая статьяСовместное использование состояний между окнами без задействования сервера
Следующая статьяЛучшие практики API-авторизации