httpapi: форма провода читающих маршрутов объявлена транспортом
- доменные типы internal/catalog лишились json-тегов и MarshalJSON; типы metricWire/layerWire/aggregationWire и перевод catalogWire живут в транспорте, тело отказа тоже получило объявленный тип — байты ответа не изменились - заведён сторож: обход графа типов ответа утверждает, что домен не доезжает до сериализации, плюс требование json-тега на полях транспортных структур и заведомо красные случаи к обоим правилам - решение с ценой обеих сторон записано в architecture.md и ADR; шаг lint в гейте получил свой кеш — общий на машину красил прогон находками из чужого worktree
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
## 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` — правило о механизме: байты на каждую
|
||||
различимую форму ответа, заведомо красный случай для структурной проверки.
|
||||
- Схема, витрина, архив, конфиг — не трогаются.
|
||||
Reference in New Issue
Block a user