заведён канон общих конвенций для личных проектов

- 13 конвенций по осям arch / lang / stack / common; репозитории берут
  оттуда копии в свой docs/conventions/ и коммитят их у себя
- conv — синхронизация копий: add / status / diff / pull / push, локальные
  регионы исключены из сравнения, поэтому расхождение не даёт шума
This commit is contained in:
av
2026-07-25 18:18:18 +03:00
commit 4a59c71737
15 changed files with 2142 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
---
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`.
<!-- local:механизировано -->
<!-- /local -->
<!-- local:отступления -->
<!-- /local -->