architect(ET-006): ADR-002/003, infra-requirements, data-requirements, tech-risks
This commit is contained in:
@@ -2,5 +2,8 @@
|
||||
|
||||
Индекс ADR проекта enduro-trails.
|
||||
|
||||
| # | Решение | Статус | Дата |
|
||||
|---|---------|--------|------|
|
||||
| # | Решение | Статус | Дата | Источник |
|
||||
|---|---------|--------|------|----------|
|
||||
| ADR-001 | Блокировка шлагбаумов через `mode.inaccessible` | accepted | 2026-05-15 | [ET-001](../../work-items/ET-001/06-adr/ADR-001-barrier-blocking.md) |
|
||||
| ADR-002 | GPX-фича как отдельный модуль `gpx.js` | accepted | 2026-05-22 | [ET-006](../../work-items/ET-006/06-adr/ADR-002-gpx-module-structure.md) |
|
||||
| ADR-003 | Парсинг GPX — `DOMParser` в основном потоке с чанковой конвертацией | accepted | 2026-05-22 | [ET-006](../../work-items/ET-006/06-adr/ADR-003-gpx-parsing-strategy.md) |
|
||||
|
||||
143
docs/work-items/ET-006/06-adr/ADR-002-gpx-module-structure.md
Normal file
143
docs/work-items/ET-006/06-adr/ADR-002-gpx-module-structure.md
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
type: adr
|
||||
work_item_id: ET-006
|
||||
adr_id: ADR-002
|
||||
title: "GPX-фича как отдельный модуль gpx.js"
|
||||
status: accepted
|
||||
date: 2026-05-22
|
||||
authors:
|
||||
- "agent:architect"
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
---
|
||||
|
||||
# ADR-002: GPX-фича как отдельный модуль `gpx.js`
|
||||
|
||||
## Контекст
|
||||
|
||||
ET-006 добавляет самодостаточную фичу: парсинг GPX, внутренняя модель,
|
||||
управление source/layer/маркерами карты, bottom sheet `sheet-gpx`,
|
||||
canvas-профиль высот, расчёт статистики. Оценка объёма — ~600–900 строк JS.
|
||||
|
||||
ТЗ (`02-trz.md` §6) оставляет структуру файлов открытой и предлагает два
|
||||
варианта: дописать в `app.js` либо вынести в отдельный `gpx.js`. ТЗ ссылается
|
||||
на `units.js` как на прецедент.
|
||||
|
||||
Фактическое состояние кодовой базы (проверено):
|
||||
|
||||
- `src/web/units.js` **не существует**. `app.js` — единственный JS-файл
|
||||
фронтенда: 113 КБ, ~2900 строк.
|
||||
- `app.js` подключён как **классический скрипт** (`<script src="app.js">`),
|
||||
**не** как ES-модуль. Все функции — глобальные; обработчики событий
|
||||
навешаны через инлайновые `onclick="..."` в `index.html`.
|
||||
- Сборщика (bundler) нет. Статика раздаётся как есть через FastAPI
|
||||
`StaticFiles` (mount `/`), Docker копирует каталог целиком
|
||||
(`COPY src/web/ ./src/web/`).
|
||||
|
||||
То есть «прецедент `units.js`» в ТЗ — иллюстративная неточность, но решение
|
||||
о структуре файлов ТЗ явно делегирует этому этапу. Принимается архитектурно.
|
||||
|
||||
## Решение
|
||||
|
||||
Реализовать GPX-фичу в **новом файле `src/web/gpx.js`**, подключённом
|
||||
**классическим** `<script>` после `app.js` в `index.html`.
|
||||
|
||||
`gpx.js` владеет:
|
||||
|
||||
- парсингом GPX (см. ADR-003);
|
||||
- внутренней моделью `window.gpxTracks` (см. `08-data-requirements.md`);
|
||||
- управлением объектами карты: source / line layer / waypoint layers / маркеры;
|
||||
- логикой `sheet-gpx` (список треков, активный трек);
|
||||
- canvas-профилем высот и расчётом статистики (Haversine, набор/сброс высот).
|
||||
|
||||
### Контракт интеграции (единственная поверхность связности)
|
||||
|
||||
`gpx.js` **потребляет** глобали, объявленные в `app.js`:
|
||||
|
||||
| Символ | Назначение |
|
||||
|---|---|
|
||||
| `window._map` | экземпляр MapLibre |
|
||||
| `openSheet(id)` / `closeSheet(id)` | управление bottom sheet |
|
||||
| `showToast(msg)` | уведомления об ошибках (см. ниже) |
|
||||
|
||||
`app.js` получает **ровно одну** новую строку — хук в `rebuildMapOverlays()`:
|
||||
|
||||
```js
|
||||
// в конце rebuildMapOverlays()
|
||||
if (typeof rebuildGpxOverlays === 'function') rebuildGpxOverlays();
|
||||
```
|
||||
|
||||
Хук защищён `typeof`, поэтому `app.js` остаётся валидным и без `gpx.js`.
|
||||
Это закрывает REQ-F-13 (восстановление GPX-слоёв после `map.setStyle()`).
|
||||
|
||||
### Toast-хелпер
|
||||
|
||||
Сейчас существует только `#ruler-toast` + `showRulerToast()` — частный случай.
|
||||
ET-006 требует несколько разных сообщений (REQ-F-02, REQ-F-03).
|
||||
Рекомендуется **обобщить** до переиспользуемого `showToast(message)`
|
||||
(элемент-контейнер + автоскрытие 4 с, стиль как у `#ruler-toast` — TRZ §3.4).
|
||||
Конкретная реализация — на этапе разработки; дизайн toast — за дизайнером.
|
||||
|
||||
### Изменения подключения
|
||||
|
||||
- `index.html`: добавить `<script src="gpx.js"></script>` после строки 400
|
||||
(`<script src="app.js">`), плюс разметку (кнопка `#btn-gpx-upload`,
|
||||
`#tb-gpx`, `#sheet-gpx`) — см. TRZ §3.
|
||||
- Новый статический ассет `gpx.js` подхватывается автоматически:
|
||||
Docker уже копирует весь `src/web/`, FastAPI `StaticFiles` отдаёт по `/gpx.js`.
|
||||
Инфраструктурных изменений нет (см. `07-infra-requirements.md`).
|
||||
|
||||
## Рассмотренные альтернативы
|
||||
|
||||
### Альтернатива A: дописать в `app.js` (отклонена)
|
||||
|
||||
- `app.js` уже 113 КБ — добавление ~800 строк ухудшает читаемость и время ревью.
|
||||
- Смешение несвязанных доменов в одном файле.
|
||||
- Невозможно ревьюить/тестировать GPX-логику изолированно.
|
||||
|
||||
### Альтернатива B: ES-модуль (`<script type="module">`) (отклонена)
|
||||
|
||||
- Потребовала бы перевода всех инлайновых `onclick`-обработчиков на
|
||||
`addEventListener` и экспорт/импорт — большой blast radius на чужой код.
|
||||
- `app.js` не является модулем; смешивать module и classic-скрипты с общими
|
||||
глобалями хрупко.
|
||||
- Вне scope ET-006.
|
||||
|
||||
### Альтернатива C: отдельный классический скрипт `gpx.js` (выбрана)
|
||||
|
||||
- Изоляция домена, минимальная поверхность связности.
|
||||
- Совместимо с текущей моделью «всё глобальное, без сборщика».
|
||||
- Ревью и тесты ET-006 локализованы: `gpx.js` + 3 правки разметки в
|
||||
`index.html` + 1 строка в `app.js`.
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
|
||||
- Первое разбиение фронтенда на несколько файлов — задаёт лёгкий паттерн
|
||||
«одна фича = один классический скрипт + глобали» для будущих задач.
|
||||
- Поверхность ревью/тестирования ET-006 изолирована.
|
||||
|
||||
### Отрицательные / митигации
|
||||
|
||||
| Последствие | Митигация |
|
||||
|---|---|
|
||||
| Порядок подключения: `gpx.js` после `app.js` | Межфайловые вызовы происходят в момент события — функции уже определены. Единственный вызов `app.js → gpx.js` защищён `typeof`. |
|
||||
| Связность через глобали (нет инкапсуляции) | Контракт интеграции зафиксирован выше и узок (3 потребляемых символа + 1 хук). |
|
||||
| Нет `showToast` — нужно обобщать `#ruler-toast` | Небольшой шаред-утиль; реализация на этапе разработки. |
|
||||
|
||||
### Влияние на компоненты
|
||||
|
||||
- **Frontend** — новый файл `gpx.js`, правки `index.html`, 1 строка в `app.js`.
|
||||
- **Backend / API / OSRM / БД** — без изменений.
|
||||
- **C4-диаграммы** — состав компонентов не меняется (фронтенд остаётся одним
|
||||
логическим компонентом) → обновление C4 не требуется. Отдельных `.mmd`
|
||||
в репозитории нет.
|
||||
|
||||
## Связанные
|
||||
|
||||
- ТЗ: `docs/work-items/ET-006/02-trz.md` (§6, REQ-F-13)
|
||||
- ADR-003: стратегия парсинга GPX
|
||||
- Данные: `docs/work-items/ET-006/08-data-requirements.md`
|
||||
- Инфра: `docs/work-items/ET-006/07-infra-requirements.md`
|
||||
- Риски: `docs/work-items/ET-006/10-tech-risks.md`
|
||||
112
docs/work-items/ET-006/06-adr/ADR-003-gpx-parsing-strategy.md
Normal file
112
docs/work-items/ET-006/06-adr/ADR-003-gpx-parsing-strategy.md
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
type: adr
|
||||
work_item_id: ET-006
|
||||
adr_id: ADR-003
|
||||
title: "Парсинг GPX — DOMParser в основном потоке с чанковой конвертацией"
|
||||
status: accepted
|
||||
date: 2026-05-22
|
||||
authors:
|
||||
- "agent:architect"
|
||||
supersedes: null
|
||||
superseded_by: null
|
||||
---
|
||||
|
||||
# ADR-003: Парсинг GPX — `DOMParser` в основном потоке с чанковой конвертацией
|
||||
|
||||
## Контекст
|
||||
|
||||
- ТЗ REQ-F-02 **предписывает** использовать `DOMParser` (XML → DOM → GeoJSON).
|
||||
- ТЗ NF-01: парсинг файла 50 МБ ≤ 5 с на устройстве с 4 ГБ RAM; во время
|
||||
парсинга показывать индикатор загрузки; рендеринг 500K точек без видимых
|
||||
фризов при pan/zoom.
|
||||
- BRD (таблица рисков) упоминает Web Worker как **возможную** митигацию
|
||||
(«при необходимости — Web Worker для парсинга») — не как требование.
|
||||
|
||||
Ключевое техническое ограничение, определяющее решение:
|
||||
|
||||
> **`DOMParser` — это API объекта `Window`. В `WorkerGlobalScope` его нет.**
|
||||
|
||||
В выделенном Web Worker нет DOM и нет `DOMParser` — `new DOMParser()` бросит
|
||||
`ReferenceError`. Следовательно, парсинг в Worker **несовместим** с REQ-F-02:
|
||||
он потребовал бы либо самописного XML-сканера, либо стороннего XML-парсера.
|
||||
|
||||
Это создаёт напряжение между REQ-F-02 (DOMParser) и идеей выноса парсинга
|
||||
в Worker. Напряжение разрешается данным ADR.
|
||||
|
||||
## Решение
|
||||
|
||||
1. **Парсинг XML — в основном потоке** через `DOMParser.parseFromString`.
|
||||
|
||||
2. **Конвертация DOM → GeoJSON и расчёт статистики — чанками.**
|
||||
Это итеративная часть, доминирующая по времени на больших файлах
|
||||
(обход 500K+ DOM-узлов, чтение атрибутов, Haversine, дельты высот).
|
||||
Обрабатывать порциями (например, по 5–10K точек), между порциями
|
||||
отдавать управление event loop (`setTimeout(0)` / `requestAnimationFrame`
|
||||
/ `requestIdleCallback`). Это сохраняет отзывчивость UI и анимацию
|
||||
индикатора загрузки.
|
||||
|
||||
3. **Web Worker не используется.**
|
||||
|
||||
Атомарный вызов `parseFromString` заблокировать чанками нельзя — он блокирует
|
||||
основной поток на время своего выполнения. Для реалистичных GPX (< 5 МБ) это
|
||||
доли секунды; для предельного файла 50 МБ — порядка 1–2 с. Это **принимается**
|
||||
(см. «Последствия» и риск R-1 в `10-tech-risks.md`).
|
||||
|
||||
## Рассмотренные альтернативы
|
||||
|
||||
### Альтернатива A: Web Worker + `DOMParser` (отклонена)
|
||||
|
||||
Невозможна технически: `DOMParser` отсутствует в `WorkerGlobalScope`.
|
||||
|
||||
### Альтернатива B: Web Worker + самописный строковый/regex XML-парсер (отклонена)
|
||||
|
||||
- Противоречит REQ-F-02 (предписан `DOMParser`).
|
||||
- Добавляет в поддержку bespoke-парсер; крайние случаи GPX (namespaces,
|
||||
XML-сущности, CDATA, экзотические кодировки) становятся нашей проблемой.
|
||||
|
||||
### Альтернатива C: Web Worker + сторонняя XML-библиотека (отклонена)
|
||||
|
||||
- Нарушает принцип «минимум зависимостей».
|
||||
- Требует подключения npm-зависимости / сборщика, которых в проекте нет.
|
||||
|
||||
### Альтернатива D: `DOMParser` в основном потоке + синхронная конвертация (отклонена)
|
||||
|
||||
- Полная блокировка UI на всё время «парсинг + конвертация» — для больших
|
||||
файлов это секунды полного фриза, индикатор загрузки не успевает
|
||||
отрисоваться/обновиться.
|
||||
|
||||
### Альтернатива E: `DOMParser` в основном потоке + чанковая конвертация (выбрана)
|
||||
|
||||
- Ноль новых зависимостей, соответствует REQ-F-02.
|
||||
- UI отзывчив на доминирующей (итеративной) части стоимости.
|
||||
- Нет Worker-файла, нет потребности в CSP `worker-src`.
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
|
||||
- Соответствие REQ-F-02, ноль новых зависимостей, нет сборщика/Worker-файла.
|
||||
- CSS-индикатор загрузки анимируется потоком композитора независимо от
|
||||
загрузки основного потока — крутится даже во время атомарного парса.
|
||||
- UI отзывчив во время чанковой конвертации (основная по времени фаза).
|
||||
|
||||
### Отрицательные / митигации
|
||||
|
||||
| Последствие | Митигация |
|
||||
|---|---|
|
||||
| Атомарный `parseFromString` файла 50 МБ блокирует UI на ~1–2 с | Реалистичные GPX-файлы существенно меньше; 50 МБ — потолок валидации, а не норма; индикатор продолжает анимироваться. Зафиксировано как риск R-1. |
|
||||
| Транзиентный пик памяти на 50 МБ (DOM ~150–300 МБ) | DOM освобождается сразу после конвертации; рекомендуется обнулять ссылку на документ. См. риск R-3. |
|
||||
| Если Worker когда-либо станет действительно необходим | Потребует замены `DOMParser` → отдельный ADR, superseding данный. |
|
||||
|
||||
### Влияние на компоненты
|
||||
|
||||
- **Frontend** (`gpx.js`) — реализация парсинга и чанковой конвертации.
|
||||
- **Backend / API / OSRM / БД** — без изменений (парсинг полностью клиентский).
|
||||
- **C4-диаграммы** — состав компонентов не меняется → обновление не требуется.
|
||||
|
||||
## Связанные
|
||||
|
||||
- ТЗ: `docs/work-items/ET-006/02-trz.md` (REQ-F-02, NF-01, §5)
|
||||
- BRD: `docs/work-items/ET-006/01-brd.md` (§5, риск производительности)
|
||||
- ADR-002: структура модуля `gpx.js`
|
||||
- Риски: `docs/work-items/ET-006/10-tech-risks.md` (R-1, R-2, R-3)
|
||||
89
docs/work-items/ET-006/07-infra-requirements.md
Normal file
89
docs/work-items/ET-006/07-infra-requirements.md
Normal file
@@ -0,0 +1,89 @@
|
||||
---
|
||||
type: infra-requirements
|
||||
work_item_id: ET-006
|
||||
version: 1
|
||||
status: approved
|
||||
created_at: 2026-05-22
|
||||
authors:
|
||||
- "agent:architect"
|
||||
---
|
||||
|
||||
# Infra Requirements — ET-006
|
||||
|
||||
ET-006 — **чисто фронтендовая** фича. Парсинг и хранение GPX полностью
|
||||
клиентские (см. BRD §2, ADR-003). Бэкенд, БД, OSRM и сетевая конфигурация
|
||||
**не затрагиваются**.
|
||||
|
||||
## 1. Сводка: инфраструктурных изменений нет
|
||||
|
||||
| Аспект | Изменение |
|
||||
|---|---|
|
||||
| Новые контейнеры | нет |
|
||||
| Новые порты | нет |
|
||||
| Новые тома (volumes) | нет |
|
||||
| Изменения `docker-compose.yml` | нет |
|
||||
| Изменения `Dockerfile` | нет |
|
||||
| Изменения nginx / reverse proxy | нет |
|
||||
| Пересборка OSRM-графа | не требуется |
|
||||
| Миграции БД | нет (см. `08-data-requirements.md`) |
|
||||
| Новые переменные окружения | нет |
|
||||
| Новые внешние зависимости (npm/pip) | нет |
|
||||
|
||||
## 2. Новый статический ассет `gpx.js`
|
||||
|
||||
ADR-002 вводит новый файл `src/web/gpx.js`. Дополнительной конфигурации
|
||||
не требуется:
|
||||
|
||||
- `Dockerfile` уже копирует каталог целиком: `COPY src/web/ ./src/web/`.
|
||||
- `docker-compose.yml` монтирует `./src/web:/app/src/web` (dev).
|
||||
- FastAPI отдаёт статику через `app.mount("/", StaticFiles(directory=STATIC_DIR,
|
||||
html=True))` — файл доступен по `/gpx.js` автоматически.
|
||||
- Подключение — тег `<script src="gpx.js">` в `index.html` (см. ADR-002).
|
||||
|
||||
## 3. Загрузка файла 50 МБ — без серверной нагрузки
|
||||
|
||||
ТЗ REQ-F-03 разрешает GPX-файлы до 50 МБ. Файл читается в браузере
|
||||
(`<input type="file">` + `FileReader` / `File.text()`) и **никогда не
|
||||
загружается на сервер**.
|
||||
|
||||
Следствия:
|
||||
|
||||
- Лимит `client_max_body_size` в nginx — **не релевантен** (нет upload).
|
||||
- Ограничения размера тела запроса в FastAPI — **не релевантны**.
|
||||
- Сетевой трафик и дисковая нагрузка сервера от ET-006 — нулевые.
|
||||
|
||||
## 4. CSP / заголовки безопасности
|
||||
|
||||
- CSP-заголовок в проекте сейчас не задаётся (проверено: `main.py`,
|
||||
`index.html`). Дополнительных директив ET-006 не требует.
|
||||
- Web Worker отклонён в ADR-003 → директива `worker-src` не нужна даже
|
||||
при возможном будущем введении CSP.
|
||||
- Сторонних CDN/доменов ET-006 не добавляет (парсинг нативный `DOMParser`,
|
||||
профиль высот — нативный `<canvas>`).
|
||||
|
||||
## 5. Деплой
|
||||
|
||||
Стандартный процесс, без особых окон и без простоя сервисов:
|
||||
|
||||
1. Merge PR в trunk.
|
||||
2. `make build` — пересборка Docker-образа (включает новый `gpx.js`).
|
||||
3. `make deploy-test` → `docker compose up -d` на mva154.
|
||||
4. Smoke-test на test-окружении
|
||||
(`https://openclaw.mva154.duckdns.org/enduro/`):
|
||||
- открыть `/gpx.js` — отдаётся 200;
|
||||
- загрузить тестовый GPX, убедиться в отрисовке трека.
|
||||
|
||||
Простой `/api/route` и прочих API — **отсутствует** (бэкенд не меняется).
|
||||
|
||||
## 6. Rollback
|
||||
|
||||
Откат — обычный откат фронтенд-файлов (`index.html`, `app.js`, `app.css`,
|
||||
`gpx.js`) через revert PR и повторную сборку образа. Серверного состояния,
|
||||
БД-изменений или графа, которые надо откатывать, нет. Время отката
|
||||
ограничено только временем пересборки/перезапуска контейнера (~1–2 мин).
|
||||
|
||||
## 7. CI
|
||||
|
||||
- ESLint покрывает новый `gpx.js` (цель `make lint`, уже включает eslint).
|
||||
- Бэкенд-тесты (`pytest`) ET-006 не затрагивает.
|
||||
- Пересборки графа в pipeline нет — не релевантно.
|
||||
102
docs/work-items/ET-006/08-data-requirements.md
Normal file
102
docs/work-items/ET-006/08-data-requirements.md
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
type: data-requirements
|
||||
work_item_id: ET-006
|
||||
version: 1
|
||||
status: approved
|
||||
created_at: 2026-05-22
|
||||
authors:
|
||||
- "agent:architect"
|
||||
---
|
||||
|
||||
# Data Requirements — ET-006
|
||||
|
||||
## 1. Сводка: персистентных данных нет
|
||||
|
||||
ET-006 не вводит серверного хранения. Все данные GPX-треков живут **только
|
||||
в памяти браузера** на время сессии (BRD §3 «Out of scope», TRZ NF-04).
|
||||
|
||||
| Аспект | Решение |
|
||||
|---|---|
|
||||
| Схема БД (SQLite/Spatialite) | без изменений |
|
||||
| Миграции (`migrations/`) | нет |
|
||||
| Серверное хранение треков | нет |
|
||||
| `localStorage` / `sessionStorage` | **не используется** (объём данных велик) |
|
||||
| Время жизни данных | сессия; при перезагрузке страницы — потеря |
|
||||
|
||||
## 2. Входные данные
|
||||
|
||||
- Формат: **GPX 1.1**, namespace `http://www.topografix.com/GPX/1/1`
|
||||
(с fallback на парсинг без namespace — TRZ REQ-F-02, тест U-08).
|
||||
- Источник: локальный файл, выбранный пользователем
|
||||
(`<input type="file" accept=".gpx" multiple>`).
|
||||
- Ограничение размера: ≤ 50 МБ на файл (TRZ REQ-F-03).
|
||||
- Извлекаемые сущности: `<trk>` / `<trkseg>` / `<trkpt>`, `<wpt>`, `<rte>`
|
||||
(трактуется как трек).
|
||||
|
||||
## 3. Внутренняя модель (in-memory)
|
||||
|
||||
Каноническая модель — `window.gpxTracks` (массив загруженных файлов),
|
||||
определена в TRZ §4. Владеет ею `gpx.js` (ADR-002):
|
||||
|
||||
```javascript
|
||||
window.gpxTracks = [
|
||||
{
|
||||
id: 'gpx-<timestamp>', // уникальный ID
|
||||
filename: 'morning_ride', // имя файла без расширения
|
||||
color: '#e6194b', // цвет из палитры (8 цветов, цикл)
|
||||
tracks: [
|
||||
{
|
||||
name: 'Morning Ride',
|
||||
points: [[lon, lat, ele, time], ...],
|
||||
stats: { distanceKm, elevGain, elevLoss, eleMin, eleMax }
|
||||
}
|
||||
],
|
||||
waypoints: [ { lon, lat, name, ele } ],
|
||||
sourceId: 'gpx-source-<timestamp>',
|
||||
layerId: 'gpx-layer-<timestamp>',
|
||||
waypointLayerId: 'gpx-wpt-<timestamp>'
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
Замечания для разработки:
|
||||
|
||||
- `ele` / `time` опциональны. При отсутствии `<ele>` — `ele = null`,
|
||||
поля статистики высот = `null` (TRZ REQ-F-11, тесты U-05, U-14).
|
||||
- `window.gpxTracks` — единственный источник правды; объекты карты
|
||||
(source / layer / маркеры) **производны** от него и пересоздаются при
|
||||
`map.setStyle()` через `rebuildGpxOverlays()` (REQ-F-13, ADR-002).
|
||||
- Дополнительно потребуется состояние «активный трек» (для статистики и
|
||||
профиля высот) — хранится в `gpx.js`, должно переживать смену стиля.
|
||||
|
||||
## 4. Объём данных в памяти
|
||||
|
||||
Предельный файл 50 МБ ≈ 500K+ точек. Ориентировочный транзиентный профиль:
|
||||
|
||||
| Стадия | Память (порядок) |
|
||||
|---|---|
|
||||
| Строка файла | ~50 МБ |
|
||||
| DOM после `parseFromString` | ~150–300 МБ (освобождается после конвертации) |
|
||||
| Модель `points` + GeoJSON source | десятки МБ на файл |
|
||||
|
||||
TRZ REQ-F-07 не ограничивает число одновременных треков. Накопление
|
||||
нескольких предельных файлов может исчерпать память вкладки на слабом
|
||||
устройстве — см. риск R-3 в `10-tech-risks.md`.
|
||||
|
||||
## 5. Приватность
|
||||
|
||||
GPX-файлы могут содержать персональные данные (координаты поездок,
|
||||
метки времени `<time>`). В ET-006 эти данные:
|
||||
|
||||
- **не передаются на сервер** (парсинг и хранение клиентские);
|
||||
- **не сохраняются** между сессиями (нет БД, нет `localStorage`).
|
||||
|
||||
Следствие: серверных обязательств по хранению/удержанию/удалению
|
||||
персональных данных ET-006 **не порождает**. Это сознательное проектное
|
||||
свойство, а не упущение.
|
||||
|
||||
## 6. Выходные данные
|
||||
|
||||
ET-006 не экспортирует и не записывает данные (экспорт обратно в GPX —
|
||||
out of scope, BRD §3). Профиль высот и статистика — производные расчёты,
|
||||
существуют только в UI.
|
||||
122
docs/work-items/ET-006/10-tech-risks.md
Normal file
122
docs/work-items/ET-006/10-tech-risks.md
Normal file
@@ -0,0 +1,122 @@
|
||||
---
|
||||
type: tech-risks
|
||||
work_item_id: ET-006
|
||||
version: 1
|
||||
status: approved
|
||||
created_at: 2026-05-22
|
||||
authors:
|
||||
- "agent:architect"
|
||||
---
|
||||
|
||||
# Technical Risks — ET-006
|
||||
|
||||
Технические риски этапа разработки. Бизнес-риски — в BRD §5.
|
||||
Шкала: вероятность / влияние ∈ {низк., сред., выс.}.
|
||||
|
||||
## R-1 — Фриз UI при атомарном парсинге файла 50 МБ
|
||||
|
||||
- **Вероятность:** сред. · **Влияние:** низк.
|
||||
- `DOMParser.parseFromString` нельзя разбить на чанки; для предельного
|
||||
файла 50 МБ он блокирует основной поток на ~1–2 с (ADR-003).
|
||||
- **Митигация:** реалистичные GPX существенно меньше; 50 МБ — потолок
|
||||
валидации, а не норма; чанковая конвертация DOM→GeoJSON снимает
|
||||
доминирующую часть стоимости; CSS-индикатор анимируется композитором.
|
||||
- **Статус:** принят. Остаточный риск задокументирован в ADR-003.
|
||||
|
||||
## R-2 — Рендеринг трека 500K точек на слабом GPU/мобильном
|
||||
|
||||
- **Вероятность:** сред. · **Влияние:** сред.
|
||||
- BRD §3 **явно запрещает** упрощение (simplify) точек. Значит штатного
|
||||
рычага снижения нагрузки на рендер нет.
|
||||
- **Митигация:** MapLibre оптимизирует GeoJSON line layers; единственная
|
||||
доступная мера — принятие риска бизнесом (simplify вне scope).
|
||||
Если на тестировании обнаружатся фризы pan/zoom — это повод для
|
||||
отдельной задачи, а не для ET-006.
|
||||
- **Статус:** принят бизнесом (ограничение BRD).
|
||||
|
||||
## R-3 — Исчерпание памяти при накоплении больших файлов
|
||||
|
||||
- **Вероятность:** низк. · **Влияние:** сред.
|
||||
- TRZ REQ-F-07 не ограничивает число одновременных треков. Несколько
|
||||
файлов по 50 МБ + транзиентные DOM-пики могут исчерпать память вкладки
|
||||
на устройстве с 4 ГБ RAM.
|
||||
- **Митигация:** обнулять ссылку на DOM-документ сразу после конвертации
|
||||
(освобождение ~150–300 МБ); не держать исходную строку файла после
|
||||
парсинга. Реалистичный сценарий накопления десятков предельных файлов
|
||||
маловероятен.
|
||||
- **Статус:** принят с рекомендацией по управлению памятью разработке.
|
||||
|
||||
## R-4 — Дублирование обработчиков событий карты после `setStyle()`
|
||||
|
||||
- **Вероятность:** выс. · **Влияние:** сред.
|
||||
- REQ-F-13: при смене стиля GPX-слои пересоздаются в `rebuildGpxOverlays()`.
|
||||
Обработчики `map.on('click', layerId, fn)` / `mouseenter` / `mouseleave`
|
||||
при повторной регистрации **накапливаются** — клик по треку начнёт
|
||||
срабатывать многократно, утечка слушателей.
|
||||
- **Митигация (для разработки):** перед повторной регистрацией снимать
|
||||
старые обработчики (`map.off(...)` с сохранёнными ссылками на функции)
|
||||
либо регистрировать делегированный обработчик один раз по префиксу ID
|
||||
слоя, а не по конкретному ID. Покрыть тестом I-07.
|
||||
- **Статус:** требует внимания на разработке.
|
||||
|
||||
## R-5 — Варианты namespace в GPX-файлах
|
||||
|
||||
- **Вероятность:** сред. · **Влияние:** низк.
|
||||
- При запросе элементов через `getElementsByTagNameNS` файл без `xmlns`
|
||||
вернёт пусто; при запросе без namespace — наоборот, возможны коллизии.
|
||||
TRZ требует работать с GPX и с namespace, и без (тест U-08).
|
||||
- **Митигация:** использовать namespace-агностичный обход — сопоставление
|
||||
по `localName` элементов, а не по полному имени с namespace.
|
||||
- **Статус:** требует внимания на разработке.
|
||||
|
||||
## R-6 — Невалидные координаты ломают `fitBounds`
|
||||
|
||||
- **Вероятность:** низк. · **Влияние:** низк.
|
||||
- Точки `(0,0)`, NaN или координаты вне диапазона раздувают bbox и портят
|
||||
автоцентрирование (REQ-F-06).
|
||||
- **Митигация:** валидировать `lat ∈ [-90,90]`, `lon ∈ [-180,180]`,
|
||||
числовой тип; отбрасывать невалидные точки до построения bbox.
|
||||
- **Статус:** требует внимания на разработке.
|
||||
|
||||
## R-7 — Z-order GPX-слоёв относительно маршрута OSRM
|
||||
|
||||
- **Вероятность:** сред. · **Влияние:** низк.
|
||||
- REQ-F-04 / REQ-F-13 / AC-10: GPX-линии должны быть **ниже** активного
|
||||
маршрута OSRM, но выше базовых слоёв — и сохранять этот порядок после
|
||||
`setStyle()`. Маршрут (`route-line-*`) и GPX рисуются в разных функциях;
|
||||
порядок их вызова в `rebuildMapOverlays()` определяет итоговый z-order.
|
||||
- **Митигация:** добавлять GPX line layer с явным `beforeId` — перед
|
||||
слоями маршрута, если они есть; иначе поверх базовых. Не полагаться на
|
||||
порядок вызовов в `rebuildMapOverlays()`. Покрыть тестом I-06.
|
||||
- **Статус:** требует внимания на разработке.
|
||||
|
||||
## R-8 — Связность `gpx.js` ↔ `app.js` через глобали
|
||||
|
||||
- **Вероятность:** низк. · **Влияние:** низк.
|
||||
- `gpx.js` зависит от глобалей `app.js` (`window._map`, `openSheet`,
|
||||
`closeSheet`, `showToast`); `app.js` вызывает `rebuildGpxOverlays()`.
|
||||
- **Митигация:** контракт интеграции зафиксирован в ADR-002; вызов из
|
||||
`app.js` защищён `typeof`; `gpx.js` подключается после `app.js`.
|
||||
- **Статус:** принят (контракт узкий и задокументирован).
|
||||
|
||||
## R-9 — Отсутствует переиспользуемый `showToast`
|
||||
|
||||
- **Вероятность:** низк. · **Влияние:** низк.
|
||||
- Сейчас есть только частный `showRulerToast()` / `#ruler-toast`.
|
||||
ET-006 требует несколько разных сообщений об ошибках.
|
||||
- **Митигация:** обобщить toast до `showToast(message)` (ADR-002).
|
||||
- **Статус:** принят (мелкий шаред-утиль на этапе разработки).
|
||||
|
||||
## Сводная таблица
|
||||
|
||||
| ID | Риск | Вер. | Влияние | Статус |
|
||||
|----|------|------|---------|--------|
|
||||
| R-1 | Фриз при парсинге 50 МБ | сред. | низк. | принят |
|
||||
| R-2 | Рендеринг 500K точек | сред. | сред. | принят бизнесом |
|
||||
| R-3 | Память при накоплении файлов | низк. | сред. | принят + рекомендация |
|
||||
| R-4 | Дублирование обработчиков после setStyle | выс. | сред. | внимание разработки |
|
||||
| R-5 | Варианты namespace GPX | сред. | низк. | внимание разработки |
|
||||
| R-6 | Невалидные координаты в fitBounds | низк. | низк. | внимание разработки |
|
||||
| R-7 | Z-order относительно маршрута OSRM | сред. | низк. | внимание разработки |
|
||||
| R-8 | Связность gpx.js ↔ app.js | низк. | низк. | принят |
|
||||
| R-9 | Нет переиспользуемого showToast | низк. | низк. | принят |
|
||||
Reference in New Issue
Block a user