Files
Balance/docs/SYNC_PROTOCOL.md
T
wt da9e17f45f
Android CI / Build and Test (push) Failing after 13m25s
iOS CI / Build and Test SwiftUI App (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s
added AGENTS.md and docs in root dir
2026-09-08 13:36:43 +07:00

89 lines
6.1 KiB
Markdown

# Единый протокол синхронизации
Все клиенты (iOS, macOS, watchOS, Android, web) синхронизируются с
`BalanceServer` через один и тот же HTTP(S) API и один и тот же набор
инвариантов. Это сводка контракта; детальные описания на стороне
сервера — [`BalanceServer/docs/API.md`](../BalanceServer/docs/API.md) и
[`BalanceServer/docs/SYNC.md`](../BalanceServer/docs/SYNC.md).
Клиентские реализации — [`Balance/docs/SYNC.md`](../Balance/docs/SYNC.md)
и [`BalanceAndroid/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-семантика идентична.
## Основные инварианты
1. **Cursor** — каждый клиент хранит курсор последнего успешно
применённого изменения. Курсор сохраняется только после успешного
применения соответствующего batch, не раньше.
2. **Sequence** — сервер поддерживает глобальный монотонный порядок
изменений в change stream; порядок не должен уменьшаться и должен
быть однозначным при конкурентных запросах.
3. **Push** — клиент отправляет свои изменения (`POST /v1/sync`);
сервер валидирует, проверяет владение записью (ownership) и
добавляет изменение в change stream. Повторный push (retry) не
должен создавать вторую логическую запись из одного и того же
client-change (идемпотентность).
4. **Pull** — клиент запрашивает изменения после своего курсора;
сервер никогда не возвращает чужие данные (изоляция по аккаунту) и
поддерживает постраничный pull с продолжением от нового курсора.
5. **Conflict resolution** — last-write-wins по `updatedAt`
(`syncUpdatedAt` на Apple, `updatedAt` на Android/сервере). Сервер —
единственный источник правды в этом сравнении, поэтому не должен
произвольно перезаписывать `updatedAt` при каждом чтении.
6. **Удаления (tombstones)** — удаление сущности не является простым
`DELETE`; оно создаёт запись изменения вида
`{ "entity": ..., "id": ..., "deleted": true, "updatedAt": ... }`,
которая проходит через тот же change stream, что и обычные
изменения. На Apple это `SyncTombstone`/`modelContext.deleteForSync`,
на Android — `deletion_journal` через DAO-методы `deleteTransaction` /
`deleteCategory` / `deleteBudget`.
7. **Batch** — Apple-клиент отправляет батчи до 100 изменений и может
делить крупные payload'ы; сервер не должен предполагать, что все
изменения аккаунта приходят одним запросом. Android должен
оставаться совместимым с этим же батчингом.
8. **Reset/full sync** — если клиент теряет курсор, сервер должен
поддерживать предусмотренный API механизм полного pull или новый
baseline-курсор. Неизвестный/просроченный курсор нельзя молча
трактовать как актуальный.
## Синхронизируемые сущности
- **Транзакция** (доход/расход, сумма, дата, заметка, snapshot
категории);
- **Пользовательская категория** (имя, иконка/emoji, цвет);
- **Месячный бюджет** (категория, месяц, лимит);
- **Tombstone / deletion journal** запись для удалений.
Подробное пополевое сопоставление — [`docs/DATA_MODEL.md`](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`) и этот файл.