Files
sfera-new/docs/api-layer/GRAPHQL_CACHE_RULES.md
Veronika Smirnova 121a4dece1 docs: создать правила для синхронизации данных, layout и статистических компонентов
- DATA_SYNCHRONIZATION_RULES.md - правила синхронизации между компонентами
- GRAPHQL_CACHE_RULES.md - настройки кеширования и fetchPolicy
- CSS_LAYOUT_SCROLL_RULES.md - решение проблем с overflow и scroll
- STATISTICAL_COMPONENTS_RULES.md - правила Master-Detail архитектуры

Документация основана на исправлениях в кабинете фулфилмента

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 12:29:00 +03:00

13 KiB
Raw Blame History

🔄 ПРАВИЛА КЕШИРОВАНИЯ GRAPHQL И FETCHPOLICY

Цель: Обеспечить корректное кеширование GraphQL данных и предотвратить проблемы синхронизации между компонентами

📋 ОСНОВНЫЕ ПРИНЦИПЫ КЕШИРОВАНИЯ

1. СИНХРОНИЗАЦИЯ СВЯЗАННЫХ КОМПОНЕНТОВ

// ✅ ПРАВИЛЬНО - одинаковые настройки для связанных данных
const masterComponent = useQuery(GET_WAREHOUSE_STATS, {
  fetchPolicy: 'cache-and-network',
  pollInterval: 30000,
  errorPolicy: 'all',
})

const detailComponent = useQuery(GET_SUPPLIES_DETAILS, {
  fetchPolicy: 'cache-and-network', // ← ОБЯЗАТЕЛЬНО то же самое!
  pollInterval: 30000, // ← ОБЯЗАТЕЛЬНО то же самое!
  errorPolicy: 'all', // ← ОБЯЗАТЕЛЬНО то же самое!
})
// ❌ НЕПРАВИЛЬНО - разные настройки создают рассинхронизацию
const masterComponent = useQuery(GET_WAREHOUSE_STATS, {
  fetchPolicy: 'cache-and-network',
})

const detailComponent = useQuery(GET_SUPPLIES_DETAILS, {
  // default fetchPolicy: 'cache-first' - СОЗДАЁТ ПРОБЛЕМУ!
})

2. ОБЯЗАТЕЛЬНЫЕ FETCH POLICIES ПО ТИПАМ КОМПОНЕНТОВ

Dashboard и Статистика

useQuery(DASHBOARD_QUERY, {
  fetchPolicy: 'cache-and-network', // Всегда актуальные данные
  pollInterval: 30000, // Обновление каждые 30 сек
  errorPolicy: 'all', // Показывать частичные данные при ошибках
})

Связанные компоненты (Master-Detail)

// Все компоненты одной функциональности ДОЛЖНЫ иметь идентичные настройки
const sharedQueryOptions = {
  fetchPolicy: 'cache-and-network' as const,
  pollInterval: 30000,
  errorPolicy: 'all' as const,
}

// Применение во всех связанных компонентах
useQuery(MASTER_QUERY, sharedQueryOptions)
useQuery(DETAIL_QUERY, sharedQueryOptions)
useQuery(STATS_QUERY, sharedQueryOptions)

Списки и таблицы

useQuery(LIST_QUERY, {
  fetchPolicy: 'cache-and-network', // Актуальные данные
  pollInterval: 60000, // 1 минута для списков
  notifyOnNetworkStatusChange: true, // Показывать статус загрузки
})

Редко изменяющиеся данные

useQuery(STATIC_DATA_QUERY, {
  fetchPolicy: 'cache-first', // Кеш приоритетен
  pollInterval: 300000, // 5 минут
  errorPolicy: 'ignore', // Не показывать ошибки для статичных данных
})

🏢 СПЕЦИФИЧЕСКИЕ ПРАВИЛА ДЛЯ ФУЛФИЛМЕНТА

ГРУППЫ СИНХРОНИЗИРОВАННЫХ КОМПОНЕНТОВ

Группа 1: Складская статистика

// Все эти компоненты ДОЛЖНЫ использовать одинаковые настройки:
const warehouseStatsOptions = {
  fetchPolicy: 'cache-and-network' as const,
  pollInterval: 30000,
  errorPolicy: 'all' as const,
}

// 1. Главный dashboard (/fulfillment-warehouse)
useQuery(GET_WAREHOUSE_STATS, warehouseStatsOptions)

