Блог CosySoft
2025-09-10 11:24 Разработка

Effector и Feature-Sliced на реальных проектах

Effector и Feature-Sliced Design (FSD) — сочетание давно проверенное в продакшене. Наш frontend-разработчик Антон плотно использовал эту связку на больших проектах и увидел сильные и слабые стороны этой архитектуры. В статье разберем, как устроен FSD, как в него встраивается Effector и какие фундаментальные проблемы есть у этого дуэта.

Feature-Sliced Design (FSD)

FSD помогает избежать проблем простой модульной архитектуры. Эта методология делит код на слои и модули (слайсы). Каждый слой отвечает за определенную зону ответственности, а слайсы внутри слоев — за конкретную задачу или бизнес-сущность. Так мы добиваемся однонаправленного потока данных и высокой связности кода.
Три базовых понятия
  1. Слой — уровень иерархии с четкой бизнес-направленностью (shared, entities, features, widgets, pages, processes, app). Часть слоев опциональна.
  2. Слайс — модуль внутри слоя, объединяющий логику, UI, запросы и константы в одном месте.
  3. Сегменты — папки внутри каждого слайса: UI, model, lib, API, config и constants.

Как работают слои

  • Shared хранит все общие компоненты без привязки к бизнес-логике: кнопки, инпуты, хелперы, конфиги, глобальные стили.
  • Entities — сущности предметной области: товар, заказ, пользователь и т.д. Внутри — компоненты и функции, применимые только к этой сущности.
  • Features — самостоятельные бизнес-функциональности (лайк, комментарий, авторизация, подписка). Используют сущности и shared.
  • Widgets — крупные видимые блоки, которые внутри себя могут включать фичи (например, верхняя панель, карточка поста).
  • Pages — собирают виджеты и фичи в одну страницу. Тут минимум логики, в основном набор виджетов и их макет.
  • Processes — для многоэтапных операций, охватывающих несколько страниц (необязательный слой).
  • App — точка входа, инициализация приложения: роутер, провайдеры, глобальная конфигурация.

Принцип работы

  1. Поток данных однонаправленный: нижние слои не используют верхние. Фича не может брать код из виджета, а entity не лезет в page.
  2. Каждый модуль (слайс) имеет публичный API. Внешнему коду доступны только явно экспортированные сущности, внутренняя реализация скрыта. Это повышает изоляцию и упрощает поддержку.
  3. Модули легко заменять или удалять. Если виджет не нужен, его убираем без опасности «сломать» соседние блоки.
Плюсы FSD
Недостатки FSD
Проект становится бизнес-ориентированным: сущности, фичи и виджеты проще искать, документировать и сопровождать.
Выше порог входа. Нужно понимать, как разбивать проект на слои и слайсы.
Архитектура масштабируется как на уровне кода, так и на уровне команды: новые люди быстро осваивают логику, а изменения не «цепляют» весь проект сразу.
Приходится сразу учитывать архитектурные правила, настраивать линтеры, проводить код-ревью. Без дисциплины методология теряет смысл.
Независимость от технологий: FSD — это не про React, Vue или Svelte, а про структурирование кода. Инструменты и менеджеры пакетов можно выбирать любые.
Первая интеграция FSD требует времени. Но для долгоживущих и растущих продуктов она экономит усилия в перспективе.

Практические советы по внедрению

  • Внедряйте FSD пошагово: определите слой App, создавайте модули в Entities и Features, храните общие элементы в Shared.
  • При необходимости можно упорядочить слои префиксами (01_pages, 02_entities), но лучше воспользоваться алиасами в настройках, чтобы упростить переход от префиксов к более «чистым» названиям.
  • По мере роста проекта строго следите, чтобы слайсы не тянулись к слоям выше.
  • В Shared складывают общую инфраструктуру и переиспользуемые компоненты, где нет бизнес-логики.
  • Если сложно ориентироваться в папках, некоторые добавляют числовые префиксы (например, 01-pages, 02-widgets). Но лучше использовать алиасы и настроить импорты так, чтобы при отказе от префиксов не пришлось переписывать весь код.

Что такое Effector

Effector — это инструмент для управления состоянием в JavaScript-приложениях. Он помогает описывать бизнес-логику в виде небольших, независимых модулей и включает три базовых концепции.
  1. Store. Хранит состояние и обновляется при наступлении событий. Можно создавать сколько угодно store, и каждый отвечает за свой участок данных.
  2. Event. Функция, отображающая изменение или действие в системе. На события можно подписываться, чтобы обновлять состояние store или запускать другие процессы.
  3. Effect. Специализированное событие для асинхронных операций: запросов к серверу, таймеров или работы с кэшем. Выполняет «побочные» действия и возвращает результат для дальнейшей обработки.
Кроме них в Effector есть вспомогательные функции (sample и т. д.), которые позволяют связывать события, сторы и эффекты в гибкую логику приложения без избыточного кода.
Обычно эффекты (createEffect) применяют для работы с API, запросов к серверу и любой асинхронной логики. При необходимости их можно расширять или заменять другими инструментами для сетевых операций. Effector не привязан к конкретному фреймворку: его можно применять с React, Vue или любым другим.
Официальная документация помогает разобраться с API, паттернами и рекомендациями по организации кода.
Таким образом Effector дает простую модель, где события изменяют состояние в store, а асинхронные операции оформляются как эффекты, без громоздкого бойлерплейта.

