5.1 KiB
AGENTS.md
Проект
Balance Android — нативный Android-клиент приложения Balance для учёта личных финансов. Он повторяет доменную модель iOS/macOS/watchOS-клиентов и синхронизируется с тем же self-hosted BalanceServer.
Стек:
- Kotlin 2.3.21
- Jetpack Compose
- Room
- Coroutines / Flow
- WorkManager
- Android Keystore
- minSdk 26 / compileSdk 36
- Java 17 bytecode
Структура
BalanceAndroid/
├── app/src/main/java/com/example/balanceandroid/
│ ├── data/ # Room entities, DAO, финансовая математика
│ ├── sync/ # HTTP API, session, preferences, sync repository/worker
│ ├── ui/ # Compose UI и theme
│ ├── MainActivity.kt
│ ├── MainViewModel.kt
│ └── BalanceApplication.kt
├── app/src/test/ # unit tests
├── app/src/main/res/
└── docs/
Правила
Data layer
- Все persisted финансовые сущности находятся в
data/. - Доступ к Room выполняется через
FinanceDao. - Не помещай HTTP или Compose-логику в DAO.
- Изменение Room entities требует проверки schema/migration.
updatedAt— timestamp версии записи и обязателен для syncable entities.
Финансовая логика
FinanceMath является единым источником правил расчёта.
- balance = income − expense;
- balance adjustments влияют на общий баланс;
- adjustments исключаются из периодической аналитики;
- spending группируется по
categoryName.
При изменении правил обновляй FinanceMathTest.
Удаления
Для синхронизируемых объектов нельзя использовать только прямой DELETE.
Используй DAO-методы:
deleteTransactiondeleteCategorydeleteBudget
Они сначала создают запись в deletion_journal, затем удаляют объект.
Категории
Категория хранится snapshot-полями в transactions/budgets. Поэтому при переименовании пользовательской категории текущая реализация обновляет связанные операции и бюджеты. Сохраняй это поведение.
Sync
SyncRepository — единственная точка orchestration синхронизации.
ServerApiотвечает только за HTTP/auth.ServerPreferencesотвечает за URL, cursor и sync metadata.SecureSessionStoreотвечает за зашифрованную сессию.SyncWorkerзапускает периодическую sync-задачу.
Не делай network calls непосредственно из Compose UI.
Security
- Access/refresh tokens не должны попадать в обычные SharedPreferences.
- Не логируй tokens/passwords/Authorization.
- Session хранится через AES/GCM с ключом Android Keystore.
- Для удалённого сервера нужен HTTPS.
- HTTP разрешён только локальным адресам, предусмотренным
ServerPreferences. - Не добавляй credentials или production secrets в репозиторий.
Compose
- UI state идёт из
MainViewModel. - Данные Room наблюдаются через Flow.
- Общие компоненты —
ui/Components.kt; feature screens —ui/BalanceRoot.ktиui/Editors.kt. - Не переносить бизнес-логику в composables.
Sync invariants
При изменении syncable entity:
- сохранить новое значение;
- обновить
updatedAt; - дождаться следующего sync.
При удалении:
- создать deletion journal;
- удалить локальную запись;
- отправить tombstone на сервер.
Remote change применяется только если его updatedAt новее локального.
Тестирование
Минимум:
./gradlew test
Перед PR желательно:
./gradlew clean test assembleDebug
Если менялся sync/data layer, добавь unit tests для новых инвариантов.
PR checklist
- Изменён правильный слой.
- Финансовая логика остаётся в
FinanceMath. - Изменения persisted schema учтены.
updatedAtобновляется.- Deletes создают tombstones.
- Sync не вызывается напрямую из UI.
- Нет секретов в репозитории/logcat.
- Unit tests проходят.
- README/docs обновлены при изменении setup/API.