- доменные типы internal/catalog лишились json-тегов и MarshalJSON; типы metricWire/layerWire/aggregationWire и перевод catalogWire живут в транспорте, тело отказа тоже получило объявленный тип — байты ответа не изменились - заведён сторож: обход графа типов ответа утверждает, что домен не доезжает до сериализации, плюс требование json-тега на полях транспортных структур и заведомо красные случаи к обоим правилам - решение с ценой обеих сторон записано в architecture.md и ADR; шаг lint в гейте получил свой кеш — общий на машину красил прогон находками из чужого worktree
6.4 KiB
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— правило о механизме: байты на каждую различимую форму ответа, заведомо красный случай для структурной проверки.- Схема, витрина, архив, конфиг — не трогаются.