Достоинства Effector

  1. Небольшой вес. Библиотека компактна, поэтому приложение не обрастает лишним кодом.
  2. Быстрый вход. Для базовой работы достаточно понимать три единицы (Store, Event, Effect) и функцию sample. Этого хватит, чтобы собирать рабочую логику и объяснить новый код коллегам.
  3. Развитая экосистема. Уже есть готовые утилиты, роутинг, web API, кеш-запросы (query), средства для форм, SSR и многое другое. Это ускоряет написание кода и облегчает поддержку.
  4. Framework-агностичен. Effector не привязан к React или Vue, его можно внедрять в любую среду и комбинировать с другими инструментами.
  5. Batching вычислений. Effector группирует обновления, избегая лишних перерендеров и улучшая производительность.
  6. Граф связей вместо дерева. Вместо одного большого стора — набор атомарных сторов, связанных в граф. Это гибче, дает наглядную визуализацию зависимостей и позволяет точечно контролировать логику.
  7. Истинная MVC-модель. Логика полностью отделена от жизненного цикла компонента. Сайд-эффекты (запросы, хранение данных) остаются за рамками UI, поэтому UI проще переиспользовать.
  8. Контроль над рендерами. За счет независимости от React-специфики в коде меньше мемоизации (useCallback, useMemo), а перерендеры сокращаются за счет отслеживания конкретных сторов и событий.
  9. Отличная поддержка SSR. Effector легко адаптируется под серверный рендеринг (есть готовые решения), в том числе благодаря созданию отдельных «доменов» состояния и их «склеиванию» при переходе с сервера на клиент.

Проблемы Effector

Избыточная декларативность
Effector требует множество объявлений (stores, events, effects) и связок через sample. Код может разрастаться и занимать в несколько раз больше строчек по сравнению с простой функцией.
Разрозненность логики
Связи через sample могут располагаться в разных местах проекта. Если не следовать четким правилам (код-стилю, соглашениям по расположению) — читать и поддерживать код сложно.
Неочевидная последовательность вызовов
Внутренние механизмы Effector (приоритет подписок .on над sample) могут привести к путанице в том, какой код и когда срабатывает первым.
Особый «домен» внутри JavaScript
Effector вынуждает использовать собственные операторы (sample, combine и т.п.) или сторонние библиотеки (Patronum) вместо привычных if/else или прямых функций. Это увеличивает порог вхождения и требует от разработчиков дополнительной гибкости.
Необходимость строгой архитектуры и код-стиля
Чтобы реакции и бизнес-логика не превращались в хаотичный набор сэмплов, команде нужно договориться, где хранить юниты (store, event, effect) и как их именовать. Иначе логику сложно масштабировать.

Как уменьшить хаос в коде Effector

Единый код-стайл и разделение на «домены»
  • Старайтесь группировать store, event и effect в одном месте, не разбрасывайте их по разным файлам.
  • Соблюдайте внутренний порядок: сначала объявление юнитов, потом их подписки и связывание через sample, и только затем экспорты.
Фабрики
  • Выносите типовые паттерны в утилиты (например, «фабрику для модалок», «фабрику для булевых сторов»).
  • Это сокращает объем кода и избавляет от повторяющихся объявлений.
Effector Models
Разработчики Effector планируют добавить «модели» как дополнительный способ структурировать логику. Это должно упростить создание и связку сторов.

Добавьте императивности через createAction

В моделях на Effector логика часто расползается по десяткам sample. Это делает код слабо связанным и трудным для чтения и ревью. Структура действий теряется, порядок срабатывания становится критичным, появляются race condition и неявные условия. В итоге, даже несложная модель может превратиться в хрупкий механизм.

В чем проблема sample?

Когда в модели все завязано на sample, глобальная логика распадается на куски. Каждый отвечает за маленькую часть — реакцию на событие, проверку условия, вызов эффекта. Такие куски сложно связать в уме: приходится ментально парсить модель, выяснять, что к чему относится и в каком порядке вызывается.
При этом:
  • Логика одного действия может быть раскидана по разным местам.
  • Условия легко теряют консистентность при перестановке семплов.
  • Ошибки появляются не из-за сложности задачи, а из-за того, что она размазана по модели.

Как помогает createAction

createAction позволяет упаковать всю логику одного действия в одном месте. Это дает:
  • Именованность. Появляется точка входа — понятно, где искать и что менять.
  • Связанность. Вся логика действия рядом — меньше прыжков по коду.
  • Линейность. Код читается сверху вниз, а не как граф из sample.
Результат — более императивный стиль, в котором проще думать, отлаживать и вносить изменения. Весь флоу — в одном месте. Легко читать, ревьюить, дебажить. Ошибки из-за неправильного порядка вызовов исключены.

Итоги

Связка Effector и Feature-Sliced Design дает гибкость, но требует внимательного отношения к архитектуре. Если правильно выстроить структуру, получится модульный и управляемый проект с отделенной логикой и понятным потоком данных. Но все это работает только при условии, что команда (или хотя бы ты сам) соблюдает договоренности.
Без четкого кодстайла, структуры и дисциплины Effector быстро превращается в мешанину из sample, store, event и effect. В моем опыте больше всего помогли фабрики, createAction и разбиение проекта на домены. Это снижает уровень шума, делает код более линейным и заметно упрощает поддержку. Надо понимать, что Effector — не универсальное решение. Но если хочется контролировать логику на уровне бизнес-событий, а не UI-компонентов — это по-прежнему один из лучших инструментов, особенно в связке с FSD.