Files
av a834d10415 httpapi: форма провода читающих маршрутов объявлена транспортом
- доменные типы internal/catalog лишились json-тегов и MarshalJSON; типы
  metricWire/layerWire/aggregationWire и перевод catalogWire живут в транспорте,
  тело отказа тоже получило объявленный тип — байты ответа не изменились
- заведён сторож: обход графа типов ответа утверждает, что домен не доезжает до
  сериализации, плюс требование json-тега на полях транспортных структур и
  заведомо красные случаи к обоим правилам
- решение с ценой обеих сторон записано в architecture.md и ADR; шаг lint в
  гейте получил свой кеш — общий на машину красил прогон находками из чужого
  worktree
2026-08-04 16:18:05 +03:00

76 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Why
Публичный контракт единственного читающего маршрута сегодня **выведен из формы
доменных типов**: `internal/catalog` сам несёт `json`-теги, а
`internal/httpapi/catalog.go` владеет только оболочкой `{"metrics": …}`.
Переименование поля в домене, разъединение встроенного `Basis` или добавление
внутреннего поля меняют байты ответа, не касаясь транспорта; удерживает это один
литерал в тесте `TestКаталогОтдаётОжидаемыеБайты`, и о том, что он и есть
контракт, в коде не сказано нигде.
Пока маршрут один и потребителей у него нет, цена нулевая. Со следующей задачи
цели образец копируют точки, тренировки, записи и MCP — четыре маршрута и второй
транспорт. Решение принимается **до** копирования: потом это будет не решение, а
археология. Момент выбран ещё и потому, что менять контракт каталога сейчас
бесплатно — снаружи его не читает никто.
## What Changes
- **Форма провода объявляется транспортом, а не выводится из домена.** Каждый
читающий маршрут `internal/httpapi` объявляет собственные типы ответа с
`json`-тегами; доменные типы `internal/catalog` тегов лишаются, перевод —
явная функция транспорта.
- **Доменные типы каталога перестают быть публичным контрактом.** `Metric`,
`LayerRange`, `Aggregation`, `Basis` остаются формой ответа **use-case**, а не
формой ответа HTTP. `Style.MarshalJSON` убирается: сериализацию рода кладёт
транспорт. Значения словаря (`cumulative`/`instant`/`unknown`) при этом
остаются в домене — провод зовёт `Style.String()`, чтобы не завести второй
словарь; граница и её цена названы в `design.md`.
- **Тело отказа тоже получает объявленный тип.** Сегодня `writeError` собирает
его из `map[string]string`; у читающего маршрута тело отказа — часть того же
контракта, и оно не должно оставаться единственным местом, куда не смотрит ни
один сторож. Байты те же.
- **Байты ответа каталога не меняются.** Правка структурная; контракт из
требования «Форма ответа каталога» остаётся тем же до символа, и это
утверждается тестом, а не намерением.
- **Заводится проверка, прямо утверждающая разделение:** ни один тип домена не
достигает сериализации ответа — обход графа типов значения ответа, с заведомо
красным случаем рядом. Отсюда и следует, что переименование поля домена до
провода не доезжает.
- **Решение и цена обеих сторон записываются** в `docs/architecture.md`
(раздел Read API); правило о **механизме** проверки — в
`docs/conventions/testing.md`, где у правил такого рода дом.
- Схема не трогается, миграции нет, данные только читаются.
## Capabilities
### New Capabilities
- `read-api`: общие правила читающих маршрутов, поверх которых встают точки,
тренировки, записи и MCP. Два правила: **публичный контракт меняется только
правкой транспорта** (форма провода объявляется явно и не выводится из формы
доменных типов) и **пустая коллекция — список, а не отсутствие**.
### Modified Capabilities
Нет. Поведение каталога не меняется: байты ответа, заголовки и коды остаются
прежними, требование «Форма ответа каталога» из `catalog` продолжает описывать
их без единой правки. Это и есть проверяемое утверждение изменения.
## Impact
- `internal/catalog/catalog.go` — снятие `json`-тегов с `Basis`, `Aggregation`,
`LayerRange`, `Metric`; удаление `Style.MarshalJSON`.
- `internal/catalog/measure_test.go``TestStyleСловарь` переезжает с
`MarshalJSON` на `String()`.
- `internal/httpapi/catalog.go` — типы провода каталога и перевод из домена.
- `internal/httpapi/httpapi.go` — объявленный тип тела отказа вместо карты.
- `internal/httpapi/catalog_test.go` — байтовые утверждения формы (измеренное
окно, неизмеренное окно, пустая витрина).
- `internal/httpapi/wire_internal_test.go` — обход графа типов ответа и заведомо
красный случай к нему.
- `docs/architecture.md` — раздел Read API: решение с ценой обеих сторон.
- `docs/conventions/testing.md` — правило о механизме: байты на каждую
различимую форму ответа, заведомо красный случай для структурной проверки.
- Схема, витрина, архив, конфиг — не трогаются.