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

- 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
+57
View File
@@ -0,0 +1,57 @@
---
status: рекомендуемая
---
# Схема и миграции (SQLite, Go)
Область действия — **новые миграции**. Существующая схема не переписывается;
линтер проверяет то, что добавляется, а не то, что уже лежит.
## Миграции
- Инструмент — goose, файлы миграций лежат рядом со store-слоем.
- **SQL-файл** для DDL: создание таблиц, индексы, изменение структуры.
- **Go-миграция** (`goose.AddMigrationContext`) — когда нужен код:
генерация идентификаторов, backfill, перенос данных между формами.
Не пытаемся выразить это SQL-ом ради единообразия.
- **В деплое движение только вперёд.** Down-миграция — инструмент
разработки, а не отката на сервере.
- **Down пишется, когда он честно обращает up**: убрать то, что up добавил.
Не пишется, когда up необратимо трансформирует данные, — тогда его
отсутствие честнее имитации, которая молча теряет колонку.
- При изменении структуры ER-схема в спеках обновляется **в том же
изменении**, а не «потом»: разошедшаяся схема хуже отсутствующей.
## Типы колонок
- **Enum-поля** (`state`, `kind`, …) — обычный `TEXT` **без `CHECK`**.
Допустимые значения держит код. `ALTER TABLE` в SQLite не умеет менять
ограничения ни в одной версии, поэтому каждое новое значение в
`CHECK(... IN (...))` означает пересоздание таблицы по 12-шаговой
процедуре; защита от невалидного значения всё равно нужна на уровне типов
Go.
- **Метки времени** — `TEXT` в формате из `arch/time.md`. Без
`DEFAULT (datetime('now'))`: помимо того, что время ставит приложение,
эта функция даёт `YYYY-MM-DD HH:MM:SS` — без `T` и без `Z`, то есть не
тот формат.
- **Булевы** — `INTEGER` 0/1. Отдельного типа в SQLite нет, а строка
`'true'` в булевом контексте приводится к **0** — то есть тихо
инвертирует смысл, а не просто ломает фильтрацию.
- **Первичные ключи** — если репозиторий взял `arch/db-identifiers.md`, то
по ней (без `AUTOINCREMENT`); иначе автоинкремент допустим.
<!-- local:механизировано -->
<!-- /local -->
<!-- local:отступления -->
<!-- /local -->
## Связано
- `arch/time.md` — формат меток времени.
- `arch/db-identifiers.md` — выбор первичных ключей.
- `lang/go/errors.md` — граничные ошибки `database/sql` транслируются в
доменные у источника, в слое store.
<!-- local:связано -->
<!-- /local -->