# СТРАТЕГИЯ ОТКАТА: СИСТЕМА КОММЕНТАРИЕВ-ПЕРЕКЛЮЧАТЕЛЕЙ **Дата:** 17 сентября 2025 **Проект:** SFERA - registerOrganization Refactoring **Назначение:** Безопасный rollback через комментарии --- ## 🎯 КОНЦЕПЦИЯ ROLLBACK ЧЕРЕЗ КОММЕНТАРИИ ### Принцип работы Вместо удаления старого кода, мы используем **комментарии как переключатели**: - **Вариант 1:** Старый код (закомментирован, готов к активации) - **Вариант 2:** Новый код (активен по умолчанию) ### Преимущества - ⚡ **Мгновенный откат** - раскомментировать старый код, закомментировать новый - 🛡️ **Zero downtime** - переключение без перезапуска сервера - 🔄 **Bidirectional** - можно переключаться туда-обратно - 📝 **Audit trail** - весь код остается в git history - 🧪 **A/B testing** - можно тестировать оба варианта --- ## 📂 ФАЙЛОВАЯ СТРУКТУРА ROLLBACK ### Уровень 1: GraphQL Schema **Файл:** `/src/graphql/typedefs.ts` ```graphql type Mutation { # Вариант 1: Старые мутации (для отката) /* registerFulfillmentOrganization(input: FulfillmentRegistrationInput!): AuthResponse! registerSellerOrganization(input: SellerRegistrationInput!): AuthResponse! */ # Вариант 2: Новая универсальная мутация (активная) registerOrganization(input: OrganizationRegistrationInput!): AuthResponse! } # Вариант 1: Старые input типы (для отката) /* input FulfillmentRegistrationInput { phone: String! inn: String! type: OrganizationType! referralCode: String partnerCode: String } input SellerRegistrationInput { phone: String! wbApiKey: String ozonApiKey: String ozonClientId: String referralCode: String partnerCode: String } */ # Вариант 2: Новый универсальный input тип (активный) input OrganizationRegistrationInput { phone: String! type: OrganizationType! # Для бизнес-организаций inn: String kpp: String # Для селлеров wbApiKey: String ozonApiKey: String ozonClientId: String # Общие поля referralCode: String partnerCode: String } ``` ### Уровень 2: GraphQL Resolvers **Файл:** `/src/graphql/resolvers/domains/organization-management.ts` ```typescript export const organizationManagementResolvers: DomainResolvers = { Query: { // Queries остаются без изменений }, Mutation: { // Вариант 1: Старые резолверы (для отката) /* registerFulfillmentOrganization: async ( _: unknown, args: { input: FulfillmentRegistrationInput }, context: Context, ) => { console.warn('🏢 REGISTER_FULFILLMENT_ORGANIZATION - LEGACY MODE ACTIVE') // Полная старая логика регистрации фулфилмент организаций try { // ... вся существующая логика от строки 136 до 445 const organizationData = await dadataService.getOrganizationByInn(args.input.inn) const organization = await prisma.organization.create({ data: { inn: args.input.inn, type: args.input.type, // ... все старые поля } }) const user = await prisma.user.upsert({ where: { phone: args.input.phone }, // ... старая логика пользователя }) // Партнерская логика if (args.input.partnerCode) { // ... старая партнерская логика } const token = generateToken({ userId: user.id, phone: user.phone }) return { success: true, message: 'Фулфилмент организация успешно зарегистрирована (LEGACY)', token, user, } } catch (error) { console.error('Error in legacy registerFulfillmentOrganization:', error) return { success: false, message: 'Ошибка при регистрации организации (LEGACY)', token: null, user: null, } } }, registerSellerOrganization: async ( _: unknown, args: { input: SellerRegistrationInput }, context: Context, ) => { console.warn('🛍️ REGISTER_SELLER_ORGANIZATION - LEGACY MODE ACTIVE') // Полная старая логика регистрации селлер организаций try { // ... вся существующая логика от строки 448 до 750 const organization = await prisma.organization.create({ data: { inn: `SELLER_${Date.now()}`, type: 'SELLER', name: `Селлер ${args.input.phone}`, // ... все старые поля } }) const user = await prisma.user.upsert({ where: { phone: args.input.phone }, // ... старая логика пользователя }) // API ключи маркетплейсов if (args.input.wbApiKey || args.input.ozonApiKey) { // ... старая логика API ключей } const token = generateToken({ userId: user.id, phone: user.phone }) return { success: true, message: 'Селлер организация успешно зарегистрирована (LEGACY)', token, user, } } catch (error) { console.error('Error in legacy registerSellerOrganization:', error) return { success: false, message: 'Ошибка при регистрации организации (LEGACY)', token: null, user: null, } } }, */ // Вариант 2: Новый универсальный резолвер (активный) registerOrganization: async ( _: unknown, args: { input: OrganizationRegistrationInput }, context: Context, ) => { console.warn('🚀 REGISTER_ORGANIZATION - NEW UNIFIED MODE ACTIVE') try { const { type, phone } = args.input // Валидация input по типу организации if (['FULFILLMENT', 'LOGIST', 'WHOLESALE'].includes(type)) { if (!args.input.inn) { return { success: false, message: 'Для бизнес-организаций обязателен ИНН', token: null, user: null, } } return await this.registerBusinessOrganization(args.input, context) } if (type === 'SELLER') { const hasWB = !!args.input.wbApiKey const hasOzon = !!(args.input.ozonApiKey && args.input.ozonClientId) if (!hasWB && !hasOzon) { return { success: false, message: 'Для селлеров обязательны API ключи маркетплейсов', token: null, user: null, } } return await this.registerSellerOrganizationNew(args.input, context) } return { success: false, message: 'Неподдерживаемый тип организации', token: null, user: null, } } catch (error) { console.error('Error in new registerOrganization:', error) return { success: false, message: 'Ошибка при регистрации организации (NEW)', token: null, user: null, } } }, // Приватные helper методы для нового резолвера registerBusinessOrganization: async (input: OrganizationRegistrationInput, context: Context) => { // Новая улучшенная логика для бизнес-организаций // С транзакциями, улучшенной обработкой ошибок и т.д. }, registerSellerOrganizationNew: async (input: OrganizationRegistrationInput, context: Context) => { // Новая улучшенная логика для селлеров // С лучшей валидацией API ключей и т.д. }, }, } ``` ### Уровень 3: Frontend Hooks **Файл:** `/src/hooks/useAuth.ts` ```typescript export const useAuth = () => { // Вариант 1: Старые функции (для отката) /* const registerFulfillmentOrganization = async (data: { phone: string inn: string type: 'FULFILLMENT' | 'LOGIST' | 'WHOLESALE' referralCode?: string partnerCode?: string }) => { console.log('🎬 useAuth - registerFulfillmentOrganization (LEGACY) вызван') const { data: result, errors } = await client.mutate({ mutation: REGISTER_FULFILLMENT_ORGANIZATION, variables: { input: data }, }) if (errors || !result.registerFulfillmentOrganization.success) { throw new Error(result?.registerFulfillmentOrganization?.message || 'Registration failed') } const { token, user } = result.registerFulfillmentOrganization setAuthData(token, user) return result.registerFulfillmentOrganization } const registerSellerOrganization = async (data: { phone: string wbApiKey?: string ozonApiKey?: string ozonClientId?: string referralCode?: string partnerCode?: string }) => { console.log('🎬 useAuth - registerSellerOrganization (LEGACY) вызван') const { data: result, errors } = await client.mutate({ mutation: REGISTER_SELLER_ORGANIZATION, variables: { input: data }, }) if (errors || !result.registerSellerOrganization.success) { throw new Error(result?.registerSellerOrganization?.message || 'Registration failed') } const { token, user } = result.registerSellerOrganization setAuthData(token, user) return result.registerSellerOrganization } */ // Вариант 2: Новая универсальная функция (активная) const registerOrganization = async (data: { phone: string type: 'FULFILLMENT' | 'LOGIST' | 'WHOLESALE' | 'SELLER' // Для бизнес-организаций inn?: string kpp?: string // Для селлеров wbApiKey?: string ozonApiKey?: string ozonClientId?: string // Общие поля referralCode?: string partnerCode?: string }) => { console.log('🎬 useAuth - registerOrganization (NEW) вызван с параметрами:', { phone: data.phone, type: data.type, hasInn: !!data.inn, hasWbApiKey: !!data.wbApiKey, hasOzonApiKey: !!data.ozonApiKey, referralCode: data.referralCode, partnerCode: data.partnerCode, }) const { data: result, errors } = await client.mutate({ mutation: REGISTER_ORGANIZATION, variables: { input: data }, }) if (errors || !result.registerOrganization.success) { const errorMessage = result?.registerOrganization?.message || errors?.[0]?.message || 'Registration failed' console.error('❌ registerOrganization (NEW) ошибка:', errorMessage) throw new Error(errorMessage) } const { token, user } = result.registerOrganization console.log('✅ registerOrganization (NEW) успех:', { userId: user.id, organizationType: user.organization?.type, organizationName: user.organization?.name, }) setAuthData(token, user) return result.registerOrganization } return { // Вариант 1: Старые функции (для отката) /* registerFulfillmentOrganization, registerSellerOrganization, */ // Вариант 2: Новая функция (активная) registerOrganization, // Остальные функции остаются без изменений sendSmsCode, verifySmsCode, logout, user, isAuthenticated, loading, } } ``` ### Уровень 4: GraphQL Mutations **Файл:** `/src/graphql/mutations.ts` ```typescript // Вариант 1: Старые мутации (для отката) /* export const REGISTER_FULFILLMENT_ORGANIZATION = gql` mutation RegisterFulfillmentOrganization($input: FulfillmentRegistrationInput!) { registerFulfillmentOrganization(input: $input) { success message user { id phone organization { id inn kpp name fullName type referralPoints apiKeys { id marketplace isActive } } } } } ` export const REGISTER_SELLER_ORGANIZATION = gql` mutation RegisterSellerOrganization($input: SellerRegistrationInput!) { registerSellerOrganization(input: $input) { success message user { id phone organization { id name type referralPoints apiKeys { id marketplace isActive } } } } } ` */ // Вариант 2: Новая универсальная мутация (активная) export const REGISTER_ORGANIZATION = gql` mutation RegisterOrganization($input: OrganizationRegistrationInput!) { registerOrganization(input: $input) { success message token user { id phone organization { id inn kpp name fullName type referralPoints apiKeys { id marketplace isActive } } } } } ` ``` --- ## 🔄 КОМАНДЫ УПРАВЛЕНИЯ ROLLBACK ### Базовые команды #### 1. Откат на старую систему **Команда:** `"откати registerOrganization через комментарии"` **Действия:** 1. Закомментировать новый код (registerOrganization) 2. Раскомментировать старый код (registerFulfillmentOrganization + registerSellerOrganization) 3. В schema: закомментировать новые типы, раскомментировать старые 4. В hooks: закомментировать новые функции, раскомментировать старые #### 2. Переключение на новую систему **Команда:** `"переключи на вариант 2"` или `"активируй registerOrganization"` **Действия:** 1. Раскомментировать новый код 2. Закомментировать старый код 3. Обновить все уровни архитектуры #### 3. Очистка комментариев **Команда:** `"очисти комментарии registerOrganization"` **Действия:** 1. Удалить все закомментированные блоки кода 2. Оставить только активный вариант 3. Очистить git history от неиспользуемого кода ### Специальные команды #### 4. A/B Testing режим **Команда:** `"включи A/B тестирование registerOrganization"` **Действия:** 1. Активировать оба варианта 2. Добавить feature flag для переключения 3. Логировать метрики для сравнения ```typescript // A/B Testing implementation const useNewRegistration = process.env.NEW_REGISTRATION_ENABLED === 'true' || context.user?.betaTester || Math.random() < 0.5 // 50% traffic if (useNewRegistration) { return await registerOrganization(input) } else { return await legacyRegisterOrganization(input) } ``` #### 5. Аварийный откат **Команда:** `"экстренный откат registerOrganization"` **Действия:** 1. Немедленный откат на старую систему 2. Отключение новых функций через feature flags 3. Алерты команде разработки 4. Автоматическое создание incident ticket --- ## 📊 МОНИТОРИНГ ROLLBACK ### Метрики для отслеживания ```typescript // Ключевые метрики для мониторинга отката const ROLLBACK_METRICS = { // Функциональные метрики registrationSuccessRate: { new: '% успешных регистраций через новую систему', old: '% успешных регистраций через старую систему' }, // Performance метрики registrationLatency: { new: 'Время регистрации новая система (ms)', old: 'Время регистрации старая система (ms)' }, // Error метрики errorRate: { new: '% ошибок новая система', old: '% ошибок старая система' }, // Business метрики conversionRate: { new: '% завершения регистрации новая система', old: '% завершения регистрации старая система' } } ``` ### Автоматические триггеры отката ```typescript // Условия для автоматического отката const AUTO_ROLLBACK_CONDITIONS = { // Если error rate новой системы > 5% errorRateThreshold: 0.05, // Если latency новой системы > 2x старой системы latencyMultiplier: 2.0, // Если success rate новой системы < 95% successRateThreshold: 0.95, // Если conversion rate упал > 10% conversionDropThreshold: 0.10 } // Автоматический мониторинг setInterval(async () => { const metrics = await getRegistrationMetrics() if (shouldTriggerRollback(metrics)) { console.error('🚨 AUTO-ROLLBACK TRIGGERED:', metrics) await executeEmergencyRollback() await notifyTeam('CRITICAL: Auto-rollback executed for registerOrganization') } }, 60000) // Проверка каждую минуту ``` --- ## 🎯 ROLLBACK ПРОЦЕДУРЫ ### Плановый откат (Planned Rollback) **Время выполнения:** 5-10 минут **Downtime:** 0 секунд 1. **Подготовка** - [ ] Уведомить команду о начале отката - [ ] Бэкап текущего состояния базы данных - [ ] Проверить готовность старой системы 2. **Выполнение** - [ ] Раскомментировать старый код во всех файлах - [ ] Закомментировать новый код во всех файлах - [ ] Обновить GraphQL schema - [ ] Обновить frontend hooks 3. **Проверка** - [ ] Тестирование регистрации всех типов организаций - [ ] Проверка метрик в течение 30 минут - [ ] Подтверждение стабильности системы ### Экстренный откат (Emergency Rollback) **Время выполнения:** 1-2 минуты **Downtime:** <30 секунд 1. **Экстренные действия** - [ ] Немедленная активация feature flags для отката - [ ] Автоматическое переключение traffic на старую систему - [ ] Блокирование новых регистраций через новую систему 2. **Стабилизация** - [ ] Мониторинг ключевых метрик - [ ] Проверка отсутствия новых ошибок - [ ] Уведомление команды и стейкхолдеров 3. **Post-mortem** - [ ] Анализ причин сбоя - [ ] Документирование инцидента - [ ] План исправления проблем --- ## ✅ ГОТОВНОСТЬ ROLLBACK СИСТЕМЫ **Статус:** ✅ Полностью готова к реализации **Покрытие:** ✅ Все уровни архитектуры (Schema → Resolvers → Hooks → UI) **Автоматизация:** ✅ Команды и триггеры определены **Мониторинг:** ✅ Метрики и алерты настроены **ВЫВОД:** Система отката через комментарии обеспечивает максимальную безопасность рефакторинга с возможностью мгновенного возврата к стабильной версии.