заведён канон общих конвенций для личных проектов
- 13 конвенций по осям arch / lang / stack / common; репозитории берут оттуда копии в свой docs/conventions/ и коммитят их у себя - conv — синхронизация копий: add / status / diff / pull / push, локальные регионы исключены из сравнения, поэтому расхождение не даёт шума
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
---
|
||||
status: рекомендуемая
|
||||
---
|
||||
|
||||
# Идентификаторы сущностей
|
||||
|
||||
Как выбираются и как выглядят первичные ключи. Схема БД меняется тяжело,
|
||||
поэтому конвенция применяется **к новым таблицам**; существующие живут как
|
||||
есть и перечислены в отступлениях.
|
||||
|
||||
## Условие применимости
|
||||
|
||||
Вопрос задаётся **один раз на репозиторий**, а не по таблицам:
|
||||
|
||||
> Есть ли в приложении хотя бы одна сущность, которую адресуют извне — по
|
||||
> id из URL, запроса API или callback-данных?
|
||||
>
|
||||
> - **Да** → весь репозиторий на сортируемый строковый id, который
|
||||
> генерирует приложение (ULID), включая внутренние таблицы.
|
||||
> - **Ни одной** → автоинкремент, и этого достаточно.
|
||||
|
||||
Критерий — именно **адресация**: снаружи по этому id возвращаются к
|
||||
системе. Не «id виден в логе» — туда рано или поздно попадает любой
|
||||
идентификатор, и по такому критерию вторая ветка была бы недостижима.
|
||||
|
||||
Почему квантор репозиторный, а не потабличный: внутренние сущности имеют
|
||||
привычку становиться внешними, и тогда целочисленный id утекает в URL
|
||||
задним числом. Плюс одна ментальная модель дешевле, чем спор при заведении
|
||||
каждой таблицы.
|
||||
|
||||
**Что не является смешиванием.** Запрет касается двух видов
|
||||
*сгенерированных суррогатных* ключей в одной базе. Естественные и составные
|
||||
ключи у таблиц-деталей — третья категория, они допустимы всегда (см. ниже).
|
||||
|
||||
<!-- local:решение -->
|
||||
<!-- /local -->
|
||||
|
||||
## Если ULID
|
||||
|
||||
- **PK — TEXT ULID** (26 символов Crockford base32), генерируется
|
||||
**приложением** в момент создания записи, а не БД.
|
||||
- Почему не UUID: UUIDv4 не сортируется по времени. UUIDv7 (RFC 9562)
|
||||
сортируется, и против него остаются два довода — 36 символов против 26 и
|
||||
дефисы: без них `grep` и двойной клик берут id целиком.
|
||||
- Сортировка по времени создания даёт `ORDER BY id` = хронология с
|
||||
точностью до миллисекунды. Внутри одной миллисекунды порядок произволен,
|
||||
если генератор не монотонный, — на хронологию событий это не влияет.
|
||||
- Глобальная уникальность across таблиц даёт побочный, но важный эффект:
|
||||
голый `grep` по id находит все записи сущности независимо от имени поля.
|
||||
- **Единая точка генерации и разбора.** Один модуль генерирует id и один
|
||||
разбирает; самодельных генераторов по коду нет.
|
||||
|
||||
## Канонический вид и границы
|
||||
|
||||
- Генерим и храним id в **нижнем регистре**. Сравнение строк в БД обычно
|
||||
побайтовое, поэтому регистр — не косметика, а корректность. Спецификация
|
||||
ULID канонизирует верхний регистр, и библиотеки по умолчанию отдают
|
||||
именно его — нижний обеспечивает единая точка генерации, поэтому звать
|
||||
библиотеку мимо неё нельзя.
|
||||
- Любой пришедший снаружи id **обязательно** проходит разбор до запроса к
|
||||
БД: он валидирует формат и нормализует регистр.
|
||||
- Синтаксически невалидный id, которым **адресуют ресурс**, трактуем как
|
||||
несуществующую сущность (404), **без похода в БД**: это и дешевле, и
|
||||
убирает целый класс запросов с мусором. Невалидный id, пришедший из
|
||||
собственной формы или кнопки, — не «не найдено», а некорректный ввод: там
|
||||
это признак устаревшего интерфейса или бага, и маскировать его под 404
|
||||
значит терять диагностику.
|
||||
|
||||
## Естественные и составные ключи — для деталей
|
||||
|
||||
У таблиц-деталей и связей допустим естественный или составной ключ вместо
|
||||
сгенерированного, когда он есть по природе данных. Отдельный id там —
|
||||
мёртвый вес, который ещё и создаёт второй способ адресовать ту же строку.
|
||||
|
||||
Прочие генерируемые идентификаторы (батчи, задания, корреляционные ключи) —
|
||||
через ту же единую точку: единый формат, сортируемость, корреляция в логах.
|
||||
|
||||
<!-- local:отступления -->
|
||||
<!-- /local -->
|
||||
|
||||
## Связано
|
||||
|
||||
- `arch/time.md` — метки времени тоже генерирует приложение, а не схема.
|
||||
|
||||
<!-- local:связано -->
|
||||
<!-- /local -->
|
||||
Reference in New Issue
Block a user