FSD для Svelte с использованием библиотеки svelte-fsd

05 января 2026
Автор

Олег Марков

Введение

Feature Sliced Design (FSD) за последние годы стал одним из самых популярных подходов к архитектуре фронтенда. Он помогает упорядочить код, уменьшить связность между модулями, упростить поддержку и развитие проекта. Если вы уже работали с FSD в React, то, скорее всего, видели множество реализаций на базе React и Redux. Но со Svelte все немного иначе, и здесь на помощь приходит библиотека svelte-fsd.

Задача svelte-fsd — не «привязать» вас к жесткому фреймворку, а дать удобные соглашения, вспомогательные функции и структуры, которые упрощают применение FSD в Svelte-приложениях. Смотрите, я покажу вам, как на практике использовать этот подход: от структуры каталогов до конкретных примеров кода.

В статье вы разберете:

  • какие уровни и сущности выделяются в FSD для Svelte;
  • как svelte-fsd помогает организовывать импорт и изоляцию слоев;
  • как структурировать страницы, виджеты, фичи и сущности в Svelte;
  • как использовать сторы, компоненты и контракты между слоями;
  • как избежать типичных ошибок при внедрении FSD в существующий проект.

Базовая идея FSD в контексте Svelte

Что такое FSD в общем виде

FSD (Feature Sliced Design) — это архитектурный подход, в котором приложение делится не на слои по технологиям (components, services, utils), а на слои и срезы по домену:

  • слои (layers) — крупные зоны ответственности;
  • срезы (slices) — конкретные доменные области внутри слоев;
  • сегменты (segments) — типы модулей внутри среза (ui, model, lib и т.д.).

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

Как это ложится на Svelte

Svelte сам по себе сильно отличатся от React: нет JSX, компоненты компилируются, сторы встроены. Поэтому реализация FSD для Svelte учитывает особенности:

  • нет привычных хуков вроде useEffect, вместо этого — реактивные декларации и сторы;
  • маршрутизация обычно реализуется внешними библиотеками или самостоятельно;
  • стили часто пишут прямо в компоненте, но можно использовать SCSS, Tailwind и т.д.

Библиотека svelte-fsd не навязывает конкретный роутер или стор-менеджер, а помогает:

  • выстроить структуру папок;
  • унифицировать точки входа в слои;
  • стандартизировать импорт между слоями;
  • использовать вспомогательные функции и типы (если вы с TypeScript).

Структура проекта по FSD с svelte-fsd

Давайте разберемся на примере базовой структуры приложения на Svelte с применением FSD и svelte-fsd.

Базовая структура слоев

Обычно структура на уровне src может выглядеть так:

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

Один из типичных вариантов для Svelte:

  • src
    • app
      • index.svelte
      • providers
      • routing
    • pages
    • widgets
    • features
    • entities
    • shared

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

Срезы и сегменты

Внутри каждого слоя вы создаете срезы (slices). Например:

  • entities

    • user
    • product
  • features

    • auth
    • add-to-cart

Каждый срез, в свою очередь, делится на сегменты. Чаще всего используют:

  • ui — компоненты;
  • model — сторы, бизнес-логика;
  • lib — вспомогательные функции;
  • config — конфиги (если нужно);
  • api — запросы к backend (иногда выносят в shared/api).

Для примера возьмем сущность user:

  • src
    • entities
      • user
        • ui
        • model
        • lib

Теперь вы увидите, как это выглядит в коде с точки зрения svelte-fsd.

Использование svelte-fsd на практике

Установка и базовая настройка

Предположим, у вас уже есть проект на Svelte (например, созданный через SvelteKit или Vite + Svelte).

Установка библиотеки (псевдокоманда, название может отличаться, ориентируйтесь на документацию репозитория svelte-fsd):

# Устанавливаем библиотеку svelte-fsd
npm install svelte-fsd

Чаще всего далее вам нужно:

  1. Настроить alias-ы в проекте (чтобы импортировать слои по понятным путям).
  2. Определить базовые правила слоёв и использовать вспомогательные функции библиотеки.

Пример настройки alias в Vite + Svelte (файл vite.config.js):

import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import path from 'path';

