# 🔄 Схема работы системы обработки ошибок ## Поток обработки ошибок ```mermaid flowchart TD Start([Пользователь выполняет действие]) --> API[API Request via axios] API --> Success{Успех?} Success -->|Да| Cache[Обновить кэш
GET запросы] Cache --> ShowSuccess[Показать успех
если указано] ShowSuccess --> End([Завершено]) Success -->|Нет| ErrorType{Тип ошибки?} ErrorType -->|Network| CheckRetry1[Проверить retry
GET: 3x, POST: 1x] ErrorType -->|Timeout| CheckRetry2[Проверить retry
GET: 2x, POST: 1x] ErrorType -->|Server 5xx| CheckRetry3[Проверить retry
GET: 2x] ErrorType -->|Client 4xx| NoRetry1[Нет retry] ErrorType -->|Unknown| NoRetry2[Нет retry] CheckRetry1 --> CanRetry{Есть попытки?} CheckRetry2 --> CanRetry CheckRetry3 --> CanRetry CanRetry -->|Да| Backoff[Exponential Backoff
300ms → 600ms → 1200ms] Backoff --> API CanRetry -->|Нет| LogError[Логировать ошибку
если критичная] NoRetry1 --> LogError NoRetry2 --> LogError LogError --> FormatMessage[Форматировать
user-friendly сообщение] FormatMessage --> GetAction[Получить рекомендацию
по устранению] GetAction --> ShowNotification[Показать уведомление
Error/Warning] ShowNotification --> RejectPromise[Отклонить Promise] RejectPromise --> ComponentHandler[Обработка в компоненте] ComponentHandler --> ErrorBoundary{Ошибка
рендеринга?} ErrorBoundary -->|Да| ShowErrorPage[Показать страницу
ошибки] ErrorBoundary -->|Нет| UserHandler[useErrorHandler] ShowErrorPage --> Recovery[Кнопки восстановления] UserHandler --> HandleLogic[Локальная обработка] Recovery --> End HandleLogic --> End ``` --- ## Структура компонентов ```mermaid graph TB App[App.jsx] --> EB[ErrorBoundary] EB --> Router[React Router] Router --> QueryClient[React Query] QueryClient --> Theme[Theme Provider] Theme --> Lang[Language Provider] Lang --> Toast[Toast Container] Toast --> Notify[Notify Provider] Notify --> NEH[Network Error Handler] Notify --> Layout[Main Layout] Layout --> Pages[Page Components] Pages --> useEH[useErrorHandler hook] Pages --> RB[RetryButton] Pages --> API[api.js] API --> Interceptors[Axios Interceptors] Interceptors --> ErrorHandler[apiErrorHandler.js] ErrorHandler --> Types[Error Types] ErrorHandler --> Retry[Retry Logic] ErrorHandler --> Format[Message Formatting] style EB fill:#ff6b6b style NEH fill:#51cf66 style ErrorHandler fill:#ffd43b style useEH fill:#339af0 ``` --- ## Жизненный цикл запроса с ошибкой ```mermaid sequenceDiagram participant User as Пользователь participant Comp as Компонент participant Hook as useErrorHandler participant API as api.js participant Inter as Interceptor participant Handler as errorHandler.js participant Server as Backend participant Notify as Notify System User->>Comp: Клик на кнопку Comp->>Hook: withErrorHandler(fn) Hook->>API: api.get('/data') API->>Inter: Request Interceptor Inter->>Server: HTTP Request alt Успех Server-->>Inter: 200 OK Inter-->>API: Response API-->>Hook: Data Hook-->>Notify: handleSuccess Notify-->>User: ✅ Успех else Ошибка (первая попытка) Server-->>Inter: 500 Error Inter->>Handler: getErrorType(error) Handler-->>Inter: SERVER Inter->>Handler: isRetriableError(error, 'GET') Handler-->>Inter: true Inter->>Handler: getRetryDelay(0) Handler-->>Inter: 300ms Inter->>Inter: Ждём 300ms Inter->>Server: Retry #1 alt Успех после retry Server-->>Inter: 200 OK Inter-->>API: Response API-->>Hook: Data Hook-->>Notify: handleSuccess Notify-->>User: ✅ Успех else Все попытки исчерпаны Server-->>Inter: 500 Error Inter->>Handler: formatErrorMessage(error) Handler-->>Inter: User-friendly message Inter->>Handler: getErrorAction(error) Handler-->>Inter: Recommendation Inter->>Handler: logError(error) Handler->>Handler: Log to console Handler-->>Notify: error notification Notify-->>User: ❌ Ошибка с деталями Inter-->>API: Reject Promise API-->>Hook: Error Hook->>Comp: Handle locally end end ``` --- ## Принятие решений о retry ```mermaid flowchart TD Error[Получена ошибка] --> GetType[Определить тип ошибки] GetType --> IsGET{Метод GET?} IsGET -->|Да| CheckTypeGET{Тип ошибки} CheckTypeGET -->|Network| Retry3[Max 3 попытки] CheckTypeGET -->|Timeout| Retry2[Max 2 попытки] CheckTypeGET -->|Server 5xx| Retry2b[Max 2 попытки] CheckTypeGET -->|Client 4xx| NoRetry[Нет retry] IsGET -->|Нет| IsSafe{Безопасный
метод?} IsSafe -->|PUT/DELETE| Limited[Ограниченный retry] IsSafe -->|POST/PATCH| Limited Limited --> CheckTypePOST{Тип ошибки} CheckTypePOST -->|Network| Retry1[Max 1 попытка] CheckTypePOST -->|Timeout| Retry1b[Max 1 попытка] CheckTypePOST -->|Server 5xx| NoRetry2[Нет retry
Риск дубликатов] CheckTypePOST -->|Client 4xx| NoRetry3[Нет retry] Retry3 --> CheckCount{Попытка
max?} Retry2 --> CheckCount Retry2b --> CheckCount Retry1 --> CheckCount Retry1b --> CheckCount CheckCount -->|Да| Backoff[Exponential Backoff
+ Jitter] CheckCount -->|Нет| Final[Финальная ошибка] Backoff --> DoRetry[Повторить запрос] DoRetry --> Success{Успех?} Success -->|Да| Done[✅ Завершено] Success -->|Нет| GetType NoRetry --> Final NoRetry2 --> Final NoRetry3 --> Final Final --> ShowError[Показать ошибку
пользователю] ShowError --> End([Конец]) Done --> End style Retry3 fill:#51cf66 style Retry2 fill:#51cf66 style Retry2b fill:#51cf66 style Retry1 fill:#ffd43b style Retry1b fill:#ffd43b style NoRetry fill:#ff6b6b style NoRetry2 fill:#ff6b6b style NoRetry3 fill:#ff6b6b ``` --- ## Обработка различных сценариев ### Сценарий 1: Сетевая ошибка (Network Error) ``` 🔴 Пользователь загружает список доменов ↓ ⚡ Backend не отвечает (offline) ↓ 🔄 Попытка 1: Ждём 300ms → Retry ↓ 🔄 Попытка 2: Ждём 600ms → Retry ↓ 🔄 Попытка 3: Ждём 1200ms → Retry ↓ ❌ Все попытки исчерпаны ↓ 📢 Показать: "Не удалось подключиться к серверу" ↓ 💡 Рекомендация: "Проверьте соединение с интернетом" ``` ### Сценарий 2: Таймаут (Timeout) ``` 🔴 Пользователь сохраняет большой список ↓ ⏱️ Запрос превышает 30 секунд ↓ 🔄 Попытка 1: Ждём 300ms → Retry ↓ 🔄 Попытка 2: Ждём 600ms → Retry ↓ ❌ Таймаут снова ↓ 📢 Показать: "Сервер не отвечает" ↓ 💡 Рекомендация: "Попробуйте повторить запрос позже" ``` ### Сценарий 3: Ошибка валидации (400) ``` 🔴 Пользователь добавляет невалидный домен ↓ ❌ Backend возвращает 400 Bad Request ↓ 🚫 Retry не выполняется (ошибка валидации) ↓ 📢 Показать: "Ошибка валидации данных" ↓ 💡 Рекомендация: "Проверьте правильность введённых значений" ↓ 📋 Детали: JSON с полями, которые не прошли валидацию ``` ### Сценарий 4: Конфликт ETag (409) ``` 🔴 Пользователь сохраняет изменения ↓ ⚠️ Другой пользователь изменил данные (ETag не совпадает) ↓ ❌ Backend возвращает 409 Conflict ↓ 🚫 Retry не выполняется (конфликт) ↓ 📢 Показать: "Конфликт данных" ↓ 💡 Рекомендация: "Обновите страницу и попробуйте снова" ``` ### Сценарий 5: Ошибка React компонента ``` 🔴 Ошибка в рендеринге компонента ↓ 🛡️ ErrorBoundary ловит ошибку ↓ 📝 Логирование в консоль ↓ 🎨 Показать fallback UI ↓ 🔘 Кнопки: "Попробовать снова", "Перезагрузить", "На главную" ↓ ├─ Попробовать снова → Reset state ├─ Перезагрузить → window.location.reload() └─ На главную → window.location.href = '/' ``` ### Сценарий 6: Потеря соединения ``` 🔴 Пользователь работает с приложением ↓ 📡 navigator.onLine = false (WiFi отключен) ↓ 🚨 NetworkErrorHandler детектирует событие ↓ 📢 Показать красный баннер: "Нет соединения с интернетом" ↓ ⏳ Ожидание восстановления... ↓ 📡 navigator.onLine = true (WiFi включен) ↓ ✅ Показать зелёный баннер: "Соединение восстановлено" ↓ ⏱️ Автоматически скрыть через 3 секунды ``` --- ## Интеграция компонентов ```mermaid graph LR A[Пользовательский
компонент] --> B[useErrorHandler] A --> C[RetryButton] A --> D[api.js] B --> E[handleError] B --> F[handleSuccess] B --> G[withErrorHandler] D --> H[Request
Interceptor] D --> I[Response
Interceptor] I --> J[Retry Logic] I --> K[Error
Formatting] K --> L[apiErrorHandler] L --> M[getErrorType] L --> N[formatErrorMessage] L --> O[getErrorAction] J --> P{Success?} P -->|Yes| Q[Return Data] P -->|No| K E --> R[Notify System] F --> R K --> R R --> S[Toast
Notification] style A fill:#e3f2fd style B fill:#fff3e0 style D fill:#fce4ec style L fill:#f3e5f5 style R fill:#e8f5e9 ``` --- ## Экспорт функциональности ```javascript // api.js экспортирует: export default api; // axios instance export { unwrapStd }; // utility export { getErrorType, // из errorHandler formatErrorMessage, // из errorHandler getErrorDetails, // из errorHandler getErrorAction, // из errorHandler isCriticalError // из errorHandler }; // useErrorHandler.js экспортирует: export { useErrorHandler }; // основной хук export { useAsyncError }; // для useEffect // ErrorBoundary.jsx экспортирует: export default ErrorBoundary; // класс компонент // NetworkErrorHandler.jsx экспортирует: export default NetworkErrorHandler; // функциональный компонент // RetryButton.jsx экспортирует: export default RetryButton; // функциональный компонент ``` --- ## Когда использовать что? | Сценарий | Решение | Пример | |----------|---------|--------| | **Простая загрузка данных** | `withErrorHandler` | Список доменов | | **Сложная логика с обработкой** | `handleError` вручную | Форма с валидацией | | **Кнопка действия** | `RetryButton` | Сохранить, Удалить | | **React Query** | `onError` callback | useQuery с handleError | | **Ошибка рендеринга** | Автоматически | ErrorBoundary ловит | | **Offline состояние** | Автоматически | NetworkErrorHandler | --- ## Преимущества архитектуры ✅ **Модульность** - каждый компонент решает свою задачу ✅ **Переиспользуемость** - хуки и компоненты можно использовать везде ✅ **Тестируемость** - изолированная логика легко тестируется ✅ **Расширяемость** - легко добавить новые типы ошибок ✅ **Производительность** - минимальный overhead, умный кэш ✅ **User Experience** - понятные сообщения, автоматический retry ✅ **Developer Experience** - простой API, хорошая документация --- ## Метрики успеха - 📉 Количество необработанных ошибок: **0%** - 📈 Успешных retry: **~60-70%** (зависит от типа ошибки) - ⏱️ Среднее время до показа ошибки: **300-1200ms** (retry) - 🎯 User satisfaction: **Значительно выше** (понятные сообщения) - 🐛 Bugs из-за ошибок: **Минимум** (ErrorBoundary + логирование)