added AGENTS.md and docs in root dir
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
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
This commit is contained in:
1 parent
2f891c9503
commit
da9e17f45f
7 files changed
+728
-7
No files matched your search
@@ -0,0 +1,88 @@
|
||||
# Единый протокол синхронизации
|
||||
|
||||
Все клиенты (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`) и этот файл.
|
||||
Reference in new issue
Block a user