6.1 KiB
Единый протокол синхронизации
Все клиенты (iOS, macOS, watchOS, Android, web) синхронизируются с
BalanceServer через один и тот же HTTP(S) API и один и тот же набор
инвариантов. Это сводка контракта; детальные описания на стороне
сервера — BalanceServer/docs/API.md и
BalanceServer/docs/SYNC.md.
Клиентские реализации — Balance/docs/SYNC.md
и BalanceAndroid/docs/SYNC.md.
Эндпоинты
| Метод | Путь | Назначение |
|---|---|---|
GET |
/health |
Health check |
POST |
/v1/auth/register |
Создание аккаунта |
POST |
/v1/auth/login |
Вход, выдача access/refresh токенов |
POST |
/v1/auth/refresh |
Ротация токенов |
POST |
/v1/auth/logout |
Отзыв refresh-токена |
GET |
/v1/me |
Текущий аккаунт |
POST |
/v1/sync |
Push и pull изменений |
Нативные клиенты используют Authorization: Bearer <access-token>.
Web-клиент использует HttpOnly/SameSite=Strict cookie-сессию —
это единственное отличие транспорта, сама sync-семантика идентична.
Основные инварианты
- Cursor — каждый клиент хранит курсор последнего успешно применённого изменения. Курсор сохраняется только после успешного применения соответствующего batch, не раньше.
- Sequence — сервер поддерживает глобальный монотонный порядок изменений в change stream; порядок не должен уменьшаться и должен быть однозначным при конкурентных запросах.
- Push — клиент отправляет свои изменения (
POST /v1/sync); сервер валидирует, проверяет владение записью (ownership) и добавляет изменение в change stream. Повторный push (retry) не должен создавать вторую логическую запись из одного и того же client-change (идемпотентность). - Pull — клиент запрашивает изменения после своего курсора; сервер никогда не возвращает чужие данные (изоляция по аккаунту) и поддерживает постраничный pull с продолжением от нового курсора.
- Conflict resolution — last-write-wins по
updatedAt(syncUpdatedAtна Apple,updatedAtна Android/сервере). Сервер — единственный источник правды в этом сравнении, поэтому не должен произвольно перезаписыватьupdatedAtпри каждом чтении. - Удаления (tombstones) — удаление сущности не является простым
DELETE; оно создаёт запись изменения вида{ "entity": ..., "id": ..., "deleted": true, "updatedAt": ... }, которая проходит через тот же change stream, что и обычные изменения. На Apple этоSyncTombstone/modelContext.deleteForSync, на Android —deletion_journalчерез DAO-методыdeleteTransaction/deleteCategory/deleteBudget. - Batch — Apple-клиент отправляет батчи до 100 изменений и может делить крупные payload'ы; сервер не должен предполагать, что все изменения аккаунта приходят одним запросом. Android должен оставаться совместимым с этим же батчингом.
- Reset/full sync — если клиент теряет курсор, сервер должен поддерживать предусмотренный API механизм полного pull или новый baseline-курсор. Неизвестный/просроченный курсор нельзя молча трактовать как актуальный.
Синхронизируемые сущности
- Транзакция (доход/расход, сумма, дата, заметка, snapshot категории);
- Пользовательская категория (имя, иконка/emoji, цвет);
- Месячный бюджет (категория, месяц, лимит);
- Tombstone / deletion journal запись для удалений.
Подробное пополевое сопоставление — docs/DATA_MODEL.md.
Что нельзя менять без координации
Любое из следующих изменений — breaking change для всех клиентов одновременно, даже если каждый подпроект по отдельности компилируется и проходит собственные тесты:
- имена/типы JSON-полей в payload'ах
/v1/syncи auth-эндпоинтов; - HTTP-методы или пути эндпоинтов;
- семантика курсора/sequence;
- правило разрешения конфликтов (last-write-wins по
updatedAt); - формат tombstone-записи.
При таком изменении обнови одновременно: BalanceServer (API +
хендлеры + docs/API.md/docs/SYNC.md), Balance/Shared/ServerSync.swift
(+ Balance/docs/SYNC.md), BalanceAndroid/.../sync/*
(+ BalanceAndroid/docs/SYNC.md) и этот файл.