Десегментация
Десегментация (также известная как горизонтальная нарезка или группировка по слоям) — это паттерн организации кода, при котором файлы группируются по их техническим ролям, а не по бизнес-доменам, которым они служат. Это значит, что код с похожим техническим назначением хранится в одном месте, независимо от бизнес-логики, которую он обрабатывает.
Этот подход популярен в метафреймворках вроде Next и Nuxt благодаря своей простоте: на нём легко начать, и он позволяет использовать такие функции, как автоимпорты и файловый роутинг:
Директорияapp
Директорияcomponents
- DeliveryCard.jsx
- DeliveryChoice.jsx
- RegionSelect.jsx
- UserAvatar.jsx
Директорияactions
- delivery.js
- region.js
- user.js
Директорияcomposables
- delivery.js
- region.js
- user.js
Директорияconstants
- delivery.js
- region.js
- user.js
Директорияutils
- delivery.js
- region.js
- user.js
Директорияstores
Директорияdelivery
- getters.js
- actions.js
Этот паттерн встречается и в кодовых базах на FSD в виде обобщённых папок:
Директорияfeatures
Директорияdelivery
Директорияui
Директорияcomponents/ ⚠️
- …
Директорияentities
Директорияrecommendations
Директорияutils/ ⚠️
- …
Источником десегментации могут быть и файлы. Файлы вроде types.ts могут объединять в себе несколько доменов, что усложняет навигацию и будущий рефакторинг, особенно в таких слоях, как pages или widgets:
Директорияpages
Директорияdelivery
- index.ts
Директорияui
- DeliveryCard.tsx
- DeliveryChoice.tsx
- UserAvatar.tsx
Директорияmodel
- types.ts ⚠️
- utils.ts ⚠️
Директорияapi
- endpoints.ts ⚠️
// ❌ Плохо: смешанные бизнес-домены в обобщённом файлеexport interface DeliveryOption { id: string; name: string; price: number;}
export interface UserInfo { id: string; name: string; avatar: string;}// ❌ Плохо: смешанные бизнес-домены в обобщённом файлеexport function formatDeliveryPrice(price: number) { return `$${price.toFixed(2)}`;}
export function getUserInitials(name: string) { return name.split(' ').map(n => n[0]).join('');}// ❌ Плохо: смешанные бизнес-домены в обобщённом файлеexport async function fetchDeliveryOptions() { /* ... */ }export async function fetchUserInfo() { /* ... */ }Проблема
Заголовок раздела «Проблема»Хотя на этой структуре легко начать, в крупных проектах она может привести к проблемам с масштабируемостью:
-
Низкая связность (low cohesion): изменение одной фичи часто требует правок файлов в нескольких больших папках, например
pages,componentsиstores. -
Сильная связанность (tight coupling): компоненты могут иметь неожидаемые зависимости, что приводит к сложным и запутанным цепочкам зависимостей.
-
Сложный рефакторинг: чтобы вручную извлечь код, относящийся к конкретному домену, требуются дополнительные усилия.
Решение
Заголовок раздела «Решение»Группируйте весь код, относящийся к конкретному домену, в одном месте.
Избегайте обобщённых имён папок, таких как types, components, utils, а также обобщённых имён файлов вроде types.ts, utils.ts или helpers.ts. Вместо этого используйте имена, которые напрямую отражают домен, который они представляют.
По возможности избегайте обобщённых имён файлов вроде types.ts, особенно в слайсах с несколькими доменами:
Директорияpages
Директорияdelivery
- index.tsx
Директорияui
- DeliveryPage.tsx
- DeliveryCard.tsx
- DeliveryChoice.tsx
- UserInfo.tsx
Директорияmodel
- delivery.ts
- user.ts