export default defineConfig({
  plugins: [svelte()],
  resolve: {
    alias: {
      // Уровень приложения
      '@app': path.resolve(__dirname, 'src/app'),
      // Страницы
      '@pages': path.resolve(__dirname, 'src/pages'),
      // Виджеты
      '@widgets': path.resolve(__dirname, 'src/widgets'),
      // Фичи
      '@features': path.resolve(__dirname, 'src/features'),
      // Сущности
      '@entities': path.resolve(__dirname, 'src/entities'),
      // Общие модули
      '@shared': path.resolve(__dirname, 'src/shared'),
    },
  },
});

// Здесь мы задаем алиасы для каждого слоя // Это упростит импорт и сделает архитектуру более прозрачной

Теперь в коде вы сможете писать:

// Импорт компонента сущности user по FSD-алису
import UserCard from '@entities/user/ui/UserCard.svelte';

Индексные файлы как контракты слоев

Важная практика FSD — не открывать весь срез наружу, а явно определять публичный API среза через index-файлы. Svelte-fsd поддерживает этот подход.

Пример для сущности user:

Структура:

  • src
    • entities
      • user
        • ui
          • UserCard.svelte
        • model
          • store.ts
        • index.ts

Файл src/entities/user/index.ts:

// Публичный API сущности user

// Экспортируем UI-компонент карточки пользователя
export { default as UserCard } from './ui/UserCard.svelte';

// Экспортируем стор с данными пользователя
export { userStore } from './model/store';

// Можно экспортировать типы, если используется TypeScript
export type { User } from './model/types';

// Здесь мы объявляем, что из сущности user "видят" остальные слои // Внутренние детали (другие компоненты, вспомогательные функции) остаются скрыты

Теперь, чтобы использовать сущность user в другом месте:

// Импортируем только то, что явно разрешено публичным API
import { UserCard, userStore } from '@entities/user';

Svelte-fsd рекомендует всегда работать только через такие индексные файлы. Это помогает избежать хаоса в импортируемых модулях и облегчает рефакторинг.

Пример: сущность User с Svelte-сторами

Давайте посмотрим, что происходит в следующем примере. Создадим простую сущность user, которая хранит текущего пользователя и рендерит карточку.

Модель: сторы и бизнес-логика

Файл src/entities/user/model/store.ts:

import { writable, derived } from 'svelte/store';

// Тип пользователя
// В реальном проекте структура может быть сложнее
export type User = {
  id: string;
  name: string;
  email: string;
};

// Создаем writable-стор для текущего пользователя
// null означает, что пользователь не авторизован
const currentUser = writable<User | null>(null);

// Экспортируем функции для управления стором
function setUser(user: User) {
  // Устанавливаем текущего пользователя
  currentUser.set(user);
}

function clearUser() {
  // Сбрасываем текущего пользователя
  currentUser.set(null);
}

// Создаем derived-стор для удобства
// isAuth будет true, если пользователь авторизован
const isAuth = derived(currentUser, ($currentUser) => !!$currentUser);

// Экспортируем объект userStore как публичный API модели
export const userStore = {
  subscribe: currentUser.subscribe, // Позволяет подписываться на изменения
  setUser,
  clearUser,
  isAuth,
};

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

UI: компонент карточки пользователя

Файл src/entities/user/ui/UserCard.svelte:

<script lang="ts">
  import type { User } from '../model/store';

  // Принимаем пользователя через проп
  // Это позволяет использовать компонент как "глупый" компонент без привязки к сторам
  export let user: User | null = null;
</script>

