CI / Rust (fmt / clippy / test / build) (push) Successful in 1m41s
CI / HTTP/JSON reference + SQLite persistence (push) Successful in 1m16s
CI / Rust (fmt / clippy / test / build) (pull_request) Successful in 47s
CI / HTTP/JSON reference + SQLite persistence (pull_request) Successful in 1m34s
The Go implementation is preserved in the v1.0.0 tag. Its observable behaviour stays the API reference: responses recorded from the Go server live in tools/compatibility_expected.txt, and tools/compatibility.py now compares the Rust server against them (--update re-records after an intentional change). The Go SQLite schema is frozen in tests/fixtures/go_schema_v1.sql for the existing-database test. Move the web client from internal/httpapi/web to web/, drop the Go CI job and Go version pin, and update README, AGENTS.md and docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
113 lines
4.4 KiB
Markdown
113 lines
4.4 KiB
Markdown
# Архитектура BalanceServer
|
|
|
|
## Роль сервера
|
|
|
|
BalanceServer — backend для синхронизации личных финансов между устройствами.
|
|
|
|
Поток данных:
|
|
|
|
```text
|
|
iOS/macOS/watchOS ─┐
|
|
├── HTTPS/JSON API ──> BalanceServer ──> Database
|
|
Android ───────────┘
|
|
```
|
|
|
|
Сервер отвечает за:
|
|
- регистрацию и авторизацию;
|
|
- пользовательские сессии;
|
|
- хранение финансовых изменений;
|
|
- push/pull synchronization;
|
|
- глобальную последовательность изменений;
|
|
- удаление через tombstones;
|
|
- выдачу изменений начиная с cursor.
|
|
|
|
## Исходный код
|
|
|
|
Production runtime — один Rust-процесс: `src/main.rs`, `runtime.rs`, `config.rs`, `database.rs`, `auth.rs`, `sync.rs`, `server.rs`, `go_json.rs`. SQLite bundled через rusqlite; HTTP обслуживается axum/hyper. `runtime.rs` сохраняет socket deadlines Go (header 5s, read 15s, write 30s, idle 60s) и ограничивает graceful shutdown десятью секундами. `go_json.rs` сохраняет особенности encoding/json и исходные payload bytes. Статические файлы из `web/` встроены при компиляции.
|
|
|
|
Исходная Go-реализация удалена после v1.0.0 (исходники остались в теге `v1.0.0`). Её наблюдаемое поведение зафиксировано в `tools/compatibility_expected.txt`, а SQLite-схема — в `tests/fixtures/go_schema_v1.sql`; Rust сверяется с обоими в CI.
|
|
|
|
Конкретные границы package/handler/service/repository нужно сохранять при рефакторинге: перенос кода между слоями не должен менять внешний API.
|
|
|
|
## HTTP layer
|
|
|
|
HTTP layer принимает JSON requests от мобильных клиентов.
|
|
|
|
Каждый handler должен:
|
|
1. проверить method/content type;
|
|
2. аутентифицировать пользователя, если endpoint защищён;
|
|
3. валидировать input;
|
|
4. вызвать domain/storage logic;
|
|
5. вернуть стабильный JSON response.
|
|
|
|
## Domain / sync layer
|
|
|
|
Sync layer является наиболее критичной частью backend.
|
|
|
|
Он должен разделять:
|
|
- изменения, созданные текущим клиентом;
|
|
- изменения, которые нужно вернуть клиенту;
|
|
- удалённые записи;
|
|
- cursor/sequence.
|
|
|
|
Ключевая модель:
|
|
|
|
```text
|
|
client cursor
|
|
│
|
|
▼
|
|
server change log
|
|
│
|
|
├── sequence 101
|
|
├── sequence 102
|
|
├── sequence 103
|
|
└── ...
|
|
```
|
|
|
|
Клиент может запросить изменения после своего cursor и затем сохранить новый cursor.
|
|
|
|
## Persistence
|
|
|
|
Хранилище должно сохранять как минимум:
|
|
- user/account identity;
|
|
- credentials/session metadata;
|
|
- financial records;
|
|
- change metadata;
|
|
- deletion/tombstone metadata;
|
|
- monotonically increasing sequence, если оно является частью текущего sync protocol.
|
|
|
|
Конкретные entities и schema документированы в `docs/DATA_MODEL.md`.
|
|
|
|
## Security boundaries
|
|
|
|
```text
|
|
Internet
|
|
│
|
|
▼
|
|
HTTP server
|
|
│
|
|
├── authentication
|
|
├── authorization
|
|
├── input validation
|
|
│
|
|
▼
|
|
domain/sync
|
|
│
|
|
▼
|
|
database
|
|
```
|
|
|
|
Нельзя позволять клиенту выбирать произвольный `user_id` для чужих records.
|
|
|
|
## Совместимость клиентов
|
|
|
|
Apple и Android реализации должны видеть одну и ту же семантику данных.
|
|
|
|
Особенно важно, чтобы:
|
|
- enum values были стабильными;
|
|
- даты имели единый формат;
|
|
- UUID/string IDs не менялись;
|
|
- `deleted` semantics были одинаковыми;
|
|
- cursor/sequence были монотонными;
|
|
- conflict resolution совпадал с клиентской логикой.
|