Перейти к основному содержимому

Data baseline

Зачем нужно

Документ задаёт обязательные правила работы с PostgreSQL для всех 7 доменов: типы, ключи, времена, миграции, идемпотентность, retention.

БД на сервис

  • Каждый backend-сервис имеет свою отдельную БД PostgreSQL 16+.
  • Один сервис не имеет прямого доступа к чужой БД.
  • Нет shared schemas.

Расширения

CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS citext;
CREATE EXTENSION IF NOT EXISTS pg_trgm;

Идентификаторы

  • Primary key: id UUID PRIMARY KEY DEFAULT gen_random_uuid().
  • Внешние ссылки на сущности других доменов хранятся как UUID без FK (они в чужой БД).
  • Внешние provider IDs хранятся отдельным столбцом + столбцом provider.

Naming

  • Имена таблиц — snake_case во множественном числе (crm_payments, lms_courses, external_teacher_profiles).
  • Имена логических сущностей в data-model, ownership и ADR — snake_case в единственном числе (crm_entitlement_consumption_log, external_teacher_profile).
  • Если логическая сущность и таблица отличаются только singular/plural, это не считается naming drift.

Время

  • Все timestamps — TIMESTAMPTZ, хранение в UTC.
  • Стандартные столбцы: created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now().
  • Триггер на updated_at или явный update в коде через ORM.

Soft delete

  • Допустим для бизнес-сущностей: deleted_at TIMESTAMPTZ.
  • Запрещён для финансовых, security и audit записей.
  • Чтение по умолчанию исключает deleted_at IS NOT NULL (через query helper).

Денежные значения

  • NUMERIC(12,2) или NUMERIC(14,2).
  • Никогда FLOAT/DOUBLE.
  • Валюта отдельной колонкой CHAR(3).

Email и phone

  • Email: CITEXT, lowercased, нормализован.
  • Phone: TEXT в E.164 (с +), без спецсимволов.

JSON

  • JSONB для расширяемых metadata.
  • Не хранить обязательные связи в JSON.
  • Индексы на JSONB: GIN или функциональные.

Индексы

  • На все FK по которым ходим;
  • на колонки фильтрации списков;
  • partial-индексы для deleted_at IS NULL;
  • уникальные ограничения на бизнес-ключи (UNIQUE (account_id, identity_user_id, role) и т.п.).

Constraints

  • NOT NULL по умолчанию; nullable требует обоснования;
  • CHECK на enum-подобные строки или используем CREATE TYPE ... AS ENUM;
  • FK ... ON DELETE явный (CASCADE/RESTRICT/SET NULL);
  • UNIQUE для естественных уникальных значений (slug, email, phone+is_primary).

Enums

Для статусов, типов, ролей внутри домена — CREATE TYPE. Изменение enum-значения добавляется (PostgreSQL не позволяет удаление без пересоздания), удаления — через миграцию данных.

Идемпотентность

  • Таблица idempotency_keys:
    key TEXT PRIMARY KEY,
    scope TEXT NOT NULL,
    request_hash TEXT NOT NULL,
    response_status INT NOT NULL,
    response_body JSONB NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    expires_at TIMESTAMPTZ NOT NULL
  • TTL — 24 часа (для финансовых — 7 дней).

Outbox

  • Для гарантированной публикации событий — таблица outbox:
    message_id UUID PRIMARY KEY,
    message_kind TEXT NOT NULL CHECK (message_kind IN ('event','command')),
    message_type TEXT NOT NULL,
    message_payload JSONB NOT NULL,
    occurred_at TIMESTAMPTZ NOT NULL,
    published_at TIMESTAMPTZ,
    attempts INT NOT NULL DEFAULT 0
  • Запись в outbox в одной транзакции с бизнес-данными;
  • background-publisher отправляет в шину и помечает published_at.

Inbox

  • Для дедупликации входящих событий — таблица processed_messages:
    message_id UUID PRIMARY KEY,
    message_kind TEXT NOT NULL CHECK (message_kind IN ('event','command')),
    message_type TEXT NOT NULL,
    processed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    expires_at TIMESTAMPTZ NOT NULL
  • TTL ≥ 14 дней для normal retry window.
  • Replay за пределами TTL processed_messages выполняется только в explicit replay mode: без side effects, через business idempotency key, или в отдельный snapshot/watermark target. Financial consumers обязаны хранить business idempotency keys весь срок финансового retention.

Audit

  • Логическая таблица audit_log в каждом домене (см. observability.md). Физическое имя может быть audit_log в отдельной service DB или <domain>_audit_logs в shared/dev DB, но контракт полей, append-only и retention едины.
  • Append-only: триггер запрещает UPDATE/DELETE.
  • Партиционирование по месяцу для retention.

Миграции

  • Инструмент: Prisma Migrate.
  • Каждая миграция — атомарная (один логический change).
  • Миграции откатываемые: где возможно — обратная миграция.
  • Никаких миграций, ломающих обратную совместимость со старой версией кода (zero-downtime: 1) добавить колонку nullable, 2) задать default, 3) задеплоить код, 4) сделать NOT NULL).
  • Long-running data migrations выполняются вне миграции схемы (отдельной фоновой задачей).

Zero-downtime для breaking changes

ChangeПроцедура
DROP COLUMN1) перестать читать/писать колонку в коде, 2) задеплоить, 3) проверить отсутствие обращений по логам, 4) удалить колонку отдельной миграцией
RENAME COLUMN1) добавить новую колонку, 2) dual-write, 3) backfill, 4) перевести read на новую, 5) удалить старую после окна совместимости
ALTER TYPE enum delete/rename1) добавить новое значение, 2) мигрировать данные, 3) перевести код, 4) пересоздать enum в maintenance migration только после отсутствия старых значений
CHANGE TYPE1) добавить новую колонку нужного типа, 2) backfill батчами, 3) dual-write, 4) переключить read, 5) удалить старую
NOT NULL1) добавить nullable, 2) backfill, 3) задеплоить код с обязательной записью, 4) добавить NOT NULL

Retention и партиционирование

  • Аудит — 7 лет, партиционирование по месяцу;
  • Logs/messages outbox — TTL по политике из observability.md;
  • User PII — пока активен пользователь и 6 месяцев после soft delete; затем анонимизация.

Бэкапы

  • Daily full + WAL archiving.
  • Retention бэкапов: 30 дней daily + 12 monthly.
  • Тестовое восстановление раз в квартал.

Запрещено

  • Использовать SERIAL/BIGSERIAL для PK новых таблиц.
  • Хранить пароли plain.
  • Хардкодить ENV-зависимые ID.
  • Кросс-сервисные FK между БД.
  • Изменять опубликованные миграции.

Связанные документы