{#if user}
  <!-- Отображаем данные пользователя, если он есть -->
  <div class="user-card">
    <h3>{user.name}</h3>
    <p>{user.email}</p>
  </div>
{:else}
  <!-- Если пользователя нет, выводим заглушку -->
  <div class="user-card user-card--empty">
    <p>Пользователь не авторизован</p>
  </div>
{/if}

<style>
  /* Простейшие стили для иллюстрации */

  .user-card {
    border: 1px solid #ccc; /* Простая рамка вокруг карточки */
    padding: 8px 12px;      /* Внутренние отступы */
    border-radius: 4px;     /* Скругляем углы */
  }

  .user-card--empty {
    opacity: 0.6;           /* Делаем заглушку визуально менее заметной */
  }
</style>

// В этом компоненте нет прямой работы со стором // Это упрощает тестирование и повторное использование

Далее вы можете создать отдельный «обвязочный» компонент (например, в features или widgets), который подключит userStore и передаст данные в UserCard.

Пример: фича авторизации (feature auth)

Теперь давайте перейдем к следующему шагу и посмотрим, как организовать фичу авторизации на уровне features.

Структура фичи auth

Предположим, нам нужна фича, которая:

  • показывает форму логина;
  • вызывает API;
  • при успехе обновляет userStore.

Структура:

  • src
    • features
      • auth
        • ui
          • LoginForm.svelte
        • model
          • login.ts
        • api
          • authApi.ts
        • index.ts

API-слой фичи

Файл src/features/auth/api/authApi.ts:

import type { User } from '@entities/user/model/store';

// Тип данных для логина
type LoginPayload = {
  email: string;
  password: string;
};

// Пример функции авторизации
// В реальном приложении здесь будет fetch к backend
export async function loginRequest(
  payload: LoginPayload
): Promise<User> {
  // Здесь мы имитируем запрос на сервер
  // Обычно вы будете вызывать fetch и обрабатывать ответ
  await new Promise((resolve) => setTimeout(resolve, 500)); // Задержка 500 мс

  // Возвращаем "фейкового" пользователя
  return {
    id: '1',
    name: 'Demo User',
    email: payload.email,
  };
}

// authApi.ts инкапсулирует всю работу с сервером для фичи auth // Это упрощает замену реализации (например, при смене backend)

Модель фичи auth

Файл src/features/auth/model/login.ts:

import { writable } from 'svelte/store';
import { loginRequest } from '../api/authApi';
import { userStore } from '@entities/user';

// Стор для состояния загрузки
const isLoading = writable(false);

// Стор для хранения ошибки
const error = writable<string | null>(null);

// Функция для выполнения логина
export async function login(email: string, password: string) {
  // При начале логина очищаем ошибку и включаем состояние загрузки
  error.set(null);
  isLoading.set(true);

  try {
    // Делаем запрос к API
    const user = await loginRequest({ email, password });

    // Если запрос успешен, обновляем userStore
    userStore.setUser(user);
  } catch (e) {
    // Обрабатываем ошибку, в реальном коде уточняйте типы и сообщения
    error.set('Ошибка авторизации'); // Сообщение можно сделать более подробным
  } finally {
    // В любом случае после завершения запроса выключаем состояние загрузки
    isLoading.set(false);
  }
}

// Экспортируем модель в виде объекта
export const loginModel = {
  isLoading,
  error,
  login,
};

// Здесь фича auth напрямую работает с сущностью user // Это нормально, потому что features могут зависеть от entities

UI фичи auth

Файл src/features/auth/ui/LoginForm.svelte:

<script lang="ts">
  import { loginModel } from '../model/login';
  import { get } from 'svelte/store';

  let email = '';    // Локальное состояние для email
  let password = ''; // Локальное состояние для пароля

  // Подписываемся на сторы через $-синтаксис
  $: isLoading = $loginModel.isLoading;
  $: error = $loginModel.error;

  async function handleSubmit() {
    // Вызываем login и передаем данные формы
    await loginModel.login(email, password);

    // Здесь можно добавить редирект после успешного логина
    // Например, через роутер или события
  }
</script>

<form on:submit|preventDefault={handleSubmit}>
  <!-- Поле для ввода email -->
  <input
    type="email"
    bind:value={email}
    placeholder="Email"
    required
  />

  <!-- Поле для ввода пароля -->
  <input
    type="password"
    bind:value={password}
    placeholder="Пароль"
    required
  />

  <!-- Кнопка отправки формы -->
  <button type="submit" disabled={isLoading}>
    {#if isLoading}
      Входим...
    {:else}
      Войти
    {/if}
  </button>

  {#if error}
    <!-- Отображаем ошибку, если она есть -->
    <p class="error">{error}</p>
  {/if}
</form>

<style>
  /* Небольшие стили для наглядности */

  form {
    display: flex;        /* Располагаем элементы по вертикали */
    flex-direction: column;
    gap: 8px;             /* Расстояние между элементами формы */
    max-width: 240px;     /* Ограничиваем ширину формы */
  }

  .error {
    color: red;           /* Красный цвет для сообщения об ошибке */
    font-size: 0.9rem;
  }
</style>

// UI-компонент работает только с loginModel // Внешний код просто использует LoginForm как часть интерфейса

Публичный API фичи auth

Файл src/features/auth/index.ts:

// Публичный API фичи авторизации

// Экспортируем UI-форму логина
export { default as LoginForm } from './ui/LoginForm.svelte';

// Экспортируем модель для продвинутых кейсов
export { loginModel } from './model/login';

// В большинстве случаев снаружи будет использоваться только LoginForm // Но при необходимости можно работать с loginModel напрямую

Виджеты и страницы: сборка интерфейса

Теперь давайте посмотрим, как собрать все вместе на уровне widgets и pages.

Виджет: Header с пользователем и формой логина

Представим, что у нас есть Header, который:

  • показывает информацию о пользователе;
  • если пользователь не авторизован — показывает кнопку «Войти» и форму.

Структура:

  • src
    • widgets
      • header
        • ui
          • Header.svelte
        • index.ts

Файл src/widgets/header/ui/Header.svelte:

<script lang="ts">
  import { userStore } from '@entities/user';
  import { LoginForm } from '@features/auth';

  // Подписываемся на userStore
  $: isAuth = $userStore.isAuth;
  $: currentUser = $userStore; // Здесь мы получаем объект стора userStore
</script>

<header class="header">
  <h1>Мое Svelte приложение</h1>

  {#if isAuth}
    <!-- Если пользователь авторизован, показываем карточку -->
    <div class="header__user">
      <!-- Здесь вы можете использовать компонент UserCard -->
      <span>Вы вошли как {currentUser?.name}</span>
    </div>
  {:else}
    <!-- Если пользователь не авторизован, показываем форму логина -->
    <div class="header__login">
      <LoginForm />
    </div>
  {/if}
</header>

<style>
  .header {
    display: flex;               /* Располагаем элементы в одну строку */
    justify-content: space-between;
    align-items: center;
    padding: 12px 16px;          /* Внутренние отступы */
    border-bottom: 1px solid #ddd;
  }

  .header__user,
  .header__login {
    display: flex;
    align-items: center;
    gap: 8px;
  }
</style>

// Header использует сущность user и фичу auth // Такой виджет можно переиспользовать на разных страницах

Файл src/widgets/header/index.ts:

// Публичный API виджета header
export { default as Header } from './ui/Header.svelte';

Страница: Главная

На уровне страниц мы только собираем уже готовые блоки (виджеты, фичи, сущности).

Структура:

  • src
    • pages
      • home
        • ui
          • HomePage.svelte
        • index.ts

Файл src/pages/home/ui/HomePage.svelte:

<script lang="ts">
  import { Header } from '@widgets/header';
  import { UserCard, userStore } from '@entities/user';

  // Подписываемся на userStore для отображения карточки пользователя
  $: currentUser = $userStore;
</script>

<Header />

<main class="page">
  <h2>Главная страница</h2>

  <!-- Отображаем карточку пользователя -->
  <section>
    <h3>Ваш профиль</h3>
    <UserCard user={currentUser} />
  </section>
</main>

<style>
  .page {
    padding: 16px; /* Отступы для главного содержимого страницы */
  }
</style>

// Страница не содержит сложной логики // Она только комбинирует уже подготовленные виджеты, фичи и сущности

Файл src/pages/home/index.ts:

// Публичный API страницы home
export { default as HomePage } from './ui/HomePage.svelte';

Уровень app: корневой компонент и роутинг

Svelte-fsd рекомендует держать все, что связано с корнем приложения, в слое app.

Структура:

  • src
    • app
      • index.svelte
      • providers
      • routing

В простейшем случае, если у вас одна страница, app/index.svelte может выглядеть так:

<script lang="ts">
  import { HomePage } from '@pages/home';
</script>

<!-- Здесь мы просто рендерим главную страницу -->
<HomePage />

// В реальном проекте здесь могут быть провайдеры темизации, i18n и роутер // Все это остается в слое app и не выносится в shared

Если вы используете роутер, его конфигурация также будет в app/routing, а каждая страница останется в своем срезе pages.

Правила зависимостей между слоями

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

  • app может зависеть от всех слоев;
  • pages зависят от widgets, features, entities, shared;
  • widgets зависят от features, entities, shared;
  • features зависят от entities, shared;
  • entities зависят от shared;
  • shared не зависит от других слоев.

Давайте зафиксируем это в виде «лестницы»:

  • app
    • pages
      • widgets
        • features
          • entities
            • shared

Svelte-fsd часто предоставляет утилиты для валидации импортов (через ESLint или собственные скрипты). Идея в том, чтобы никакой модуль из нижнего слоя не импортировал верхний. Например:

  • features не может импортировать pages;
  • entities не может импортировать features и widgets;
  • shared вообще не должен знать о существовании других слоев.

Это правило защищает архитектуру от «разрастания» связей и нарушений изоляции.

Использование общих модулей (shared)

Уровень shared — это место для всего, что не относится к конкретной доменной области. Чаще всего сюда помещают:

  • дизайн-систему (UI-кит, кнопки, инпуты);
  • общие утилиты и хелперы;
  • типы, которые используются во многих местах;
  • API-клиент, настройки запросов;
  • конфиги.

Пример: общий UI-кит кнопок.

Структура:

  • src
    • shared
      • ui
        • Button.svelte
        • Input.svelte
      • lib
        • formatDate.ts

Файл src/shared/ui/Button.svelte:

<script lang="ts">
  // Тип для варианта кнопки
  export let variant: 'primary' | 'secondary' = 'primary';
  export let disabled = false; // Флаг отключения кнопки
</script>

<button
  class={`btn btn--${variant}`}
  disabled={disabled}
>
  <slot />
</button>

<style>
  .btn {
    padding: 8px 12px;          /* Внутренние отступы */
    border-radius: 4px;         /* Скругление углов */
    border: none;               /* Убираем стандартную рамку */
    cursor: pointer;            /* Курсор в виде руки */
  }

  .btn--primary {
    background-color: #007bff;  /* Синий фон */
    color: white;               /* Белый текст */
  }

  .btn--secondary {
    background-color: #f0f0f0;  /* Светло-серый фон */
    color: #333;                /* Темный текст */
  }

  .btn:disabled {
    opacity: 0.6;               /* Делаем отключенную кнопку полупрозрачной */
    cursor: not-allowed;        /* Запрещаем клик визуально */
  }
</style>

// Такой компонент можно использовать в любом слое // Главное — чтобы зависимости были только "вниз" по слоям от места использования

Интеграция FSD в существующий Svelte-проект

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

Шаг 1. Вводим слои и alias-ы

Сначала вы:

  1. Создаете в src папки app, pages, widgets, features, entities, shared.
  2. Настраиваете alias-ы (как мы делали выше в Vite).

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

Шаг 2. Новый функционал — только по FSD

Дальше вы принимаете правило:

  • весь новый код создается сразу в структуре FSD;
  • старый код переносится постепенно по мере доработок.

Например, если вы добавляете новую фичу, сделайте ее в src/features/<название> вместо src/components.

Шаг 3. Перенос сущностей и общего кода

Постепенно:

  • выделяете сущности (user, product, order) и переносите их в entities;
  • выносите общие компоненты и утилиты в shared;
  • делите крупные компоненты страниц на виджеты и фичи.

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

Шаг 4. Валидация импортов

Когда структура уже более-менее устойчива, можно подключить правила линтинга, чтобы:

  • запретить прямые импорты между неправильными слоями;
  • заставить использовать index-файлы как публичный API.

Большинство таких проверок настраивается через ESLint и плагины, которые идут вместе с svelte-fsd (или описаны в его документации).

Типичные ошибки при использовании FSD в Svelte

Слишком ранняя детализация

Иногда разработчики создают слишком много слоев и папок сразу. Например:

  • разделяют почти каждый компонент на отдельную фичу;
  • делают много «пустых» срезов.

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

Отсутствие index-файлов

Без index-файлов разработчики начинают импортировать все подряд:

  • из конкретных файлов;
  • с обходом слоя.

Это делает рефакторинг крайне болезненным. Лучше потратить время и:

  • создать index.ts в каждом срезе;
  • экспортировать через него только то, что действительно нужно снаружи.

Смешивание бизнес-логики и UI

В Svelte легко писать «все сразу» в одном компоненте:

  • запросы к API;
  • бизнес-логика;
  • UI.

В рамках FSD и svelte-fsd лучше:

  • держать бизнес-логику в model (stores, функции);
  • выносить API в отдельные файлы (api);
  • оставлять UI-компоненты максимально «глупыми».

Это упрощает тестирование, переиспользование и рефакторинг.

Нарушение зависимостей слоев

Еще одна частая проблема: entities начинает зависеть от features или widgets. Это сильно ломает идею FSD.

Решение:

  • если вам нужно переиспользовать UI компонент из widgets в entities, скорее всего, он должен быть в shared/ui;
  • если фича зависит от другой фичи, подумайте, не является ли это одной более крупной фичей или частью уровня widgets/pages.

Заключение

Feature Sliced Design в связке с Svelte и библиотекой svelte-fsd помогает структурировать код вокруг бизнес-функциональности, а не технологий. Вы разделяете приложение на слои, срезы и сегменты, определяете публичные API через index-файлы, контролируете зависимости и тем самым упрощаете развитие проекта.

В контексте Svelte подход FSD особенно хорошо сочетается с встроенными сторами и простотой компонентов. Svelte-fsd добавляет к этому четкие соглашения, вспомогательные инструменты и рекомендации по тому, как правильно организовать слои и точки входа. Вы получили примеры:

  • сущностей (entities) на основе Svelte-сторов;
  • фич (features) с разделением api, model и ui;
  • виджетов (widgets), которые собирают фичи и сущности;
  • страниц (pages), которые только компонируют интерфейс;
  • общего кода (shared), который остается независимым.

Подход рекомендуется внедрять постепенно: начинать с новых модулей, затем переносить ключевые сущности и фичи, а уже после — ужесточать правила импортов и зависимостей. При аккуратном применении FSD и svelte-fsd проект на Svelte становится более предсказуемым, модульным и удобным для работы команды.

Частозадаваемые технические вопросы по теме статьи и ответы на них

Как организовать тесты в структуре FSD для Svelte

Обычно тесты размещают рядом с кодом среза. Например:

  • entities/user/model/store.test.ts
  • features/auth/model/login.test.ts

Важно сохранять ту же иерархию слоев и срезов. Для UI-компонентов создавайте файлы вроде ui/UserCard.test.ts. Это позволит быстро находить тесты и поддерживать их в актуальном состоянии.

Как использовать TypeScript с svelte-fsd без дублирования типов между слоями

Общие типы, например для ID сущностей или стандартных ответов API, выносите в shared/types. Доменные типы конкретных сущностей держите в entities//model/types.ts и экспортируйте через index.ts этой сущности. Если тип нужен в features, импортируйте его из entities, а не копируйте.

Как адаптировать FSD для SvelteKit с серверным рендерингом

SvelteKit добавляет слои маршрутизации и серверных обработчиков (load, +page.server.ts и т.д.). При этом структура FSD по слоям (app, pages, widgets, features, entities, shared) остается той же. Внутри маршрутов SvelteKit вы подключаете страницы из слоя pages. Логику загрузки данных (load-функции) можно считать частью слоя app/pages и использовать внутри них model/api из features и entities.

Как разделять код по чанкам с учетом слоев FSD

Код-сплиттинг в Svelte и SvelteKit обычно настраивается на уровне маршрутов или динамических импортов. В FSD это хорошо сочетается: страница (pages/home) становится естественной границей чанка. Внутри страницы можно использовать динамический импорт для тяжелых виджетов или фич, например import('src/widgets/chart'), не нарушая структуры слоев.

Как поступать с кросс-слайсовыми событиями и шиной событий

Если вам нужна общая шина событий, она должна располагаться в shared (например shared/lib/eventBus.ts). Фичи и сущности могут подписываться на события и публиковать их через эту шину. Главное — не допускать, чтобы eventBus знал о конкретных фичах или сущностях, только о событиях в виде строк или типизированных структур.

Стрелочка влевоАрхитектура FSD для Vue - vue-fsdАрхитектура FSD для React - практическое руководство по react-fsdСтрелочка вправо

Все гайды по Fsd

Открыть базу знаний

Отправить комментарий