Feature-Sliced Design

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

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

Структура проекта по FSD

Основная идея FSD — разделение проекта на несколько “срезов”, каждый из которых представляет собой отдельную функциональную единицу. Это разделение можно применить к любой крупной системе, включая фронтенд-приложения на Solid.js. Структура проекта может быть организована следующим образом:

  • app — корневой слой, содержащий общую логику приложения, маршрутизацию и глобальные состояния.
  • features — слои, содержащие отдельные фичи приложения, например, работу с пользователями, обработку платежей или добавление товара в корзину.
  • entities — слои, описывающие сущности, такие как пользователь, продукт, заказ, и все связанные с ними бизнес-правила.
  • shared — общие утилиты, компоненты, хуки и другие элементы, используемые в разных частях приложения.
  • pages — представления, которые отображаются пользователю и соответствуют конкретным маршрутам.

Преимущества FSD в Solid.js

1. Логическая изоляция фич

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

2. Масштабируемость

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

3. Повторное использование кода

Важной частью FSD является разделение на слои, где каждая функциональность инкапсулируется в своем модуле. Компоненты, хуки и утилиты, которые могут быть использованы в разных частях приложения, помещаются в слой shared. Это позволяет избежать дублирования кода и повысить его повторное использование.

4. Улучшенная тестируемость

Каждый срез, фича или сущность в Solid.js можно протестировать независимо от других частей системы. В FSD легко выделить отдельные компоненты и провести их юнит-тестирование. Это значительно упрощает процессы QA и автоматического тестирования, ускоряя доставку качественного продукта.

5. Снижение связности и повышение модульности

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

Реализация FSD в Solid.js

При реализации Feature-Sliced Design в Solid.js необходимо учитывать несколько важных аспектов, таких как структура состояний, маршрутизация и работа с реактивностью.

Разделение на фичи

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

/src
  /features
    /cart
      index.tsx
      cartStore.ts
      cartActions.ts
      cartItems.tsx

В этом примере:

  • index.tsx — основной компонент корзины.
  • cartStore.ts — содержит состояние корзины.
  • cartActions.ts — функции для добавления/удаления товаров.
  • cartItems.tsx — отображение товаров в корзине.

Использование реактивности в Solid.js

Solid.js предоставляет мощную реактивную систему, которая позволяет эффективно управлять состоянием. Например, для управления состоянием корзины можно использовать createSignal для хранения списка товаров и их количества:

import { createSignal } from 'solid-js';

const [cartItems, setCartItems] = createSignal([]);

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

export function useCart() {
  const [cartItems, setCartItems] = createSignal([]);
  
  const addItem = (item) => {
    setCartItems([...cartItems(), item]);
  };
  
  return { cartItems, addItem };
}

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

Структура страниц

В Solid.js маршруты можно организовать с помощью библиотеки solid-app-router. Каждая страница может быть реализована как отдельный компонент в слое pages, что позволяет гибко управлять маршрутами:

/src
  /pages
    /home
      HomePage.tsx
    /cart
      CartPage.tsx

Компоненты страниц подключаются в файле маршрутизации:

import { Routes, Route } from 'solid-app-router';
import HomePage from './pages/home/HomePage';
import CartPage from './pages/cart/CartPage';

function App() {
  return (
    <Routes>
      <Route path="/" component={HomePage} />
      <Route path="/cart" component={CartPage} />
    </Routes>
  );
}

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

Общие компоненты

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

// shared/Button.tsx
import { JSX } from 'solid-js';

export default function Button(props: JSX.IntrinsicElements['button']) {
  return <button {...props} />;
}

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

Организация состояний в FSD

Состояния в Solid.js могут быть разделены по срезам, каждый из которых отвечает за свою часть данных. Важно помнить, что состояние должно быть локализовано в соответствующем срезе. Для этого используются реактивные хранилища (например, createSignal, createStore) или глобальные менеджеры состояний, такие как store для более сложных сценариев.

Пример организации состояний в приложении

Рассмотрим проект, в котором есть фичи “пользователь”, “товары” и “корзина”. Состояние для каждого из этих слоев может быть изолировано и управляться отдельно:

// /features/user/userStore.ts
import { createSignal } from 'solid-js';

const [user, setUser] = createSignal(null);

export function useUser() {
  return { user, setUser };
}

// /features/products/productsStore.ts
import { createSignal } from 'solid-js';

const [products, setProducts] = createSignal([]);

export function useProducts() {
  return { products, setProducts };
}

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

Заключение

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