// 2. Подраздел расходников (/fulfillment-warehouse/supplies)
useQuery(GET_SUPPLIES_STATS, warehouseStatsOptions)

// 3. Раздел услуг (/services) - вкладка "Расходники"
useQuery(GET_SERVICE_SUPPLIES, warehouseStatsOptions)

Группа 2: История поставок

const suppliesHistoryOptions = {
  fetchPolicy: 'cache-and-network' as const,
  pollInterval: 30000,
  errorPolicy: 'all' as const,
}

// 1. Основные поставки (главная таблица)
useQuery(GET_MY_FULFILLMENT_SUPPLIES, suppliesHistoryOptions)

// 2. Детали поставок (раскрывающиеся строки)
useQuery(GET_MY_FULFILLMENT_CONSUMABLE_SUPPLIES, suppliesHistoryOptions)

🚨 КРИТИЧЕСКИЕ АНТИ-ПАТТЕРНЫ

НИКОГДА НЕ ДЕЛАЙТЕ:

1. Разные fetchPolicy для связанных данных

// ❌ СОЗДАЁТ РАССИНХРОНИЗАЦИЮ
const warehouse = useQuery(GET_WAREHOUSE, { fetchPolicy: 'cache-and-network' })
const supplies = useQuery(GET_SUPPLIES, {}) // default cache-first

// Результат: warehouse показывает новые данные, supplies - старые

2. Разные pollInterval для одной функциональности

// ❌ СОЗДАЁТ НЕСОГЛАСОВАННОСТЬ
const stats = useQuery(GET_STATS, { pollInterval: 30000 })
const details = useQuery(GET_DETAILS, { pollInterval: 60000 })

// Результат: stats обновляется чаще details - значения не совпадают

3. Смешивание cache-first и cache-and-network

// ❌ КЛАССИЧЕСКАЯ ОШИБКА
const mainData = useQuery(MAIN_QUERY, { fetchPolicy: 'cache-and-network' })
const relatedData = useQuery(RELATED_QUERY, { fetchPolicy: 'cache-first' })

// Результат: mainData актуальные, relatedData устаревшие

4. Игнорирование ошибок в критических компонентах

// ❌ СКРЫВАЕТ ПРОБЛЕМЫ
useQuery(CRITICAL_DATA_QUERY, {
  errorPolicy: 'ignore', // Не показывает ошибки когда нужно!
})

📊 ПРАВИЛА ПО ТИПАМ ДАННЫХ

СТАТИСТИКА И DASHBOARD

const dashboardOptions = {
  fetchPolicy: 'cache-and-network', // Всегда актуальные данные
  pollInterval: 30000, // 30 сек - критическая актуальность
  errorPolicy: 'all', // Показывать частичные данные
  notifyOnNetworkStatusChange: true, // Показывать индикатор обновления
}

ИНВЕНТАРЬ И ОСТАТКИ

const inventoryOptions = {
  fetchPolicy: 'cache-and-network', // Остатки должны быть точными
  pollInterval: 30000, // Частое обновление
  errorPolicy: 'all', // Критические данные
}

ИСТОРИЯ И ЛОГИ

const historyOptions = {
  fetchPolicy: 'cache-first', // История не меняется часто
  pollInterval: 120000, // 2 минуты достаточно
  errorPolicy: 'all', // Показывать что есть
}

СПРАВОЧНИКИ И КАТАЛОГИ

const catalogOptions = {
  fetchPolicy: 'cache-first', // Справочники стабильны
  pollInterval: 300000, // 5 минут
  errorPolicy: 'ignore', // Не критично
}

🔧 ПРАКТИЧЕСКИЕ ПРИМЕРЫ ИСПРАВЛЕНИЙ

ПРИМЕР 1: Рассинхронизация карточек статистики

Проблема:

// Главный dashboard
const warehouseStats = useQuery(GET_WAREHOUSE_STATS, {
  fetchPolicy: 'cache-and-network',
})

// Подраздел supplies
const suppliesStats = useQuery(GET_SUPPLIES_STATS, {
  // default fetchPolicy: 'cache-first' - ПРОБЛЕМА!
})

// Результат: карточка "РАСХОДНИКИ ФУЛФИЛМЕНТА" = 130, карточка "ОСТАТОК" = 160

