baton/docs/adr/ADR-005-frontend-stack.md

6.9 KiB
Raw Permalink Blame History

ADR-005: Выбор фронтенд-стека и i18n-стратегия

Дата: 2026-03-20 Статус: Accepted Автор: Architect Agent (Kin pipeline, BATON-003) Решения: #1026


Контекст

Фронтенд Baton — PWA с одной кнопкой SOS. Функционал:

  • Показать кнопку по центру экрана
  • При первом визите — форма ввода имени (регистрация)
  • При нажатии — вызов /api/signal с UUID, timestamp, geo (опционально)
  • Service Worker для cache-first
  • Адаптация под iOS/Android/Desktop

Пользователи из разных стран (300400 человек) → вопрос о локализации.


Варианты фронтенд-стека

Вариант A: Vanilla JS (zero dependencies)

Плюсы:

  • Нет build step: файлы деплоятся как есть
  • Минимальный размер: app.js ~23 KB, style.css ~12 KB
  • Нет транспиляции, бандлинга, node_modules
  • Время загрузки: < 50ms на 3G
  • Полный контроль над SW lifecycle (нет абстракций)

Минусы:

  • Нет компонентной модели (не нужна для 1 экрана)
  • Ручное управление DOM (getElementById, classList)
  • Нет HMR при разработке

Вариант B: Preact (~3 KB gzip)

Плюсы:

  • React-совместимый API в 3 KB
  • JSX + компоненты
  • Hooks для state management

Минусы:

  • Требует build step (Vite/esbuild)
  • node_modules для 1 кнопки и 1 формы — overkill
  • Добавляет ~3 KB к bundle без пользы
  • Усложняет SW интеграцию (build output vs source)

Вариант C: Vue 3 (используется в Kin)

Плюсы:

  • Знакомость команды
  • Мощный template синтаксис
  • Экосистема (router, Pinia)

Минусы:

  • ~33 KB gzip (runtime)
  • Обязателен build step
  • Vue Router, Pinia, SFC — всё это не нужно для 1 экрана
  • Кратный overkill: framework для 1 кнопки

Решение (фронтенд)

Выбран Вариант A: Vanilla JS


Обоснование (фронтенд)

  1. Один экран, одна кнопка. Компонентная модель не даёт преимуществ когда весь UI — это кнопка + форма имени + сообщение об ошибке.

  2. Zero build step = zero complexity. Файлы index.html, app.js, style.css, sw.js, manifest.json деплоятся напрямую. Нет Vite, нет esbuild, нет npm run build.

  3. Минимальный bundle. Для экстренного приложения скорость загрузки критична. Vanilla JS: ~5 KB total. Vue 3: ~40 KB минимум. На 3G разница: 50ms vs 400ms.

  4. SW интеграция проще. Service Worker precache список — конечный и известный заранее. С build step нужно интегрировать hashed filenames в SW.

  5. Preact/Vue отклонены — обоснованный overkill. Если в будущем UI усложнится (v2: история сигналов, настройки, чат) — миграция на Preact за 2 часа.


i18n-стратегия (#1026)

Решение: i18n НЕ НУЖНА в v1

Обоснование

Анализ текстового контента UI:

Элемент Текст Универсальность
Кнопка "SOS" или "HELP" SOS — международный сигнал, не требует перевода
Заголовок "Baton" Название продукта, не переводится
Форма регистрации "Your name" + placeholder 1 строка, английский понятен целевой аудитории
Ошибка сети "No connection" 1 строка
Ошибка сервера "Try again" 1 строка

Итого: 45 строк текста, из которых главная (SOS) универсальна.

Целевая аудитория: 300400 пользователей из разных стран, но это не массовый consumer-продукт. Пользователи знают что это за приложение и как им пользоваться (установлено по прямой рекомендации).

v2 (если потребуется)

// Минимальный i18n: JSON + navigator.language
const LANG = navigator.language.slice(0, 2);
const T = translations[LANG] || translations['en'];

Файл translations.json:

{
  "en": { "name_placeholder": "Your name", "no_connection": "No connection", "try_again": "Try again" },
  "ru": { "name_placeholder": "Ваше имя", "no_connection": "Нет связи", "try_again": "Повторите" }
}

Триггер перехода к i18n: явный запрос от пользователей или расширение аудитории на non-English speaking массовый рынок.


Файловая структура фронтенда (v1)

frontend/
├── index.html          # App shell, meta tags, apple-touch-icon, manifest link
├── app.js              # UUID auth, geolocation, fetch /api/signal, error handling
├── style.css           # Центрированная кнопка, responsive, dark theme
├── sw.js               # Cache-first precache, skipWaiting, clientsClaim
├── manifest.json       # PWA metadata (name, icons, display:standalone)
├── icon-180.png        # iOS apple-touch-icon
├── icon-192.png        # Android manifest (required)
└── icon-512.png        # Android splash + maskable

Последствия

  1. Нет package.json в проекте. Фронтенд не зависит от npm. Backend уже использует pip (requirements.txt).

  2. Деплой фронтенда = копирование файлов. Nginx root /path/to/frontend; — готово. Нет CI/CD для сборки.

  3. Стили — один CSS файл. Нет SCSS, нет PostCSS, нет CSS-in-JS. Для 1 экрана это достаточно.

  4. Тестирование фронтенда: в v1 — ручное. Если потребуется автоматизация — Playwright (headless Chrome, без build step).

  5. i18n решение задокументировано явно (#1026). При запросе локализации — план миграции готов (JSON файл + 3 строки JS).