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

Мы используем preact/compat — с его помощью получаем доступ к множеству библиотек экосистемы React, что делает разработку более гибкой, и при этом можем использовать Preact. Но эти же плюсы зачастую оборачиваются в обратную сторону: например, нет единой методологии по проектированию приложений, как, например, в Angular. Кроме того, многообразие библиотек усложняет погружение в проект, а свобода в реализации и проектировании может обернуться захламлением кодовой базы, что пугает разработчиков, особенно новичков. 

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

Для нашей команды эти проблемы также актуальны. Чтобы их решить раз и навсегда, мы обратились к активно развивающейся методологии Feature-Sliced Design (FSD). Ниже я познакомлю с ее главными принципами и с нашим опытом ее использования. Забыл представиться — я Женя, фронтенд-разработчик в команде Quick Experiments inDrive. Расскажу, как мы занимается разработкой внутренних стартапов на основе бизнес-гипотез с помощью FSD.

В методологии FSD используются три уровня абстракции:

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

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

3. Сегменты. Распределяются по назначению модуля в коде и реализации. По методологии каждый сегмент отвечает за свою часть технической реализации модуля:

  • api/ — работа с API. Авторы методологии советуют класть API-логику в shared, чтобы она не распылялась по проекту.

  • config/ — конфигурация модуля.

  • lib/ — различные утилитарные функции и вспомогательные библиотеки. 

  • model/ — бизнес-логика: store, actions, effects, reducers и так далее.

  • ui/ — отвечает за отображение. 

Графическое представление методологии FSD
Графическое представление методологии FSD

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

Важное правило архитектуры FSD, о котором я не могу не упомянуть:

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

Когда мы модифицируем фичу B, мы неявно изменяем и фичу A. В небольшом приложении, возможно, это не так критично. Но чем больше людей работает над проектом и чем объемнее кодовая база, тем сложнее держать в голове все связи между модулями. Данное правило позволяет стандартизировать этот процесс, упрощает рефакторинг и модификацию модулей.

Перед тем, как рассмотрим каждый слой отдельно, несколько общих советов по внедрению FSD:

  1. Понимание методологии должно быть у всех разработчиков в команде. Новые разработчики должны в обязательном порядке прочитать документацию о FSD. В противном случае пользы от внедрения методологии точно не будет.

  1. Глобальные изменения в архитектуре необходимо обсуждать всей командой. Важно периодически проводить анализ архитектуры на наличие кросс-импортов и «болей» в кодовой базе. В целом, совет актуален и для архитектур других видов. 

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

Рассмотрим каждый слой подробно — от самого нижнего до самого верхнего.

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

  • shared/api — работа с API.

  • shared/config — модуль конфигурации приложения и его окружения. 

  • shared/hooks — кастомные хуки.

  • shared/lib — различные утилитарные функции и вспомогательные библиотеки.

  • shared/themes — список цветов и тем приложения.

  • shared/ui — UI-компоненты: Input, Select, Table и другие. 

Самое главное — не путать абстрактные UI-компоненты с компонентами, реализующими конкретные бизнес-сущности или фичи. К примеру, Select — это Shared/UI-компонент, а CitySelect — уже фича. При создании того или иного модуля нужно оценить, будут ли использоваться бизнес-сущности в компоненте. Если нет — модуль необходимо назвать максимально абстрактно и поместить в Shared. В противном случае, желательно указать в названии бизнес-сущность или фичу, которую модуль реализует, и поместить в слой Entities или Features. От грамотного нейминга в этой методологии зависит очень многое. 

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

3. Features — части функциональности приложения. Пожалуй, самый сложный для понимания и определения слой в методологии, так как само определение «фичи» зависит от конкретной прикладной области и бизнес-требований. Поэтому при переходе на FSD нужно внедрять «фичи»  только при полной уверенности, что это не внесет дополнительную сложность для разработчиков.

Изначально в нашей команде было проблемно определить разницу между Features и Entities. После нескольких обсуждений мы договорились, что фича — это полезная и, желательно, одна функциональность для пользователя, а сущность — то, с помощью чего реализуется эта функциональность. Например, City, Product — сущности, а SearchByCity, BuyProduct — фичи.

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

5. Со слоем Pages, я думаю, все просто и понятно — это страницы нашего приложения. Методология советует, чтобы каждая страница имела максимально простую структуру, а бизнес-логику переносить на более низкие слои. Поэтому страница — это композиция из виджетов и/или фич, которая отображает взаимодействие между нижележащими слоями. 

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

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

8. В App находится общая инициализирующая логика приложения — различные обертки, глобальные сторы и стили. В общем, все то, что влияет на работу всего приложения.

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

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

Эти сложности в полной мере компенсируется преимуществами в виде упрощения онбординга, командного взаимодействия и единой терминологии. Методология подталкивает разработчиков сфокусироваться на задачах бизнеса и отражать их в коде. Тогда (в идеале, конечно) никому не придется объяснять, ради чего создавался тот или иной модуль.

Источники:

  1. Официальная документация по методологии FSD.

  2. Отличный доклад от одного из соавторов методологии.

Комментарии (2)


  1. chuikoffru
    04.11.2022 15:24

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


  1. nin-jin
    04.11.2022 18:24

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

    Вот я смотрю вы не боитесь самых дерзких экспериментов с активно развивающимися штуками. Может тогда ещё и $mol попробуете, чтобы не иметь следующих проблем:

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

    Ну и заодно расскажите, почему нам стоит ограничивать себя и не допускать встраивания страницы "авторизация" в страницу "форма комментария", или приложения "чат" в виджет "лента комментариев"? Потому что кривой фреймворк на самом деле не умеет в компонентную декомпозицию?