Files
sfera-new/docs/presentation-layer/DATA_SYNCHRONIZATION_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

287 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🔄 ПРАВИЛА СИНХРОНИЗАЦИИ ДАННЫХ МЕЖДУ КОМПОНЕНТАМИ
> **Цель:** Обеспечить консистентность данных между различными компонентами системы SFERA
## 📋 **ОСНОВНЫЕ ПРИНЦИПЫ**
### 1. **ЕДИНЫЙ ИСТОЧНИК ИСТИНЫ (Single Source of Truth)**
-**Один резолвер = одна таблица БД** для одного типа данных
-**V2 система** - приоритет над legacy кодом
-**Никогда не дублируйте** одни данные в разных резолверах
### 2. **КОНСИСТЕНТНАЯ CACHE POLICY**
```typescript
// ✅ ПРАВИЛЬНО - одинаковая policy для связанных данных
const query1 = useQuery(QUERY_A, { fetchPolicy: 'cache-and-network' })
const query2 = useQuery(QUERY_B, { fetchPolicy: 'cache-and-network' })
// ❌ НЕПРАВИЛЬНО - разные policies создают рассинхронизацию
const query1 = useQuery(QUERY_A, { fetchPolicy: 'cache-and-network' })
const query2 = useQuery(QUERY_B, {}) // default cache-first policy
```
### 3. **ОБЯЗАТЕЛЬНЫЕ FETCH POLICIES**
#### **Для связанных компонентов:**
```typescript
fetchPolicy: 'cache-and-network', // Всегда актуальные данные
pollInterval: 30000, // Автообновление каждые 30 сек
```
#### **Для dashboard/статистики:**
```typescript
fetchPolicy: 'cache-and-network', // Синхронизация с основными данными
pollInterval: 30000, // Регулярное обновление
errorPolicy: 'all', // Показывать частичные данные при ошибках
```
---
## 🏢 **ПРАВИЛА ДЛЯ ФУЛФИЛМЕНТА**
### **ТАБЛИЦЫ V2 СИСТЕМЫ**
- **`fulfillmentConsumableInventory`** - складские остатки расходников
- **`fulfillmentConsumableSupplyOrder`** - поставки расходников
- **`fulfillmentInventoryV2`** - общий инвентарь фулфилмента
### **СВЯЗАННЫЕ КОМПОНЕНТЫ**
```typescript
// Все эти компоненты должны использовать одинаковые данные:
1. Главный dashboard склада (/fulfillment-warehouse)
- Карточка "РАСХОДНИКИ ФУЛФИЛМЕНТА"
2. Подраздел расходников (/fulfillment-warehouse/supplies)
- Карточка "ОСТАТОК"
- Главная таблица поставок
3. Раздел услуг (/services)
- Вкладка "Расходники"
4. История поставок
- Раскрывающиеся детали каждого товара
```
### **СИНХРОНИЗИРОВАННЫЕ ПОЛЯ**
```typescript
interface SynchronizedData {
currentStock: number // Текущий остаток (одинаковый везде)
totalReceived: number // Общее количество поступлений
productId: string // ID для группировки истории поставок
fulfillmentCenterId: string // ID фулфилмент-центра
}
```
---
## 📊 **ПРАВИЛА ДЛЯ СТАТИСТИЧЕСКИХ КОМПОНЕНТОВ**
### **1. АРХИТЕКТУРА MASTER-DETAIL**
```typescript
// MASTER компонент (главный раздел)
const masterStats = useQuery(GET_WAREHOUSE_STATS, {
fetchPolicy: 'cache-and-network',
pollInterval: 30000,
})
// DETAIL компонент (подраздел)
const detailStats = useQuery(GET_DETAILED_SUPPLIES, {
fetchPolicy: 'cache-and-network', // ← ОБЯЗАТЕЛЬНО то же самое!
pollInterval: 30000, // ← ОБЯЗАТЕЛЬНО то же самое!
})
// Вычисление должно давать одинаковые результаты
const masterValue = masterStats.data?.fulfillmentSupplies?.current
const detailValue = detailStats.data?.supplies?.reduce((sum, s) => sum + s.currentStock, 0)
// masterValue === detailValue ← ОБЯЗАТЕЛЬНО!
```
### **2. ПРАВИЛА АГРЕГАЦИИ**
```typescript
// ✅ ПРАВИЛЬНО - агрегация из одного источника данных
const totalStock = inventoryItems.reduce((sum, item) => sum + item.currentStock, 0)
// ❌ НЕПРАВИЛЬНО - агрегация из разных источников
const totalStock1 = supplies.reduce((sum, s) => sum + s.currentStock, 0) // источник A
const totalStock2 = inventory.reduce((sum, i) => sum + i.remainingStock, 0) // источник B
```
---
## 🔗 **ПРАВИЛА СВЯЗЫВАНИЯ ДАННЫХ**
### **ОБЯЗАТЕЛЬНЫЕ ПОЛЯ ДЛЯ СВЯЗИ**
```typescript
// В GraphQL схемах ОБЯЗАТЕЛЬНО указывать связующие поля:
type Supply {
id: ID!
productId: ID! // ← Для фильтрации истории поставок
fulfillmentCenterId: ID // ← Для привязки к фулфилменту
}
type SupplyOrder {
id: ID!
fulfillmentCenterId: ID! // ← Для фильтрации по фулфилменту
items: [SupplyOrderItem!]!
}
type SupplyOrderItem {
productId: ID! // ← Для связи с inventory
requestedQuantity: Int!
receivedQuantity: Int! // ← Для подсчёта остатков
}
```
### **ФИЛЬТРАЦИЯ ПО СВЯЗАННЫМ ДАННЫМ**
```typescript
// ✅ ПРАВИЛЬНО - фильтрация по ID
const getSupplyHistory = (supply: Supply) => {
return allDeliveries.filter((delivery) => delivery.items?.some((item) => item.productId === supply.productId))
}
// ❌ НЕПРАВИЛЬНО - фильтрация по названию (может быть дубли)
const getSupplyHistory = (supply: Supply) => {
return allDeliveries.filter((delivery) => delivery.name === supply.name && delivery.category === supply.category)
}
```
---
## ⚡ **ПРАВИЛА REAL-TIME ОБНОВЛЕНИЙ**
### **АВТОМАТИЧЕСКАЯ СИНХРОНИЗАЦИЯ**
```typescript
// Компоненты должны обновляться автоматически при изменениях
// 1. При приёме поставки на склад
prisma.fulfillmentConsumableInventory.update({
where: { id: itemId },
data: { currentStock: newStock },
})
// → Автоматически обновятся: статистика, таблица, услуги
// 2. При отгрузке товаров
prisma.fulfillmentConsumableInventory.update({
where: { id: itemId },
data: { currentStock: { decrement: shippedQuantity } },
})
// → Автоматически обновятся все связанные компоненты
```
### **POLLING ИНТЕРВАЛЫ**
```typescript
pollInterval: 30000 // 30 сек - для статистики и dashboard
pollInterval: 60000 // 1 мин - для списков и таблиц
pollInterval: 120000 // 2 мин - для отчётов и архивных данных
```
---
## 🚨 **АНТИ-ПАТТЕРНЫ И ТИПИЧНЫЕ ОШИБКИ**
### **❌ НИКОГДА НЕ ДЕЛАЙТЕ:**
#### **1. Разные источники для одних данных**
```typescript
// ❌ ПЛОХО
const stats = useQuery(GET_OLD_SUPPLIES) // legacy таблица
const details = useQuery(GET_NEW_SUPPLIES) // V2 таблица
```
#### **2. Разные cache policies для связанных данных**
```typescript
// ❌ ПЛОХО
const master = useQuery(QUERY_A, { fetchPolicy: 'cache-and-network' })
const detail = useQuery(QUERY_B, {}) // default cache-first
```
#### **3. Ручная синхронизация через состояние**
```typescript
// ❌ ПЛОХО
const [manualSync, setManualSync] = useState(0)
useEffect(() => {
// Попытка ручной синхронизации через state
}, [manualSync])
```
#### **4. Группировка по нестабильным полям**
```typescript
// ❌ ПЛОХО - названия могут изменяться
supplies.filter((s) => s.name === targetName)
// ✅ ХОРОШО - ID стабильны
supplies.filter((s) => s.productId === targetProductId)
```
---
## 🎯 **ЧЕКЛИСТ СИНХРОНИЗАЦИИ**
### **При создании связанных компонентов:**
- [ ] Используют одну таблицу БД как источник данных
- [ ] Одинаковые `fetchPolicy` настройки
- [ ] Одинаковые `pollInterval` значения
- [ ] Связь через стабильные ID поля
- [ ] Одинаковая логика агрегации/фильтрации
- [ ] Обработка ошибок и loading состояний
### **При тестировании синхронизации:**
- [ ] Изменение данных обновляет все связанные компоненты
- [ ] Значения в master и detail компонентах совпадают
- [ ] Нет задержек между обновлениями (< 30 сек)
- [ ] Ошибки в одном компоненте не ломают другие
---
## 📈 **МОНИТОРИНГ СИНХРОНИЗАЦИИ**
### **DEBUG ЛОГИ**
```typescript
// Добавляйте логи для контроля синхронизации
console.warn('SYNC CHECK:', {
masterValue: masterData?.totalStock,
detailValue: detailData?.reduce(sum, 0),
timestamp: new Date().toISOString(),
source: 'fulfillmentInventorySync',
})
```
### **УВЕДОМЛЕНИЯ О РАССИНХРОНИЗАЦИИ**
```typescript
// Проверка консистентности данных
if (Math.abs(masterValue - detailValue) > 0) {
console.error('🚨 DATA SYNCHRONIZATION ERROR', {
master: masterValue,
detail: detailValue,
component: 'FulfillmentStats',
})
// Отправка уведомления разработчикам
toast.error('Обнаружена рассинхронизация данных')
}
```
**Следование этим правилам обеспечит надёжную синхронизацию данных между всеми компонентами системы!** 🚀