Решение:

// Общие настройки для всех статистических компонентов
const statsOptions = {
  fetchPolicy: 'cache-and-network' as const,
  pollInterval: 30000,
  errorPolicy: 'all' as const,
}

// Применяем везде одинаково
const warehouseStats = useQuery(GET_WAREHOUSE_STATS, statsOptions)
const suppliesStats = useQuery(GET_SUPPLIES_STATS, statsOptions)

ПРИМЕР 2: История поставок не синхронизируется с основной таблицей

Проблема:

// Основная таблица
const supplies = useQuery(GET_SUPPLIES, { fetchPolicy: 'cache-and-network' })

// История в раскрывающихся строках
const history = useQuery(GET_SUPPLY_HISTORY, {}) // default cache-first

// Результат: основная таблица обновилась, история показывает старые данные

Решение:

const syncedOptions = {
  fetchPolicy: 'cache-and-network',
  pollInterval: 30000,
}

const supplies = useQuery(GET_SUPPLIES, syncedOptions)
const history = useQuery(GET_SUPPLY_HISTORY, syncedOptions)

🎯 ЧЕКЛИСТ НАСТРОЙКИ КЕШИРОВАНИЯ

Перед релизом компонента:

  • Определили группу связанных компонентов
  • Выбрали единую fetchPolicy для группы
  • Установили одинаковые pollInterval значения
  • Настроили errorPolicy в соответствии с критичностью
  • Протестировали синхронизацию данных

При обнаружении рассинхронизации:

  • Проверили fetchPolicy всех связанных запросов
  • Сравнили pollInterval значения
  • Убедились в использовании одних таблиц БД
  • Проверили последовательность обновления данных

📈 МОНИТОРИНГ И ОТЛАДКА

DEBUG ЛОГИ ДЛЯ КЕШИРОВАНИЯ

// Добавляйте в компоненты для контроля кеширования
useQuery(QUERY, {
  fetchPolicy: 'cache-and-network',
  onCompleted: (data) => {
    console.log('CACHE DEBUG:', {
      query: 'QUERY_NAME',
      timestamp: new Date().toISOString(),
      dataLength: data?.items?.length,
      source: 'network/cache',
    })
  },
  onError: (error) => {
    console.error('CACHE ERROR:', {
      query: 'QUERY_NAME',
      error: error.message,
      networkError: error.networkError?.message,
    })
  },
})

ПРОВЕРКА СИНХРОНИЗАЦИИ

// Контрольные точки для проверки консистентности
useEffect(() => {
  if (masterData && detailData) {
    const masterValue = masterData.total
    const detailValue = detailData.reduce((sum, item) => sum + item.value, 0)

    if (Math.abs(masterValue - detailValue) > 0) {
      console.error('🚨 CACHE SYNC ERROR:', {
        component: 'ComponentName',
        master: masterValue,
        detail: detailValue,
        diff: masterValue - detailValue,
      })
    }
  }
}, [masterData, detailData])

🚀 РЕКОМЕНДАЦИИ ПО ПРОИЗВОДИТЕЛЬНОСТИ

ОПТИМИЗАЦИЯ POLLING

// Адаптивные интервалы в зависимости от активности пользователя
const useAdaptivePolling = () => {
  const [isActive, setIsActive] = useState(true)

  useEffect(() => {
    const handleVisibilityChange = () => {
      setIsActive(!document.hidden)
    }

    document.addEventListener('visibilitychange', handleVisibilityChange)
    return () => document.removeEventListener('visibilitychange', handleVisibilityChange)
  }, [])

  return {
    pollInterval: isActive ? 30000 : 120000, // Реже обновляем неактивные вкладки
  }
}

УСЛОВНОЕ КЕШИРОВАНИЕ

// Разные стратегии для разных сценариев
const getCachePolicy = (dataType: string) => {
  switch (dataType) {
    case 'critical-stats':
      return { fetchPolicy: 'cache-and-network', pollInterval: 30000 }
    case 'user-lists':
      return { fetchPolicy: 'cache-and-network', pollInterval: 60000 }
    case 'reference-data':
      return { fetchPolicy: 'cache-first', pollInterval: 300000 }
    default:
      return { fetchPolicy: 'cache-first' }
  }
}

Следование этим правилам обеспечит стабильное кеширование и синхронизацию GraphQL данных! 🚀