# ADR-001: Выбор бэкенд-стека **Дата:** 2026-03-20 **Статус:** Accepted **Автор:** Architect Agent (Kin pipeline, BATON-001) --- ## Контекст Baton — минималистичное PWA приложение экстренного сигнала. Бэкенд выполняет три задачи: 1. Принять POST /signal от 300-400 пользователей (возможны одновременные запросы) 2. Сохранить сигнал в SQLite 3. Отправить уведомление в Telegram-группу через Bot API Проект требует простого деплоя на один VPS, без kubernetes, без сложной инфраструктуры. Команда имеет опыт работы с Python/FastAPI (используется в проекте Kin). Рассматривались три стека: FastAPI (Python), Express/Fastify (Node.js), Go (net/http). --- ## Варианты ### Вариант A: FastAPI (Python 3.11+) **Плюсы:** - Знакомость команды (используется в Kin) - asyncio нативно - Pydantic — автоматическая валидация входных данных - `aiosqlite` или `sqlite3` через `run_in_executor` - Быстрый старт разработки **Минусы:** - В 2-3x медленнее Go по RPS - Docker образ ~200-400 MB (Python runtime + зависимости) - bcrypt блокирует event loop — нужен `run_in_executor` (решение #1004) ### Вариант B: Express/Fastify (Node.js 20+) **Плюсы:** - Единый язык с фронтендом (vanilla JS) - `better-sqlite3` — синхронный, самый быстрый SQLite биндинг для Node.js - Fastify ~24% быстрее FastAPI по RPS в независимых тестах - Docker образ ~200-300 MB **Минусы:** - Нет опыта работы в команде - `better-sqlite3` синхронный — блокирует event loop при долгих запросах (на практике приемлемо для simple INSERT) - Дополнительное переключение контекста (JS для фронта, JS для бека) ### Вариант C: Go (net/http) **Плюсы:** - Компилируется в единый статический бинарь ~8-15 MB (простейший деплой) - В 2-3x быстрее Python, ~2x быстрее Node.js - Горутины — нативный concurrency без event loop ограничений - Нет проблем с bcrypt (не блокирует горутины) - Cross-compile: `GOARCH=amd64 GOOS=linux go build` **Минусы:** - Нет опыта работы в команде - Более длительный онбординг - `modernc.org/sqlite` (CGO-free): 10-100% медленнее нативного SQLite — компромисс для кросс-компиляции --- ## Решение **Выбран Вариант A: FastAPI (Python 3.11+)** --- ## Обоснование 1. **Знакомость команды — главный фактор для минимального проекта.** FastAPI используется в Kin. Нет времени на онбординг в Go или Node.js для задачи, которая требует ~200 строк бэкенд-кода. 2. **Производительности FastAPI достаточно.** При нагрузке 300-400 одновременных запросов бутылочное горлышко — Telegram rate limit (20 сообщений/минуту в группу), а не скорость Python. SQLite WAL + `busy_timeout=5000` справится с 400 одновременными INSERT за ~400 мс (решения #1002, #1005). 3. **Pydantic даёт бесплатную валидацию** входных данных (user_id, timestamp, geo) без дополнительного кода. 4. **Размер деплоя приемлем.** ~300 MB Docker образ — не проблема для одного VPS сервиса. 5. **Вариант B отклонён:** нет опыта у команды, преимущество в скорости (+24%) несущественно при текущей нагрузке. 6. **Вариант C отклонён:** несмотря на превосходную производительность и минимальный деплой, отсутствие опыта в команде создаёт риск для проекта без аргументированной причины переходить на Go. --- ## Последствия - Использовать `aiosqlite` для async SQLite операций (или `sqlite3` через `run_in_executor`) - bcrypt (если понадобится в будущем) — только через `run_in_executor` (решение #1004) - SQLite WAL обязателен: `busy_timeout=5000`, `synchronous=NORMAL` — вместе (решение #1005) - Агрегатор Telegram: реализовать в Python как background task (asyncio) или через простой in-memory буфер с `asyncio.sleep` - requirements.txt: `fastapi`, `uvicorn[standard]`, `aiosqlite`, `httpx` (для Telegram API) - Переменные окружения: `BOT_TOKEN`, `CHAT_ID`, `DB_PATH` — читать из `.env` через `python-dotenv`