
Обновления: - CLAUDE.md - обновлены правила взаимодействия с акцентом на модульную архитектуру - docs/development/MODULAR_ARCHITECTURE_PATTERN.md - обновлена документация паттерна модульной архитектуры 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
15 KiB
🏗️ Паттерн модульной архитектуры для React компонентов
⚠️ ОФИЦИАЛЬНЫЙ СТАНДАРТ АРХИТЕКТУРЫ SFERA
Этот документ описывает ОБЯЗАТЕЛЬНУЮ модульную архитектуру по умолчанию для большинства компонентов в системе SFERA.
🎯 СТАТУС АРХИТЕКТУРНОГО СТАНДАРТА
🟢 ПРИНЯТЫЕ СТАНДАРТЫ (готовы к использованию):
- ✅ create-suppliers-supply-page.tsx → Модульная архитектура (ФАЗА 1-4 завершены)
- ✅ direct-supply-creation.tsx → Модульная архитектура (ЭТАПЫ 1-5 завершены)
📋 ПРАВИЛА ПРИМЕНЕНИЯ:
✅ ВСЕГДА МОДУЛЬНАЯ АРХИТЕКТУРА (без исключений):
1. Страницы и основные разделы:
- ВСЕ файлы
page.tsx
в/app/**/
- ВСЕ дашборды любых типов
- ВСЕ разделы управления (supplies, employees, products, settings)
- ВСЕ wizard/multi-step компоненты
2. Формы (даже простые!):
- ЛЮБАЯ форма создания/редактирования сущности
- Формы с >3 полей
- Многошаговые формы
- Формы с динамическими полями
- Почему: формы ВСЕГДА разрастаются (валидация, автозаполнение, зависимые поля)
3. Таблицы и списки:
- ЛЮБАЯ таблица с данными из БД
- Таблицы с фильтрацией/сортировкой
- Таблицы с inline-редактированием
- Списки с действиями (approve/reject/delete)
- Почему: всегда добавятся фильтры, сортировка, экспорт, bulk-операции
4. Комплексные компоненты:
- Чаты/мессенджеры
- Календари/планировщики
- Графики/аналитика
- Файловые менеджеры
- Корзины/калькуляторы
❌ ИСКЛЮЧЕНИЯ (только эти остаются простыми):
- Чистые UI из Radix/shadcn - уже оптимизированы
- Stateless компоненты < 50 строк без логики
- Чистые Layout компоненты (Header/Footer)
- Utility компоненты (ErrorBoundary, LoadingState)
- Простые модалки подтверждения (Да/Нет)
🎯 КЛЮЧЕВОЕ ПРАВИЛО SFERA
"Если сомневаешься - делай модульным. Лучше иметь избыточную структуру папок, чем 2000-строчный спагетти-код через месяц."
📏 Критерии принятия решения:
// Алгоритм принятия решения о модульности
function shouldUseModularArchitecture(component) {
// Автоматически ДА
if (
component.type === 'page' ||
component.type === 'dashboard' ||
component.type === 'form' ||
component.type === 'table' ||
component.expectedSize > 300
) {
return true
}
// Проверяем сложность
const complexityScore =
component.stateVariables + // количество useState
component.apiCalls * 2 + // API вызовы весят больше
component.formFields + // поля форм
(component.hasBusinessLogic ? 3 : 0) // бизнес-логика
return complexityScore >= 5
}
Кандидаты для рефакторинга (legacy компонентов):
- Размер: >800 строк кода
- Сложность: Множественные состояния и бизнес-логика
- Многообразие UI: Различные секции интерфейса
- Частые изменения: Активно развивающиеся компоненты
Большие компоненты в SFERA (кандидаты):
3052 строки - timesheet-demo.tsx
2012 строк - fulfillment-warehouse-dashboard.tsx
1654 строки - navigation-demo.tsx
1637 строк - direct-supply-creation.tsx ✅ ЗАВЕРШЕН (модульная архитектура)
1563 строки - user-settings.tsx
1523 строки - advertising-tab.tsx
1304 строки - wb-product-cards.tsx
📁 Универсальная структура модуля
src/components/[domain]/[component-name]/
├── index.tsx # Главный компонент-оркестратор
├── blocks/ # UI блок-компоненты
│ ├── [Feature]Block.tsx # Функциональные блоки
│ ├── [Section]Block.tsx # Секции интерфейса
│ └── [Action]Block.tsx # Блоки действий
├── hooks/ # Бизнес-логика
│ ├── use[Domain][Action].ts # Специфичная логика
│ ├── use[Entity]Management.ts # CRUD операции
│ └── use[Feature]State.ts # Управление состоянием
├── types/ # TypeScript типы
│ └── index.ts # Все интерфейсы модуля
└── constants/ # Константы (опционально)
└── index.ts # Конфигурация и моки
🔄 Процесс рефакторинга
ЭТАП 1: Анализ и планирование
-
Выделить основные секции UI
- Определить логические блоки интерфейса
- Найти повторяющиеся паттерны
-
Проанализировать состояния
- Сгруппировать связанные useState
- Выделить логику управления состоянием
-
Найти бизнес-логику
- useEffect с API вызовами
- Обработчики форм и событий
- Вычисляемые значения
ЭТАП 2: Создание типов
// types/index.ts
export interface [Entity] {
id: string
// ... поля сущности
}
export interface [Component]Props {
// ... props компонента
}
export interface [Block]Props {
// ... props блока
}
ЭТАП 3: Извлечение hooks
// hooks/use[Feature].ts
export function use[Feature]() {
const [state, setState] = useState()
const handleAction = useCallback(() => {
// бизнес-логика
}, [dependencies])
return {
state,
handleAction,
// ... другие возвращаемые значения
}
}
ЭТАП 4: Создание блок-компонентов
// blocks/[Feature]Block.tsx
export const [Feature]Block = React.memo(function [Feature]Block({
// ... props
}: [Feature]BlockProps) {
return (
<div className="...">
{/* UI блока */}
</div>
)
})
ЭТАП 5: Интеграция в главном компоненте
// index.tsx
export function [Component]() {
// Подключение hooks
const featureA = useFeatureA()
const featureB = useFeatureB()
// Обработчики с useCallback
const handleAction = useCallback((data) => {
// координация между features
}, [featureA.method, featureB.method])
return (
<div>
<FeatureABlock {...featureA} onAction={handleAction} />
<FeatureBBlock {...featureB} onAction={handleAction} />
</div>
)
}
⚡ Оптимизация производительности
Мемоизация компонентов
// Все блоки обернуть в React.memo
export const MyBlock = React.memo(function MyBlock(props) {
// ...
})
// Главный компонент: useCallback для обработчиков
const handleEvent = useCallback(
(data) => {
// логика
},
[dependency1, dependency2],
)
Управление ререндерами
// В hooks: useMemo для тяжелых вычислений
const expensiveValue = useMemo(() => {
return heavyCalculation(data)
}, [data])
// Оптимизация зависимостей useCallback
const optimizedHandler = useCallback(
(item) => {
// вместо передачи всего массива
// передавать только нужные методы
},
[addItem, removeItem],
) // не [items, addItem, removeItem]
🧪 Тестирование модульной архитектуры
Unit тесты для hooks
import { renderHook, act } from '@testing-library/react'
import { useFeature } from '../hooks/useFeature'
describe('useFeature', () => {
it('should handle action correctly', () => {
const { result } = renderHook(() => useFeature())
act(() => {
result.current.handleAction('test')
})
expect(result.current.state).toEqual(expectedState)
})
})
Component тесты для блоков
import { render, screen } from '@testing-library/react'
import { FeatureBlock } from '../blocks/FeatureBlock'
describe('FeatureBlock', () => {
it('should render with props', () => {
render(<FeatureBlock data={mockData} onAction={mockHandler} />)
expect(screen.getByText('Expected Text')).toBeInTheDocument()
})
})
📊 Метрики успеха рефакторинга
Количественные метрики
- Размер главного файла: ↓ на 70-85%
- Количество модулей: ↑ в 5-10 раз
- Время компиляции: ↓ на 90%+
- Переиспользуемые компоненты: ↑ с 0 до N
Качественные улучшения
- ✅ Читаемость и понимание кода
- ✅ Простота добавления новых фич
- ✅ Изолированное тестирование
- ✅ Переиспользование в других местах
- ✅ Параллельная разработка команды
🚨 Частые ошибки и как их избежать
❌ Чрезмерное дробление
// ПЛОХО: слишком много мелких блоков
<HeaderBlock />
<SubHeaderBlock />
<TitleBlock />
<ButtonBlock />
// ХОРОШО: логичные функциональные блоки
<HeaderSection />
<ActionsSection />
❌ Плохие зависимости useCallback
// ПЛОХО: передача всего объекта
const handler = useCallback(() => {
doSomething(fullObject)
}, [fullObject]) // объект пересоздается каждый рендер
// ХОРОШО: передача только нужных значений
const handler = useCallback(() => {
doSomething(fullObject.id, fullObject.name)
}, [fullObject.id, fullObject.name])
❌ Неправильное разделение состояния
// ПЛОХО: состояние остается в главном компоненте
function MainComponent() {
const [complexState, setComplexState] = useState() // управляется извне
return <FeatureBlock state={complexState} setState={setComplexState} />
}
// ХОРОШО: состояние инкапсулировано в hook
function MainComponent() {
const featureData = useFeature() // управляется внутри hook
return <FeatureBlock {...featureData} />
}
📋 Чек-лист рефакторинга
Перед началом
- Компонент относится к категории: page, form, table, dashboard
- ИЛИ ожидаемый размер >300 строк
- ИЛИ сложность по алгоритму ≥5 баллов
- ИЛИ есть несколько логических секций UI
- ИЛИ множественные useState и useEffect
Планирование
- Определены основные UI блоки (3-6 штук)
- Выделена бизнес-логика для hooks (2-5 штук)
- Созданы TypeScript интерфейсы
- План поэтапного рефакторинга готов
Реализация
- Создана структура папок
- Extracted типы в types/index.ts
- Hooks реализованы и протестированы
- Блоки созданы с React.memo
- Главный компонент интегрирует все части
- Старый файл удален
Оптимизация
- Все обработчики используют useCallback
- Зависимости useCallback оптимизированы
- Тяжелые вычисления в useMemo
- Компонент работает без ошибок
Документация
- README для модуля создан
- Примеры использования hooks
- Документация props для блоков
- Архитектурная диаграмма
🎯 Заключение
Модульная архитектура по умолчанию значительно улучшает качество кода, скорость разработки и поддержки в долгосрочной перспективе. В SFERA мы применяем принцип "лучше переструктурировать сразу, чем рефакторить потом".
🎯 Практические примеры применения:
Модульные (новые стандарты):
/app/suppliers/create/page.tsx
→ CreateSupplierPage/EmployeeManagementTable
→ EmployeeManagement/SupplyOrderForm
→ SupplyOrderForm/ProductCatalog
→ ProductCatalog/UserSettingsForm
→ UserSettingsForm/
Простые (исключения):
Logo.tsx
LoadingDots.tsx
ConfirmDialog.tsx
PriceDisplay.tsx
Основано на: Успешном рефакторинге create-suppliers-supply-page.tsx (1,467→240 строк)
Обновлено: Синхронизировано с правилами CLAUDE.md
Автор паттерна: Claude Code
Дата: Август 2025