From bafbea2dab4f71ca95579b29f3566e9879dc6115 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Thu, 21 May 2026 22:18:08 +0300 Subject: [PATCH] architect(ET-005): ADR, infra-requirements, data-requirements, tech-risks --- .../adr-0001-unit-toggle-client-side.md | 205 ++++++++++++++++++ .../ET-005/07-infra-requirements.md | 107 +++++++++ .../work-items/ET-005/08-data-requirements.md | 76 +++++++ docs/work-items/ET-005/10-tech-risks.md | 128 +++++++++++ 4 files changed, 516 insertions(+) create mode 100644 docs/work-items/ET-005/06-adr/adr-0001-unit-toggle-client-side.md create mode 100644 docs/work-items/ET-005/07-infra-requirements.md create mode 100644 docs/work-items/ET-005/08-data-requirements.md create mode 100644 docs/work-items/ET-005/10-tech-risks.md diff --git a/docs/work-items/ET-005/06-adr/adr-0001-unit-toggle-client-side.md b/docs/work-items/ET-005/06-adr/adr-0001-unit-toggle-client-side.md new file mode 100644 index 0000000..84a4dc3 --- /dev/null +++ b/docs/work-items/ET-005/06-adr/adr-0001-unit-toggle-client-side.md @@ -0,0 +1,205 @@ +--- +type: adr +work_item_id: ET-005 +adr_id: adr-0001 +title: "ADR-0001: Переключение единиц измерения (км/мили) — клиентское решение с централизованным форматтером" +status: accepted +created_at: 2026-05-21 +authors: + - "agent:architect" +supersedes: [] +superseded_by: [] +labels: [] +--- + +# ADR-0001 — Переключение единиц измерения (км/мили): клиентское решение с централизованным форматтером + +## Статус + +Accepted + +## Контекст + +ET-005 добавляет переключатель единиц измерения расстояний (км/мили) в +панель настроек карты. Выбор сохраняется в `localStorage`, по умолчанию — +километры, при переключении все видимые расстояния пересчитываются за +< 100 мс (см. `01-brd.md`, `02-trz.md`, `03-acceptance-criteria.md`). + +Существующее состояние кодовой базы (`src/web/`): + +- Фронтенд **плоский, без сборщика и без модульной системы**: + `index.html`, `app.js`, `app.css`, `style.json`, `style-dark.json`. + `app.js` (≈ 3036 строк) подключается как **классический скрипт** + (``, `index.html:405`), а не ES-модуль. + Каталога `src/web/static/js/` в репозитории **нет**. +- Форматирование расстояний **не централизовано** — захардкоженный + паттерн `(m / 1000).toFixed(N) + ' км'` встречается минимум в **13 + местах** `app.js`: `formatDist()` (стр. 187–189), карточки сегментов + (638), карточки маршрута (1157, 1191, 2202, 2212, 2357, 2370, 2605), + scale-bar (1416–1440), всплывающие подсказки (1478), линейка/ruler + (1837, 1875, 1885, 1931). Единого форматтера нет. +- Внутренняя каноническая единица расстояния — **метры** + (`route.distance_m`), для линейки — километры (`rulerTotal`). +- Сложился устойчивый паттерн персистентности UI-настроек в + `localStorage`: ключи `enduro-theme-mode`, `terrain-hillshade`, + `terrain-tri`, `trails-track`, `trails-path`, `poi-visible` (ET-002), + `MARKERS_KEY`. +- «Панель настроек карты» из BRD/ТЗ де-факто реализована как попап + `#terrain-popup` (заголовок «Эндуро»), открываемый кнопкой «Рельеф». + В ET-002 в этот же попап добавлен чекбокс POI. В `app.css` уже есть + готовый компонент сегментированного переключателя `.seg-control` / + `.seg-btn` (стр. 360–363). + +Backend (FastAPI), БД (SQLite/Spatialite), тайл-сервер и OSRM к выбору +единиц измерения отношения **не имеют**: расстояния приходят с backend +в метрах, перевод — исключительно вопрос представления на клиенте. + +## Рассматриваемые варианты + +### Вариант A — Централизованный модуль-форматтер + единый оркестратор (выбран) + +Новый модуль `src/web/units.js`, подключаемый как **классический скрипт +до `app.js`**, экспортирует глобальный неймспейс `window.Units` с +функциями состояния (`getUnit()` / `setUnit()`) и форматирования +(`formatDistance(meters, {precision})`). Все 13 мест форматирования в +`app.js` переводятся на вызов `Units.formatDistance(...)`. При смене +единицы `setUnit()` пишет в `localStorage` и диспатчит `unitchange` на +`document`; в `app.js` регистрируется **один** обработчик-оркестратор +`onUnitChange()`, который пере-вызывает существующие функции отрисовки +видимых поверхностей с расстояниями. + +- **Плюсы:** единственный источник истины по форматированию убирает + риск рассинхрона; нулевые изменения backend/БД/инфраструктуры; + пересчёт мгновенный (нет ни сети, ни перезагрузки тайлов) — НФТ + «< 100 мс» выполняется тривиально; согласуется с принципом «минимум + зависимостей»; модуль покрывается unit-тестами изолированно. +- **Минусы:** требует рефакторинга 13 мест в `app.js` (риск пропустить + вызов — вынесен в `10-tech-risks.md`, R1); глобальный неймспейс вместо + ES-import — но это сознательное соответствие текущему стилю `app.js`. + +### Вариант B — Рассыпанные подписчики `unitchange` по компонентам (буквальный тех-дизайн ТЗ) + +Каждый компонент с расстоянием самостоятельно подписывается на +`unitchange` и сам себя перерисовывает. + +- **Плюсы:** формально совпадает с разделом «Технический дизайн» ТЗ. +- **Минусы:** N подписок вместо одной — высокая связность, легко + забыть подписку у нового компонента; дублирование логики перерисовки; + не решает корневую проблему — захардкоженное `+ ' км'` остаётся + размазанным. Отклонён: оркестрация в одном месте надёжнее. + +### Вариант C — Вынести `units.js` в ES-модуль `src/web/static/js/units.js` + +Буквально следовать пути из ТЗ: создать `src/web/static/js/units.js` как +ES-модуль. + +- **Плюсы:** «правильная» модульность. +- **Минусы:** в проекте нет каталога `static/js/`, нет сборщика, а + `app.js` подключён как классический скрипт. Полноценный `import` + потребовал бы перевода `app.js` в `type="module"` и реструктуризации + ≈ 3000 строк — несопоставимо со scope ET-005 и противоречит принципу + «минимум зависимостей». Отклонён. + +## Решение + +Принимается **Вариант A**. + +1. **Только клиент.** Backend, БД, API, тайл-сервер, OSRM, инфраструктура + — без изменений. Расстояния продолжают приходить с backend в метрах. + +2. **Новый модуль `src/web/units.js`** (НЕ `src/web/static/js/units.js`). + Раздел «Технический дизайн» ТЗ носит рекомендательный характер; + финальное размещение файлов — мандат архитектуры. Путь скорректирован + под фактическую плоскую структуру `src/web/`. Модуль подключается в + `index.html` как классический скрипт **строго перед `app.js`**: + `` затем `