--- status: рекомендуемая extends: arch/db-identifiers.md --- # Идентификаторы: реализация на Go Как `arch/db-identifiers.md` выглядит в Go-приложении, выбравшем ULID. ## Единая точка — `internal/ident` - `ident.NewID()` — генерация. **PK сущности** генерируется в `Create`-методах слоя `store`. Прочие идентификаторы (батч, задание, корреляционный ключ) генерируются там, где начинается операция, — но тоже только через `ident`. - `ident.NewIDAt(t)` — генерация с заданным временем, для бэкфилла в Go-миграциях: сортировка id тогда сохраняет историческую хронологию, а не момент прогона миграции. - `ident.Parse()` — разбор и нормализация; зовётся на **входных границах** (HTTP-роут, форма, callback бота), до обращения к store. - Других генераторов и парсеров id в коде нет. Это то самое «единая точка» из базовой конвенции; без него нормализация регистра неизбежно где-нибудь пропускается. ## Типы В структурах store и домена id — обычный `string`. Отдельный тип `ID` заводим, только если появится вторая семья идентификаторов, которую можно перепутать; до этого он даёт конверсии без выгоды. От перепутывания двух id одной семьи в сигнатуре он всё равно не спасает — там помогают имена параметров. ## Невалидный id на границе Разбор не удался — дальше зависит от того, откуда id пришёл: - **из пути или query URL** — сразу 404, без обращения к store и без фабрикации доменной ошибки: снаружи это неотличимо от несуществующей записи, и хорошо; - **из собственной формы или callback-данных кнопки** — 400 либо понятное сообщение («кнопка устарела»): это баг интерфейса или протухший экран, и под «не найдено» его маскировать нельзя. Транспорт не создаёт доменные sentinel'ы, чтобы тут же их сматчить, — это инверсия правила «трансляция у источника» из `lang/go/errors.md`.