Files
QuoteForge/LOCAL_FIRST_INTEGRATION.md
Mikhail Chusavitin fa0f5e321d refactor: rename binary from quoteforge to qfs
- Rename cmd/server to cmd/qfs for shorter binary name
- Update all documentation references (README, CLAUDE.md, etc.)
- Update build commands to output bin/qfs
- Binary name now matches directory name

Usage:
  go run ./cmd/qfs              # Development
  go build -o bin/qfs ./cmd/qfs # Production

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 10:55:14 +03:00

6.1 KiB
Raw Blame History

Local-First Architecture Integration Guide

Overview

QuoteForge теперь поддерживает local-first архитектуру: приложение ВСЕГДА работает с SQLite (localdb), MariaDB используется только для синхронизации.

Реализованные компоненты

1. Конвертеры моделей (internal/localdb/converters.go)

Конвертеры между MariaDB и SQLite моделями:

  • ConfigurationToLocal() / LocalToConfiguration()
  • PricelistToLocal() / LocalToPricelist()
  • ComponentToLocal() / LocalToComponent()

2. LocalDB методы (internal/localdb/localdb.go)

Добавлены методы для работы с pending changes:

  • MarkChangesSynced(ids []int64) - помечает изменения как синхронизированные
  • GetPendingCount() - возвращает количество несинхронизированных изменений

3. Sync Service расширения (internal/services/sync/service.go)

Новые методы:

  • SyncPricelistsIfNeeded() - проверяет и скачивает новые прайслисты при необходимости
  • PushPendingChanges() - отправляет все pending changes на сервер
  • pushSingleChange() - обрабатывает один pending change
  • pushConfigurationCreate/Update/Delete() - специфичные методы для конфигураций

ВАЖНО: Конструктор изменен - теперь требует ConfigurationRepository:

syncService := sync.NewService(pricelistRepo, configRepo, local)

4. LocalConfigurationService (internal/services/local_configuration.go)

Новый сервис для работы с конфигурациями в local-first режиме:

  • Все операции CRUD работают через SQLite
  • Автоматически добавляет изменения в pending_changes
  • При создании конфигурации (если online) проверяет новые прайслисты
localConfigService := services.NewLocalConfigurationService(
    localDB,
    syncService,
    quoteService,
    isOnlineFunc,
)

5. Sync Handler расширения (internal/handlers/sync.go)

Новые endpoints:

  • POST /api/sync/push - отправить pending changes на сервер
  • GET /api/sync/pending/count - получить количество pending changes
  • GET /api/sync/pending - получить список pending changes

Интеграция

Шаг 1: Обновить main.go

// В cmd/qfs/main.go
syncService := sync.NewService(pricelistRepo, configRepo, local)

// Создать isOnline функцию
isOnlineFunc := func() bool {
    sqlDB, err := db.DB()
    if err != nil {
        return false
    }
    return sqlDB.Ping() == nil
}

// Создать LocalConfigurationService
localConfigService := services.NewLocalConfigurationService(
    local,
    syncService,
    quoteService,
    isOnlineFunc,
)

Шаг 2: Обновить ConfigurationHandler

Заменить ConfigurationService на LocalConfigurationService в handlers:

// Было:
configHandler := handlers.NewConfigurationHandler(configService, exportService)

// Стало:
configHandler := handlers.NewConfigurationHandler(localConfigService, exportService)

Шаг 3: Добавить endpoints для sync

В роутере добавить:

syncGroup := router.Group("/api/sync")
{
    syncGroup.POST("/push", syncHandler.PushPendingChanges)
    syncGroup.GET("/pending/count", syncHandler.GetPendingCount)
    syncGroup.GET("/pending", syncHandler.GetPendingChanges)
}

Как это работает

Создание конфигурации

  1. Пользователь создает конфигурацию
  2. LocalConfigurationService.Create():
    • Если online → SyncPricelistsIfNeeded() проверяет новые прайслисты
    • Сохраняет конфигурацию в SQLite
    • Добавляет в pending_changes с operation="create"
  3. Конфигурация доступна локально сразу

Синхронизация с сервером

Manual sync:

POST /api/sync/push

Background sync (TODO):

  • Периодический worker вызывает syncService.PushPendingChanges()
  • Проверяет online статус
  • Отправляет все pending changes на сервер
  • Удаляет успешно синхронизированные записи

Offline режим

  1. Все операции работают нормально через SQLite
  2. Изменения копятся в pending_changes
  3. При восстановлении соединения автоматически синхронизируются

Pending Changes Queue

Таблица pending_changes:

type PendingChange struct {
    ID         int64     // Auto-increment
    EntityType string    // "configuration", "project", "specification"
    EntityUUID string    // UUID сущности
    Operation  string    // "create", "update", "delete"
    Payload    string    // JSON snapshot сущности
    CreatedAt  time.Time
    Attempts   int       // Счетчик попыток синхронизации
    LastError  string    // Последняя ошибка синхронизации
}

TODO для Phase 2.5

  • Background sync worker (автоматическая синхронизация каждые N минут)
  • Conflict resolution (при конфликтах обновления)
  • UI: pending counter в header
  • UI: manual sync button
  • UI: conflict alerts
  • Retry logic для failed pending changes
  • RefreshPrices для local mode (через local_components)

Testing

# Compile
go build ./cmd/qfs

# Run
./quoteforge

# Check pending changes
curl http://localhost:8080/api/sync/pending/count

# Manual sync
curl -X POST http://localhost:8080/api/sync/push