153 lines
6.9 KiB
Markdown
153 lines
6.9 KiB
Markdown
# 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
|
||
|
||
Пользователи из разных стран (300–400 человек) → вопрос о локализации.
|
||
|
||
---
|
||
|
||
## Варианты фронтенд-стека
|
||
|
||
### Вариант A: Vanilla JS (zero dependencies)
|
||
|
||
**Плюсы:**
|
||
- Нет build step: файлы деплоятся как есть
|
||
- Минимальный размер: app.js ~2–3 KB, style.css ~1–2 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 строка |
|
||
|
||
**Итого: 4–5 строк текста**, из которых главная (SOS) универсальна.
|
||
|
||
**Целевая аудитория:** 300–400 пользователей из разных стран, но это не массовый consumer-продукт. Пользователи знают что это за приложение и как им пользоваться (установлено по прямой рекомендации).
|
||
|
||
### v2 (если потребуется)
|
||
|
||
```javascript
|
||
// Минимальный i18n: JSON + navigator.language
|
||
const LANG = navigator.language.slice(0, 2);
|
||
const T = translations[LANG] || translations['en'];
|
||
```
|
||
|
||
Файл `translations.json`:
|
||
```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).
|