# Базы данных

Версия источника: 4880e8b4372c96001c2e052be28b68a95d31252b
Синхронизировано: 2026-11-09T11:15:07.649Z
Источник: https://github.com/sergey-from-riders/vibecoding-base/tree/4880e8b4372c96001c2e052be28b68a95d31252b

## Оглавление

- [Идентификаторы: UUID и bigint](#database-identifiers)
- [Имена таблиц и полей](#database-naming)
- [Ограничения целостности](#database-constraints)
- [Внешние ключи](#database-foreign-keys)
- [Транзакции](#database-transactions)
- [Миграции схемы](#database-migrations)
- [Индексы PostgreSQL](#database-indexes)
- [Дата и время: timestamptz](#database-time-money)
- [JSONB и реляционные поля](#database-jsonb)
- [История и удаление данных](#database-history-delete)
- [Деньги: bigint, numeric и валюта](#database-money)

<a id="database-identifiers"></a>

# Идентификаторы: UUID и bigint

## Коротко

UUID удобен для независимого создания записей и публичных идентификаторов. Имя поля и его тип — разные решения.

## Плохо

```sql
CREATE TABLE orders (
  order_id bigint PRIMARY KEY
);
-- Два независимых сервиса выдали order_id = 42.
```

## Хорошо

```sql
CREATE TABLE orders (
  order_id uuid PRIMARY KEY DEFAULT uuidv7()
);
-- PostgreSQL 18: генерация без общего счётчика.
```

## Почему

UUID уменьшает зависимость от единого генератора. Он не заменяет проверку доступа; UUIDv7 раскрывает временную составляющую.

## Когда применять

Числовой identity допустим в централизованной БД. UUIDv7 есть в PostgreSQL 18; UUIDv4 через gen_random_uuid() подходит там, где v7 недоступен.

## Как проверить

- Где создаётся идентификатор? Нужен ли единый счётчик? Проверяется ли доступ независимо от UUID?

## Человек

Выбирает требования к публичности, генерации и совместимости идентификаторов.

## Агент

Готовит схему или миграцию, проверяет граничные значения и совместимость с текущими запросами.

Контроль результата:

- Где создаётся идентификатор? Нужен ли единый счётчик? Проверяется ли доступ независимо от UUID?

## Источники

- [PostgreSQL: Documentation: 18: 9.14. UUID Functions](https://www.postgresql.org/docs/18/functions-uuid.html)

Уровень: Зависит от задачи. Проверено: 2026-11-09. Изменено: 2026-11-09.

[Исходный файл](https://github.com/sergey-from-riders/vibecoding-base/blob/4880e8b4372c96001c2e052be28b68a95d31252b/registry/standards/database/postgres/rules/identifiers.md)

---

<a id="database-naming"></a>

# Имена таблиц и полей

## Коротко

Из имени поля должно быть понятно, какую связь оно хранит.

## Плохо

```sql
CREATE TABLE sessions (
  id uuid, ref uuid
);
```

## Хорошо

```sql
CREATE TABLE sessions (
  session_id uuid PRIMARY KEY,
  user_id uuid REFERENCES users(user_id)
);
```

## Почему

Явные имена помогают читать запросы с несколькими таблицами.

## Когда применять

snake_case и entity_id — соглашение этого справочника. Название id допустимо в другом последовательном соглашении; массовое переименование существующей БД не требуется.

## Как проверить

- Можно ли без комментария понять назначение ref?

## Человек

Согласует термины предметной области и существующие соглашения проекта.

## Агент

Готовит схему или миграцию, проверяет граничные значения и совместимость с текущими запросами.

Контроль результата:

- Можно ли без комментария понять назначение ref?

## Источники

- [PostgreSQL: Documentation: 18: 4.1. Lexical Structure](https://www.postgresql.org/docs/18/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS)

Уровень: Рекомендуется. Проверено: 2026-11-09. Изменено: 2026-11-09.

[Исходный файл](https://github.com/sergey-from-riders/vibecoding-base/blob/4880e8b4372c96001c2e052be28b68a95d31252b/registry/standards/database/postgres/rules/naming.md)

---

<a id="database-constraints"></a>

# Ограничения целостности

## Коротко

Проверка в приложении не защищает от конкурирующих запросов и другого клиента БД.

## Плохо

```javascript
if (!emailExists(email)) {
  insertUser(email);
}
```

## Хорошо

```sql
CREATE UNIQUE INDEX users_email_unique
  ON users (email);
```

## Почему

Два запроса могут одновременно пройти проверку отсутствия записи.

## Когда применять

Способ нормализации email и состав уникальности, включая tenant, определяет продукт.

## Как проверить

- Попробуйте два одновременных создания одной сущности.

## Человек

Определяет инварианты данных и допустимые исключения.

## Агент

Готовит схему или миграцию, проверяет граничные значения и совместимость с текущими запросами.

Контроль результата:

- Попробуйте два одновременных создания одной сущности.

## Источники

- [PostgreSQL: Documentation: 18: 5.5. Constraints](https://www.postgresql.org/docs/18/ddl-constraints.html)

Уровень: Обязательно. Проверено: 2026-11-09. Изменено: 2026-11-09.

[Исходный файл](https://github.com/sergey-from-riders/vibecoding-base/blob/4880e8b4372c96001c2e052be28b68a95d31252b/registry/standards/database/postgres/rules/constraints.md)

---

<a id="database-foreign-keys"></a>

# Внешние ключи

## Коротко

Внешний ключ не позволяет хранить ссылку на несуществующую запись.

## Плохо

```sql
user_id uuid -- связь известна только коду
```

## Хорошо

```sql
user_id uuid REFERENCES users(user_id)
  ON DELETE RESTRICT
```

## Почему

Без ограничения легко получить осиротевшие данные.

## Когда применять

CASCADE подходит только для действительно зависимых записей. В распределённом хранилище без FK нужен документированный контроль целостности.

## Как проверить

- Проверьте создание неверной ссылки и удаление родителя.

## Человек

Выбирает поведение связанных данных при удалении и переносе.

## Агент

Готовит схему или миграцию, проверяет граничные значения и совместимость с текущими запросами.

Контроль результата:

- Проверьте создание неверной ссылки и удаление родителя.

## Источники

- [PostgreSQL: Documentation: 18: 5.5. Constraints](https://www.postgresql.org/docs/18/ddl-constraints.html)

Уровень: Рекомендуется. Проверено: 2026-11-09. Изменено: 2026-11-09.

[Исходный файл](https://github.com/sergey-from-riders/vibecoding-base/blob/4880e8b4372c96001c2e052be28b68a95d31252b/registry/standards/database/postgres/rules/foreign-keys.md)

---

<a id="database-transactions"></a>

# Транзакции

## Коротко

Связанные операции должны завершиться вместе или вместе отмениться.

## Плохо

```text
Списать остаток.
Сбой.
Заказ не создан.
```

## Хорошо

```sql
BEGIN;
-- Проверить и списать остаток.
-- Создать заказ.
COMMIT;
```

## Почему

Частичный результат нарушает бизнес-инвариант.

## Когда применять

Транзакция БД не включает внешний API; для внешних эффектов используйте outbox или сверку.

## Как проверить

- Остановите операцию между шагами и проверьте целостность.

## Человек

Определяет операции, которые должны выполняться целиком.

## Агент

Готовит схему или миграцию, проверяет граничные значения и совместимость с текущими запросами.

Контроль результата:

- Остановите операцию между шагами и проверьте целостность.

## Источники

- [PostgreSQL: Documentation: 18: 3.4. Transactions](https://www.postgresql.org/docs/18/tutorial-transactions.html)

Уровень: Обязательно. Проверено: 2026-11-09. Изменено: 2026-11-09.

[Исходный файл](https://github.com/sergey-from-riders/vibecoding-base/blob/4880e8b4372c96001c2e052be28b68a95d31252b/registry/standards/database/postgres/rules/transactions.md)

---

<a id="database-migrations"></a>

# Миграции схемы

## Коротко

Выпускайте несовместимые изменения в несколько совместимых этапов.

## Плохо

```text
Удалить колонку → выпустить новый код.
```

## Хорошо

```text
Добавить новое поле → заполнить порциями →
переключить чтение → позже удалить старое.
```

## Почему

Во время выпуска старый и новый код могут работать одновременно.

## Когда применять

Для production с данными. Уже применённые миграции не переписываются; откат данных не всегда возможен через down.

## Как проверить

- Проверены блокировки, время заполнения и старый код на новой схеме?

## Человек

Согласует окно выпуска, совместимость и допустимые блокировки.

## Агент

Готовит схему или миграцию, проверяет граничные значения и совместимость с текущими запросами.

Контроль результата:

- Проверены блокировки, время заполнения и старый код на новой схеме?

## Источники

- [PostgreSQL: Documentation: 18: ALTER TABLE](https://www.postgresql.org/docs/18/sql-altertable.html)
- [Editing a migration in Prisma ORM](https://www.prisma.io/docs/orm/prisma-migrate/workflows/customizing-migrations)

Уровень: Обязательно. Проверено: 2026-11-09. Изменено: 2026-11-09.

[Исходный файл](https://github.com/sergey-from-riders/vibecoding-base/blob/4880e8b4372c96001c2e052be28b68a95d31252b/registry/standards/database/postgres/rules/migrations.md)

---

<a id="database-indexes"></a>

# Индексы PostgreSQL

## Коротко

Индекс ускоряет выбранное чтение и увеличивает стоимость записи.

## Плохо

```text
Создать индекс на каждой колонке.
```

## Хорошо

```text
Найти медленный запрос → EXPLAIN →
добавить подходящий индекс → измерить.
```

## Почему

Лишние индексы расходуют место и замедляют обновления.

## Когда применять

Индексы FK на стороне ссылки не создаются PostgreSQL автоматически. Для связей и фильтров учитывайте объём и план запроса.

## Как проверить

- У индекса есть запрос и сравнение времени до и после?

## Человек

Задаёт приоритетные запросы и допустимую стоимость записи.

## Агент

Готовит схему или миграцию, проверяет граничные значения и совместимость с текущими запросами.

Контроль результата:

- У индекса есть запрос и сравнение времени до и после?

## Источники

- [PostgreSQL: Documentation: 18: Chapter 11. Indexes](https://www.postgresql.org/docs/18/indexes.html)
- [PostgreSQL: Documentation: 18: 14.1. Using EXPLAIN](https://www.postgresql.org/docs/18/using-explain.html)
- [PostgreSQL: Documentation: 18: 5.5. Constraints](https://www.postgresql.org/docs/18/ddl-constraints.html#DDL-CONSTRAINTS-FK)

Уровень: Рекомендуется. Проверено: 2026-11-09. Изменено: 2026-11-09.

[Исходный файл](https://github.com/sergey-from-riders/vibecoding-base/blob/4880e8b4372c96001c2e052be28b68a95d31252b/registry/standards/database/postgres/rules/indexes.md)

---

<a id="database-time-money"></a>

# Дата и время: timestamptz

## Коротко

Момент времени и местное расписание — разные данные. PostgreSQL `timestamptz` хранит момент, но не исходное название часового пояса.

## Плохо

```sql
CREATE TABLE meeting (starts_at text);
-- '09.11.2026 10:00' без формата и часового пояса
```

## Хорошо

```sql
CREATE TABLE meeting (
  starts_at timestamptz NOT NULL,
  time_zone text NOT NULL
);
-- '2026-11-09T10:00:00+03:00', 'Europe/Moscow'
```

## Почему

Без зоны одна строка может означать разные моменты. Для будущего повторяющегося расписания нужен местный час и название зоны, а не только одна UTC-метка.

## Когда применять

PostgreSQL 18: `timestamptz` — события и аудит; `date` — календарная дата; `timestamp without time zone` вместе с зоной и правилом повтора — локальное расписание. Не добавляйте зону к дате рождения. Денежные типы вынесены в отдельное правило `database.money`.

## Как проверить

- Проверены преобразования UTC, разные зоны и переходы летнего времени?
- Различаются дата, момент и повторяющееся расписание?

## Человек

Определяет, хранится ли точный момент, местная дата или повторяющееся расписание.

## Агент

Выбирает типы по согласованной семантике, проверяет преобразования зон и неоднозначное местное время.

## Источники

- [PostgreSQL: Documentation: 18: 8.5. Date/Time Types](https://www.postgresql.org/docs/18/datatype-datetime.html)

Уровень: Рекомендуется. Проверено: 2026-11-09. Изменено: 2026-11-09.

[Исходный файл](https://github.com/sergey-from-riders/vibecoding-base/blob/4880e8b4372c96001c2e052be28b68a95d31252b/registry/standards/database/postgres/rules/time-money.md)

---

<a id="database-jsonb"></a>

# JSONB и реляционные поля

## Коротко

Поля, участвующие в доступе, связях и частом поиске, нуждаются в явной модели.

## Плохо

```text
Весь заказ — один JSON без ограничений.
```

## Хорошо

```text
order_id, tenant_id, total_minor — колонки.
provider_snapshot — JSONB.
```

## Почему

Гибкое поле не отменяет проверку структуры и прав.

## Когда применять

JSONB полезен для внешних снимков и разреженных настроек; запросы внутри JSONB тоже можно индексировать.

## Как проверить

- Какие поля нужны для связей, прав и отчётов?

## Человек

Определяет стабильные бизнес-поля и изменяемые метаданные.

## Агент

Готовит схему или миграцию, проверяет граничные значения и совместимость с текущими запросами.

Контроль результата:

- Какие поля нужны для связей, прав и отчётов?

## Источники

- [PostgreSQL: Documentation: 18: 8.14. JSON Types](https://www.postgresql.org/docs/18/datatype-json.html)

Уровень: Зависит от задачи. Проверено: 2026-11-09. Изменено: 2026-11-09.

[Исходный файл](https://github.com/sergey-from-riders/vibecoding-base/blob/4880e8b4372c96001c2e052be28b68a95d31252b/registry/standards/database/postgres/rules/jsonb.md)

---

<a id="database-history-delete"></a>

# История и удаление данных

## Коротко

Мягкое удаление, история версий и аудит решают разные задачи.

## Плохо

```text
deleted_at есть, но все запросы читают удалённое.
```

## Хорошо

```text
Чтение учитывает deleted_at.
Уникальность и восстановление проверены.
Срок окончательной очистки определён.
```

## Почему

Флаг удаления сам по себе не удаляет данные и не создаёт историю изменений.

## Когда применять

Аудит фиксирует, кто и что изменил; история версий позволяет восстановить прежнее содержимое. Выберите нужные механизмы отдельно. При soft delete проверьте уникальность активных записей, восстановление и окончательную очистку. Срок хранения задаёт политика проекта.

## Как проверить

- Проверьте восстановление, повторное создание и очистку связанных данных.

## Человек

Определяет сроки хранения, восстановление и требования к удалению.

## Агент

Готовит схему или миграцию, проверяет граничные значения и совместимость с текущими запросами.

Контроль результата:

- Проверьте восстановление, повторное создание и очистку связанных данных.

## Источники

- [PostgreSQL: Documentation: 18: 11.8. Partial Indexes](https://www.postgresql.org/docs/18/indexes-partial.html)
- [PostgreSQL: Documentation: 18: 41.10. Trigger Functions](https://www.postgresql.org/docs/18/plpgsql-trigger.html)

Уровень: Зависит от задачи. Проверено: 2026-11-09. Изменено: 2026-11-09.

[Исходный файл](https://github.com/sergey-from-riders/vibecoding-base/blob/4880e8b4372c96001c2e052be28b68a95d31252b/registry/standards/database/postgres/rules/history-delete.md)

---

<a id="database-money"></a>

# Деньги: bigint, numeric и валюта

## Коротко

Сумма хранится вместе с валютой и единицей измерения. Для расчётов используйте целые минимальные единицы или точный десятичный тип.

## Плохо

```sql
CREATE TABLE price (amount double precision);
-- 123.45: неизвестны валюта и политика округления
```

## Хорошо

```sql
CREATE TABLE price (
  amount_minor bigint NOT NULL CHECK (amount_minor >= 0),
  currency text NOT NULL CHECK (currency = 'RUB')
);
INSERT INTO price VALUES (12345, 'RUB'); -- 123 руб. 45 коп.
```

## Почему

`float`, `real` и `double precision` не гарантируют точное представление десятичных денежных дробей. Проверка в БД удерживает формат даже при записи другим сервисом.

## Когда применять

PostgreSQL 18. Тип выбирается по единице измерения и максимальной сумме:

- `integer` / `int`: целые минимальные единицы в доказанном диапазоне. Максимум 2 147 483 647, то есть 21 474 836,47 RUB при хранении копеек.
- `bigint`: целые минимальные единицы, если диапазона `integer` недостаточно. У него тоже есть предел; это не бесконечный тип.
- `numeric(p,s)` / `decimal(p,s)`: точные десятичные суммы, дробные тарифы и курсы. `numeric(20,6)` допускает 14 целых и 6 дробных разрядов. Точность и округление задаёт предметная область.

В примере поддерживается только RUB. Для нескольких валют нужна проверяемая таблица валют и их разрядности: не у всех валют две цифры после запятой; API провайдера может иметь исключения. Отрицательные суммы разрешайте для проводок, если это требует модель. PostgreSQL `money` зависит от денежных настроек локали, поэтому для переносимого контракта предпочтительнее явно заданные единицы.

В JavaScript `Number` гарантирует точность целых только в безопасном диапазоне от −(2^53 − 1) до 2^53 − 1. Большие `bigint` передавайте в JSON строкой; преобразование и арифметику выполняйте без промежуточного `float`.

## Человек

Определяет валюты, максимальные суммы, точность тарифов и правила округления/возвратов.

## Агент

Готовит поля БД, ограничения и сериализацию. Проверяет точность на граничных суммах и сверяет единицы платёжного API.

## Пример применения

Магазин принимает RUB: цена 1999,90 ₽ хранится как `199990` в `amount_minor bigint`. Комиссия рассчитывается десятичной арифметикой, округляется по согласованному правилу до копеек и только затем сохраняется. Для бухгалтерских проводок создаётся отдельная модель с допустимым отрицательным знаком.

## Как проверить

- 12345 минимальных единиц RUB преобразуются в 123,45 ₽ и обратно без потери копеек.
- Проверены предел типа, отрицательные значения, дробное деление и округление.
- В API описаны валюта, единица и строковый формат больших целых.

## Источники

- [PostgreSQL: Documentation: 18: 8.1. Numeric Types](https://www.postgresql.org/docs/18/datatype-numeric.html)
- [PostgreSQL: Documentation: 18: 8.2. Monetary Types](https://www.postgresql.org/docs/18/datatype-money.html)
- [Supported currencies](https://docs.stripe.com/currencies)
- [Number.MAX_SAFE_INTEGER - JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)

Уровень: Рекомендуется. Проверено: 2026-11-09. Изменено: 2026-11-09.

[Исходный файл](https://github.com/sergey-from-riders/vibecoding-base/blob/4880e8b4372c96001c2e052be28b68a95d31252b/registry/standards/database/postgres/rules/money.md)
