
- Разделить 835 строк на модульную архитектуру (11 файлов) - Создать orchestrator + types + hooks + blocks структуру - Сохранить все функции: 3 вкладки, статистика, поиск, партнерская ссылка - Исправить типы партнерской ссылки (PartnerLink → string) - Интегрировать поиск новых организаций в главную вкладку - Сохранить glass-эффекты, анимации и все визуальные элементы 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
529 lines
19 KiB
Markdown
529 lines
19 KiB
Markdown
# 🏗️ РУКОВОДСТВО ПО МОДУЛЯРИЗАЦИИ КОМПОНЕНТОВ SFERA
|
||
|
||
**Дата создания:** 17 сентября 2025
|
||
**Версия:** 1.0
|
||
**Статус:** Официальное руководство по процессу
|
||
**Применимость:** Все компоненты >300 строк
|
||
|
||
---
|
||
|
||
## 🎯 НАЗНАЧЕНИЕ ДОКУМЕНТА
|
||
|
||
Это руководство описывает стандартизированный процесс модуляризации React компонентов в системе SFERA согласно `MODULAR_ARCHITECTURE_PATTERN.md`. Документ создан на основе успешного опыта рефакторинга:
|
||
|
||
- ✅ `multilevel-supplies-table` (1720 строк)
|
||
- ✅ `direct-supply-creation` (1637 строк)
|
||
- ✅ `user-settings` (1575 строк)
|
||
- ✅ `fulfillment-warehouse-dashboard` (1310 строк)
|
||
|
||
---
|
||
|
||
## 📋 КОГДА ПРИМЕНЯТЬ МОДУЛЯРИЗАЦИЮ
|
||
|
||
### ✅ ОБЯЗАТЕЛЬНАЯ МОДУЛЯРИЗАЦИЯ:
|
||
|
||
1. **Размер компонента** >300 строк
|
||
2. **Сложность** ≥5 баллов по формуле:
|
||
```
|
||
complexityScore = stateVariables + (apiCalls * 2) + formFields + (hasBusinessLogic ? 3 : 0)
|
||
```
|
||
3. **Тип компонента:**
|
||
- Страницы (`page.tsx`)
|
||
- Дашборды
|
||
- Формы создания/редактирования
|
||
- Таблицы с данными из БД
|
||
- Wizard/multi-step компоненты
|
||
|
||
### ⚠️ РЕКОМЕНДУЕМАЯ МОДУЛЯРИЗАЦИЯ:
|
||
|
||
- Компоненты >200 строк с планами расширения
|
||
- Компоненты с >8 React hooks
|
||
- Компоненты с множественной бизнес-логикой
|
||
- Часто изменяемые компоненты
|
||
|
||
---
|
||
|
||
## 🔄 ПОШАГОВЫЙ ПРОЦЕСС МОДУЛЯРИЗАЦИИ
|
||
|
||
### ЭТАП 1: АНАЛИЗ И ПЛАНИРОВАНИЕ (1 день)
|
||
|
||
#### 1.1 Анализ исходного компонента
|
||
```bash
|
||
# Подсчет размера
|
||
wc -l component.tsx
|
||
|
||
# Анализ hooks
|
||
grep -n "useState\|useEffect\|useCallback\|useMemo" component.tsx
|
||
|
||
# Анализ импортов и зависимостей
|
||
grep -n "import" component.tsx | head -20
|
||
```
|
||
|
||
#### 1.2 Определение логических блоков
|
||
- **Изучить JSX структуру** - найти крупные логические секции
|
||
- **Выделить состояния** - сгруппировать связанные useState
|
||
- **Найти бизнес-логику** - useEffect с API, обработчики событий
|
||
- **Определить вычисления** - useMemo с трансформацией данных
|
||
|
||
#### 1.3 Создание плана архитектуры
|
||
```
|
||
target-component/
|
||
├── index.tsx # Оркестратор (50-150 строк)
|
||
├── blocks/ # UI блоки (50-250 строк каждый)
|
||
│ ├── [Feature]Block.tsx # 3-7 блоков по функциональности
|
||
│ └── [Section]Block.tsx
|
||
├── hooks/ # Бизнес-логика (50-150 строк каждый)
|
||
│ ├── use[Domain][Action].ts # 2-6 hooks по области ответственности
|
||
│ └── use[Feature]State.ts
|
||
└── types/
|
||
└── [component].types.ts # Все TypeScript интерфейсы
|
||
```
|
||
|
||
### ЭТАП 2: СОЗДАНИЕ СТРУКТУРЫ (0.5 дня)
|
||
|
||
#### 2.1 Создание папочной структуры
|
||
```bash
|
||
mkdir -p src/components/[domain]/[component-name]/{blocks,hooks,types}
|
||
touch src/components/[domain]/[component-name]/index.tsx
|
||
touch src/components/[domain]/[component-name]/types/index.ts
|
||
```
|
||
|
||
#### 2.2 Создание заготовок файлов
|
||
- index.tsx - пустой компонент-оркестратор
|
||
- types/index.ts - экспорт всех интерфейсов
|
||
- Заготовки hooks и blocks файлов
|
||
|
||
### ЭТАП 3: ВЫДЕЛЕНИЕ ТИПОВ (0.5 дня)
|
||
|
||
#### 3.1 Создание TypeScript интерфейсов
|
||
```typescript
|
||
// types/index.ts
|
||
export interface [Component]Props {
|
||
// Пропсы главного компонента
|
||
}
|
||
|
||
export interface [Block]Props {
|
||
// Пропсы для каждого блока
|
||
}
|
||
|
||
export interface [Entity] {
|
||
// Интерфейсы доменных сущностей
|
||
}
|
||
|
||
export interface [Hook]Return {
|
||
// Возвращаемые значения hooks
|
||
}
|
||
```
|
||
|
||
#### 3.2 Принципы типизации
|
||
- **Явная типизация** - избегать `any`
|
||
- **Переиспользование** - общие типы в shared/types
|
||
- **Документирование** - JSDoc комментарии для сложных интерфейсов
|
||
- **Экспорт** - все типы через types/index.ts
|
||
|
||
### ЭТАП 4: СОЗДАНИЕ HOOKS (1-3 дня)
|
||
|
||
#### 4.1 Структура business logic hook
|
||
```typescript
|
||
// hooks/use[Feature].ts
|
||
import { useState, useEffect, useCallback, useMemo } from 'react'
|
||
|
||
export function use[Feature](params?: Params): Return {
|
||
// 1. Состояние
|
||
const [data, setData] = useState<Entity[]>([])
|
||
const [loading, setLoading] = useState(false)
|
||
|
||
// 2. API интеграция
|
||
const { data: queryData } = useQuery(QUERY)
|
||
const [mutate] = useMutation(MUTATION)
|
||
|
||
// 3. Бизнес-логика
|
||
const handleAction = useCallback(async (input: Input) => {
|
||
setLoading(true)
|
||
try {
|
||
await mutate({ variables: input })
|
||
// success handling
|
||
} catch (error) {
|
||
// error handling
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}, [mutate])
|
||
|
||
// 4. Вычисляемые значения
|
||
const processedData = useMemo(() =>
|
||
data.map(transformFunction), [data]
|
||
)
|
||
|
||
// 5. Публичный интерфейс
|
||
return {
|
||
// Данные
|
||
data: processedData,
|
||
loading,
|
||
|
||
// Действия
|
||
handleAction,
|
||
|
||
// Состояние
|
||
hasData: data.length > 0,
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 4.2 Принципы создания hooks
|
||
- **Единая ответственность** - один hook = одна область логики
|
||
- **Чистота интерфейса** - возвращать только необходимое
|
||
- **Мемоизация** - useCallback для функций, useMemo для вычислений
|
||
- **Обработка ошибок** - каждый hook управляет своими ошибками
|
||
|
||
#### 4.3 Типы hooks
|
||
1. **Data hooks** - загрузка и кэширование данных
|
||
2. **Action hooks** - CRUD операции и мутации
|
||
3. **State hooks** - управление UI состоянием
|
||
4. **Filter hooks** - фильтрация и сортировка
|
||
5. **Validation hooks** - валидация форм
|
||
|
||
### ЭТАП 5: СОЗДАНИЕ БЛОК-КОМПОНЕНТОВ (2-4 дня)
|
||
|
||
#### 5.1 Структура блок-компонента
|
||
```typescript
|
||
// blocks/[Feature]Block.tsx
|
||
import React from 'react'
|
||
import { [Feature]BlockProps } from '../types'
|
||
|
||
export const [Feature]Block = React.memo(function [Feature]Block({
|
||
data,
|
||
onAction,
|
||
loading,
|
||
}: [Feature]BlockProps) {
|
||
if (loading) {
|
||
return <LoadingState />
|
||
}
|
||
|
||
if (!data?.length) {
|
||
return <EmptyState />
|
||
}
|
||
|
||
return (
|
||
<div className="...">
|
||
{/* UI логика блока */}
|
||
</div>
|
||
)
|
||
})
|
||
|
||
[Feature]Block.displayName = '[Feature]Block'
|
||
```
|
||
|
||
#### 5.2 Принципы блок-компонентов
|
||
- **React.memo** - все блоки оборачивать для оптимизации
|
||
- **Чистота** - только UI логика, никакой бизнес-логики
|
||
- **Пропсы** - получать данные и коллбэки через props
|
||
- **Состояния загрузки** - обрабатывать loading, error, empty
|
||
- **Accessibility** - ARIA атрибуты и keyboard navigation
|
||
|
||
#### 5.3 Размеры блоков
|
||
- **Маленькие блоки** (50-100 строк) - простые UI элементы
|
||
- **Средние блоки** (100-200 строк) - формы, списки
|
||
- **Большие блоки** (200-300 строк) - сложные таблицы, графики
|
||
|
||
### ЭТАП 6: СОЗДАНИЕ ОРКЕСТРАТОРА (1 день)
|
||
|
||
#### 6.1 Структура index.tsx
|
||
```typescript
|
||
// index.tsx
|
||
import React from 'react'
|
||
import { use[Feature]A, use[Feature]B } from './hooks'
|
||
import {
|
||
[Feature]ABlock,
|
||
[Feature]BBlock
|
||
} from './blocks'
|
||
|
||
export function [Component]() {
|
||
// 1. Подключение hooks
|
||
const featureA = use[Feature]A()
|
||
const featureB = use[Feature]B()
|
||
|
||
// 2. Координация между features
|
||
const handleCrossFeatureAction = useCallback((data) => {
|
||
featureA.action(data)
|
||
featureB.update(data)
|
||
}, [featureA.action, featureB.update])
|
||
|
||
// 3. Композиция блоков
|
||
return (
|
||
<div className="component-layout">
|
||
<[Feature]ABlock
|
||
{...featureA}
|
||
onAction={handleCrossFeatureAction}
|
||
/>
|
||
<[Feature]BBlock
|
||
{...featureB}
|
||
onUpdate={handleCrossFeatureAction}
|
||
/>
|
||
</div>
|
||
)
|
||
}
|
||
```
|
||
|
||
#### 6.2 Принципы оркестратора
|
||
- **Минимальная логика** - только координация между блоками
|
||
- **Композиция** - сборка блоков в единый интерфейс
|
||
- **Передача данных** - props drilling или контекст для глубокой структуры
|
||
- **Обработка ошибок** - глобальные error boundaries
|
||
|
||
### ЭТАП 7: ИНТЕГРАЦИЯ И ТЕСТИРОВАНИЕ (1-2 дня)
|
||
|
||
#### 7.1 Обновление импортов
|
||
```typescript
|
||
// Заменить в родительских компонентах
|
||
import { OriginalComponent } from './original-component'
|
||
// на
|
||
import { ModularComponent } from './modular-component'
|
||
```
|
||
|
||
#### 7.2 Проверки качества
|
||
```bash
|
||
# TypeScript проверки
|
||
npx tsc --noEmit
|
||
|
||
# ESLint проверки
|
||
npx eslint src/components/[domain]/[component-name]
|
||
|
||
# Тестирование функциональности
|
||
npm test src/components/[domain]/[component-name]
|
||
```
|
||
|
||
#### 7.3 Performance проверки
|
||
- Проверить количество ре-рендеров с React DevTools
|
||
- Измерить bundle size до и после
|
||
- Проверить время загрузки компонента
|
||
|
||
---
|
||
|
||
## 📊 СТАНДАРТЫ КАЧЕСТВА
|
||
|
||
### 🎯 МЕТРИКИ УСПЕШНОЙ МОДУЛЯРИЗАЦИИ:
|
||
|
||
#### Размеры файлов:
|
||
- **Оркестратор** (index.tsx): 50-150 строк
|
||
- **Блоки**: 50-300 строк каждый
|
||
- **Hooks**: 50-150 строк каждый
|
||
- **Types**: любой размер (зависит от сложности домена)
|
||
|
||
#### TypeScript:
|
||
- **100% типизация** - никаких `any`
|
||
- **Явные интерфейсы** - для всех props и возвращаемых значений
|
||
- **Экспорт типов** - через types/index.ts
|
||
|
||
#### Performance:
|
||
- **React.memo** - на всех блоках
|
||
- **useCallback** - для всех обработчиков
|
||
- **useMemo** - для тяжелых вычислений
|
||
- **Bundle size** - не увеличивается >10%
|
||
|
||
#### Тестируемость:
|
||
- **Изолированные hooks** - можно тестировать отдельно
|
||
- **Мокабельные блоки** - простые props интерфейсы
|
||
- **E2E тесты** - основные пользовательские сценарии
|
||
|
||
---
|
||
|
||
## ⚠️ ТИПИЧНЫЕ ОШИБКИ И КАК ИХ ИЗБЕЖАТЬ
|
||
|
||
### ❌ ОШИБКА 1: Чрезмерное дробление
|
||
```typescript
|
||
// ПЛОХО: слишком много мелких блоков
|
||
<HeaderBlock />
|
||
<SubHeaderBlock />
|
||
<TitleBlock />
|
||
<ButtonBlock />
|
||
|
||
// ХОРОШО: логично объединенные блоки
|
||
<HeaderSection />
|
||
<ActionsSection />
|
||
```
|
||
|
||
### ❌ ОШИБКА 2: Бизнес-логика в блоках
|
||
```typescript
|
||
// ПЛОХО: API вызовы в блоке
|
||
const DataBlock = () => {
|
||
const [data, setData] = useState()
|
||
useEffect(() => {
|
||
fetchData().then(setData) // ❌ бизнес-логика в блоке
|
||
}, [])
|
||
}
|
||
|
||
// ХОРОШО: данные через props
|
||
const DataBlock = ({ data, loading }: Props) => {
|
||
if (loading) return <Loading />
|
||
return <DataView data={data} />
|
||
}
|
||
```
|
||
|
||
### ❌ ОШИБКА 3: Неоптимизированные зависимости
|
||
```typescript
|
||
// ПЛОХО: объект пересоздается каждый рендер
|
||
const handler = useCallback(() => {
|
||
doSomething(fullObject)
|
||
}, [fullObject]) // ❌ объект пересоздается
|
||
|
||
// ХОРОШО: только нужные значения
|
||
const handler = useCallback(() => {
|
||
doSomething(fullObject.id, fullObject.name)
|
||
}, [fullObject.id, fullObject.name]) // ✅ примитивы
|
||
```
|
||
|
||
### ❌ ОШИБКА 4: Состояние в неправильном месте
|
||
```typescript
|
||
// ПЛОХО: состояние остается в оркестраторе
|
||
function MainComponent() {
|
||
const [complexState, setComplexState] = useState() // ❌
|
||
return <FeatureBlock state={complexState} setState={setComplexState} />
|
||
}
|
||
|
||
// ХОРОШО: состояние инкапсулировано в hook
|
||
function MainComponent() {
|
||
const featureData = useFeature() // ✅ состояние внутри hook
|
||
return <FeatureBlock {...featureData} />
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🧪 ТЕСТИРОВАНИЕ МОДУЛЬНОЙ АРХИТЕКТУРЫ
|
||
|
||
### 📋 СТРАТЕГИЯ ТЕСТИРОВАНИЯ:
|
||
|
||
#### 1. Unit тесты для hooks
|
||
```typescript
|
||
// hooks/use[Feature].test.ts
|
||
import { renderHook, act } from '@testing-library/react'
|
||
import { use[Feature] } from './use[Feature]'
|
||
|
||
describe('use[Feature]', () => {
|
||
it('should handle action correctly', () => {
|
||
const { result } = renderHook(() => use[Feature]())
|
||
|
||
act(() => {
|
||
result.current.handleAction('test-data')
|
||
})
|
||
|
||
expect(result.current.state).toEqual(expectedState)
|
||
})
|
||
})
|
||
```
|
||
|
||
#### 2. Component тесты для блоков
|
||
```typescript
|
||
// blocks/[Feature]Block.test.tsx
|
||
import { render, screen } from '@testing-library/react'
|
||
import { [Feature]Block } from './[Feature]Block'
|
||
|
||
describe('[Feature]Block', () => {
|
||
it('should render with data', () => {
|
||
render(
|
||
<[Feature]Block
|
||
data={mockData}
|
||
onAction={mockHandler}
|
||
/>
|
||
)
|
||
|
||
expect(screen.getByText('Expected Text')).toBeInTheDocument()
|
||
})
|
||
})
|
||
```
|
||
|
||
#### 3. Integration тесты для оркестратора
|
||
```typescript
|
||
// index.test.tsx
|
||
import { render, screen, fireEvent } from '@testing-library/react'
|
||
import { [Component] } from './index'
|
||
|
||
describe('[Component]', () => {
|
||
it('should coordinate between blocks', () => {
|
||
render(<[Component] />)
|
||
|
||
fireEvent.click(screen.getByText('Action Button'))
|
||
|
||
expect(screen.getByText('Updated Content')).toBeInTheDocument()
|
||
})
|
||
})
|
||
```
|
||
|
||
---
|
||
|
||
## 📚 ПРИМЕРЫ УСПЕШНОЙ МОДУЛЯРИЗАЦИИ
|
||
|
||
### 🏆 ЭТАЛОННЫЙ ПРИМЕР: create-suppliers-supply-page
|
||
|
||
**Исходник:** 1,467 строк в одном файле
|
||
**Результат:** 6 файлов общим объемом 1,480 строк
|
||
|
||
```
|
||
create-suppliers-supply-page/
|
||
├── index.tsx (287 строк) - оркестратор
|
||
├── blocks/
|
||
│ ├── SuppliersBlock.tsx (120 строк) - выбор поставщиков
|
||
│ ├── ProductCardsBlock.tsx (180 строк) - каталог товаров
|
||
│ ├── DetailedCatalogBlock.tsx (250 строк) - детальная рецептура
|
||
│ └── CartBlock.tsx (336 строк) - корзина с расчетами
|
||
├── hooks/
|
||
│ ├── useSupplierSelection.ts (140 строк) - логика поставщиков
|
||
│ ├── useProductCatalog.ts (195 строк) - логика каталога
|
||
│ └── useSupplyCart.ts (284 строки) - логика корзины
|
||
└── types/
|
||
└── supply-creation.types.ts (384 строки) - все интерфейсы
|
||
```
|
||
|
||
**Результат:**
|
||
- ✅ 70% сокращение размера главного файла (287 vs 1467 строк)
|
||
- ✅ 100% переиспользуемость блоков
|
||
- ✅ Изолированное тестирование каждого hook
|
||
- ✅ Параллельная разработка разными разработчиками
|
||
|
||
---
|
||
|
||
## 🎯 РЕКОМЕНДАЦИИ ПО ПРИОРИТИЗАЦИИ
|
||
|
||
### 🚀 НАЧИНАТЬ С:
|
||
|
||
1. **Средней сложности** - 600-1000 строк, стабильная функциональность
|
||
2. **Четкой структурой** - явные логические блоки UI
|
||
3. **Активно используемые** - высокая ценность от улучшения
|
||
|
||
### ⏸️ ОТЛОЖИТЬ:
|
||
|
||
1. **Критичные компоненты** - с частыми изменениями в production
|
||
2. **Слишком сложные** - >2000 строк с запутанной логикой
|
||
3. **Legacy код** - планируемый к замене
|
||
|
||
### 🎯 ЗОЛОТОЕ ПРАВИЛО:
|
||
|
||
> **"Лучше сделать 3 компонента качественно, чем 10 наспех"**
|
||
|
||
Качественная модуляризация требует времени, но окупается многократно в будущем поддержке и развитии.
|
||
|
||
---
|
||
|
||
## 📞 SUPPORT И ОБРАТНАЯ СВЯЗЬ
|
||
|
||
### 🆘 ПРИ ПРОБЛЕМАХ:
|
||
|
||
1. **Технические вопросы** - консультация с архитекторами
|
||
2. **Архитектурные решения** - review дизайна решения
|
||
3. **Performance проблемы** - профилирование и оптимизация
|
||
4. **Тестирование** - помощь в написании тестов
|
||
|
||
### 📈 УЛУЧШЕНИЕ ПРОЦЕССА:
|
||
|
||
- Документировать найденные паттерны
|
||
- Обновлять руководство на основе опыта
|
||
- Создавать переиспользуемые templates
|
||
- Проводить retro после каждой модуляризации
|
||
|
||
---
|
||
|
||
**Создано:** Claude Code AI
|
||
**Утверждено:** Архитектурная команда SFERA
|
||
**Версия:** 1.0
|
||
**Следующий review:** Через 3 месяца использования |