documentation
documentation copied to clipboard
INTEGRATION: Добавить туториал миграции с legacy (v1)
Туториал по разделению приложения (by-types => feature-sliced?)
- без файлов, только со структурой (возможно тольок с .gitkeep)
Чтобы не усложнять понимание
Возможно нужен только гайд
А возможно отдельно туториал (для демонстрации), а отдельно гайд (с конкретными шагами и советами практическими)
есть какие-нибудь гайд в стиле до -> после с поэтапным переходом (когда у нас было вообще все наваленно в components, store)?
С чего стоит начинать мигрировать существующий проект, какие-то идеи на плановый рефакторинг для приведения проекта к фьюче-слайс виду?
sidebar_position: 3
import WIP from '@site/src/shared/ui/wip/tmpl.mdx'
Миграция с legacy
В статье агрегируется опыт нескольких компаний и проектов по переезду на feature-sliced с разными изначальными условиями
Зачем?
Насколько нужен переезд? "Смерть от тысячи порезов" и Техдолг. Чего не хватает? Чем может помочь методология?
См. доклад Илья Климова про необходимость и порядок рефакторинга
Про что стоит помнить
Адаптируйте под ваш проект
Стоит понимать, что все проекты очень уникальны - поэтому здесь описан максимально общий план по переходу проекта от легаси на архитектуру, которая больше завязана на предметную область
Все шаги стоит воспринимать и адаптировать конкретно под ваш случай
Взвесьте "все за и против"
При этом каждый шаг, хоть и уводит нас от Big Ball of Mud архитектуры, но и требует затраты на рефакторинг
Поэтому учитывайте затраты и профиты на каждый пункт, когда будете составлять план
Изначальная структура
Допустим, имеем примерно такой по структуре типичный legacy-проект:
├── products/
| ├── components/
| ├── containers/
| ├── store/
| ├── styles/
├── checkout/
| ├── components/
| ├── containers/
| ├── helpers/
| ├── styles/
└── src/
├── actions/
├── api/
├── epics/
├── components/
├── containers/
├── constants/
├── i18n/
├── modules/
├── helpers/
├── pages/
├── routes/
├── utils/
├── reducers/
├── redux/
├── selectors/
├── store/
├── styles/
├── App.jsx
└── index.jsx
Какой план?
1. Унификация кодовой базы
:::info Важно
Часто в проектах, модули, имеющие одну и ту же ответственность, называются по-разному или расположены в совсем разных местах
Чтобы избежать путанницы в разработке, следует унифицировать этот код и сам нейминг
:::
- Если у вас несколько директорий, где хранятся одни и те же сущности/фичи/компоненты - самое время переместить их в одно место
-
Чаще всего - это
/src/
-
Чаще всего - это
- Если у вас есть несколько типов директорий для модулей, который отвечают за одно и тоже - унифицируйте и их
-
helpers/utils
=>helpers
(или лучше сразуlib
) -
redux/store/stores/data
=>store
-
pages/routes
=>pages
-
- ├── products/
- | ├── components/
- | ├── containers/
- | ├── store/
- | ├── styles/
- ├── checkout/
- | ├── components/
- | ├── containers/
- | ├── helpers/
- | ├── styles/
+ └── src/
├── actions/
├── api/
+ ├── components/
+ ├── containers/
├── constants/
├── epics/
+ ├── i18n/
├── modules/
+ ├── helpers/
+ ├── pages/
- ├── routes/
- ├── utils/
├── reducers/
- ├── redux/
├── selectors/
+ ├── store
+ ├── styles/
├── App.jsx
└── index.jsx
2. Собираем вместе излишне раздробленное
Если вашему проекту относительно "повезло", вполне возможно, что вы находитесь уже на этой стадии:
└── src/
├── actions/
├── api/
├── components/
├── containers/
├── constants/
├── epics/
├── i18n/
├── modules/
├── helpers/
├── pages/
├── reducers/
├── selectors/
├── store/
├── styles/
├── App.jsx
└── index.jsx
Проблема
Теперь следует посмотреть на то - как декомпозирована логика по проекту
:::info Важно
Чаще всего логика в проектах, относящаяся к одному домену в предметной области, излишне размазана по проекту
Чтобы устранить этот неприятный эффект, стоит расположить такие модули рядом
См. подробнее в "Handbook: Desegmented"
:::
# Плохо - логика модулей разбросана по проекту
- ├── components/DeliveryCard.js
- ├── containers/DeliveryCard.js
- ├── actions/delivery.js
- ├── epics/delivery.js
- ├── constants/delivery.js
- ├── getters/delivery.js
- ├── selectors/delivery.js
- ├── helpers/delivery.js
# Еще хуже - нет консистентности
- ├── actions/delivery.js
- ├── epics/delivery.js
- ├── constants/delivery.js
- ├── entities/delivery/{getters, selectors, constants}
# Хорошо - все, близкое по предметной области, находится рядом
+ ├── entities/delivery
+ | ├── ui/ # ~ components
+ | | ├── DeliveryCard.js
+ | ├── model/
+ | | ├── actions.js
+ | | ├── epics.js
+ | | ├── getters.js
+ | | ├── selectors.js
+ | ├── lib/ # ~ helpers
Как исправить
-
Разбираем свалку в
store
, и распределяем по предметной области- Еще хуже, когда некоторые экшены расположены "глобально", а некоторые в store папке
- В этом случае, будет еще ощутимее польза от того, что мы соберем эти разбросанные сегменты вместе
-
Устраняем излишнее дробление на компоненты контейнеры
- Поначалу может показаться странным - "Почему мы кладем components/containers рядом с entity?"
- Но если мы попробуем отдельно сгруппировать компоненты/контейнеры - то увидим, что так или иначе они будут группироваться по доменной области
└── src/ ├── components/ | ├── delivery/ | | ├── deliveryCard.js | | ├── deliveryChoice.js | ├── region/ | | ├── regionSelect.js | | ├── regionMap.js | ├── user/ | | ├── userAvatar.js | | ├── userPicker.js
По итогу получаем
└── src/
- ├── actions/
├── api/
- ├── components/
- ├── containers/
- ├── constants/
- ├── epics/
+ ├── entities/{...}
+ | ├── ui
+ | ├── model/{actions, selectors, ...}
+ | ├── lib
├── i18n/
| # Временно можем положить сюда оставшиеся сегменты
+ ├── modules/{helpers, constants}
- ├── helpers/
├── pages/
- ├── reducers/
- ├── selectors/
- ├── store/
├── styles/
├── App.jsx
└── index.jsx
3. Выделяем скоупы ответственности
На самом деле - уже хорошо, когда проект разбит похожим образом:
└── src/
├── api/
├── entities/{...}
| ├── ui
| ├── model/{actions, selectors, ...}
| ├── lib
├── i18n/
├── modules/{helpers, constants}
├── pages/
├── styles/
├── App.jsx
└── index.jsx
Проблема
Но тем не менее, если мы посмотрим на получившиеся сущности и компоненты, то заметим интересную вещь:
:::info Важно
В проектах часто нет явного разделения модулей по скоупу их ответственности
Из-за этого рядом лежат совсем разные по уровню знаний модули, и впоследствие возникают кросс-импорты
Чтобы этого избежать, следует явно разделять их по слоям, или хотя бы локализовать их зоны влияния
См. подробнее в "Handbook: Cross-imports"
:::
└── src/entities
├── product # entity
├── product-title # entity [partition]
├── add-to-cart # feature
├── upload-image # feature
|
├── search-bar # feature
├── viewer-picker # feature
├── header # widget
Как исправить
Попытаться явно выделить слои согласно методологии: shared
, entities
, features
, (widgets)
, pages
, (process)
, app
└── src/
- ├── api/
+ ├── app/
+ | ├── index.jsx
+ | ├── style.css
├── pages/
+ ├── features/
+ | ├── add-to-cart/{ui, model, lib}
+ | ├── choose-delivery/{ui, model, lib}
+ ├── entities/{...}
+ | ├── delivery/{ui, model, lib}
+ | ├── cart/{ui, model, lib}
+ | ├── product/{ui, model, lib}
+ ├── shared/
+ | ├── api/
+ | ├── lib/ # helpers
+ | | ├── i18n/
+ | ├── config/ # constants
- ├── i18n/
- ├── modules/{helpers, constants}
└── index.jsx
4. Final ?
К этому моменту проект уже по большей части переведен на feature-sliced
└── src/
├── app/
| ├── index.jsx
| ├── style.css
├── pages/
├── features/
| ├── add-to-cart/{ui, model, lib}
| ├── choose-delivery/{ui, model, lib}
├── entities/{...}
| ├── delivery/{ui, model, lib}
| ├── cart/{ui, model, lib}
| ├── product/{ui, model, lib}
├── shared/
| ├── api/
| ├── lib/
| | ├── i18n/
| ├── config/
└── index.jsx
Но при переносе могут подсветиться и другие слабые места:
- Complexity & Discoverability: Логика некоторых модулей излишне разбухшая, из-за чего сложно их изучать и вносить правки
- Explicit sharing: Часть логики из предметной области встречается несколько раз по всему проекту (и каждый раз реализуется заново)
- Public API: При использовании модулей, местами используются приватные импорты - к внутренним частям модулей (нарушая Public API контракт)
Насколько стоит их исправлять при рефакторинге?
Зависит, опять же, от оставшихся ресурсов и соотношения профита & затрат после исправления этих проблем
См. также
- (Доклад) Илья Климов - Крысиные бега бесконечного рефакторинга: как не дать техническому долгу убить мотивацию и продукт
-
(Доклад) Илья Азин - Архитектура Frontend проектов
- В докладе в том числе рассмотрены подходы к архитектуре и стоимости рефакторинга
Я бы в данном случае даж говорил не про "entities", а про "features"
Тип чаще всего на этой стадии люди же мыслят не сущностями, а фичами
И пусть даже будет фича "карточка товара", но это куда лучше, чем "сущность добавления в корзину" (хотя всякое бывает офк)