# Архитектурные решения (ADR) Формат: контекст → решение → последствия. Новое решение — новый раздел с номером; старые не переписываются, а помечаются `Заменено ADR-NNN`. --- ## ADR-001. Монорепозиторий **Контекст.** Веб, мобильное, бэкенд, тесты и CI должны жить в одном репозитории; у веба и мобильного много общего (API-клиент, логика очереди и событий, словари i18n). **Решение.** Монорепо: pnpm workspaces + Turborepo для TS-части; Go-модуль в `services/api`; Python-проект (uv) в `services/recommender`. Единый `api/openapi.yaml`. **Последствия.** Атомарные изменения контракта и клиентов в одном PR. CI использует фильтры путей, чтобы не гонять всё на каждый коммит. --- ## ADR-002. Backend API на Go **Контекст.** Нагрузка — IO (стриминг, NFS, БД), десятки одновременных слушателей; важна простота деплоя и низкое потребление памяти. **Решение.** Go 1.23+: роутер `chi`, `oapi-codegen` (strict server), `pgx` + `sqlc`, миграции `goose`, фоновые задачи `River` (очередь в Postgres), логирование `log/slog`, конфиг через env (`caarlos0/env`). **Последствия.** Один статический бинарник на сервис, быстрые холодные старты, тайпсейф SQL. ML-код в Go не пишем — для этого отдельный сервис (ADR-003). --- ## ADR-003. Рекомендации и аудиоанализ — отдельный Python-сервис **Контекст.** Нужны: аудиоанализ (Essentia и её предобученные модели), коллаборативная фильтрация (implicit ALS), learning-to-rank (LightGBM). Экосистема есть только в Python. **Решение.** `services/recommender`: FastAPI (онлайн-инференс), отдельный процесс `analyzer` (тот же образ), обучение по расписанию (APScheduler внутри сервиса). Python 3.12, `uv`, `numpy`, `implicit`, `lightgbm`, `essentia-tensorflow`, `psycopg`, `redis`. **Последствия.** Два языка на бэкенде. Граница чёткая: Python не обслуживает пользователей напрямую, только внутренние вызовы `api`. При падении сервиса работает fallback в `api`. ⚠️ Предобученные модели Essentia распространяются под CC BY-NC-SA 4.0 — допустимо для частного некоммерческого self-hosted использования. При коммерческом использовании нужно заменить модели. --- ## ADR-004. Мобильное приложение — React Native (Expo), только Android **Контекст.** Только Android, самообновление APK из релизов, фоновое воспроизведение, офлайн, анимированная кнопка волны. Рассматривались Flutter и React Native. **Решение.** React Native + Expo SDK (prebuild / dev client, не Expo Go), `expo-router`, `react-native-track-player` (ExoPlayer под капотом, MediaSession, уведомление, кэш), `@shopify/react-native-skia` + `react-native-reanimated` для волны, `expo-sqlite` для офлайн-данных, `expo-file-system` для загрузок. **Почему не Flutter.** Flutter не хуже по возможностям, но React Native даёт общий TypeScript-код с вебом: API-клиент, типы, логику очереди / событий / офлайн-синхронизации, словари i18n, дизайн-токены. Для команды из одного-двух человек это существенно сокращает объём работы. **Последствия.** iOS не поддерживается (самообновление в обход стора там невозможно); при необходимости iOS добавляется позже через TestFlight без смены стека. --- ## ADR-005. Раздача аудио через nginx X-Accel-Redirect **Контекст.** Нужна перемотка (HTTP Range), высокая пропускная способность, минимум нагрузки на бэкенд. **Решение.** Клиент обращается к `api`, `api` проверяет права и подпись ссылки и отвечает заголовком `X-Accel-Redirect` на `internal` location nginx. Файл отдаёт nginx. **Последствия.** Go не держит долгих соединений со стримами. Тома библиотеки и кэша транскодов должны быть смонтированы и в `api`, и в `nginx` по одинаковым путям. --- ## ADR-006. Подписанные ссылки на стрим **Контекст.** `