Описание базы данных у меня менялось от проекта к проекту — каждый раз я пытался улучшить подход. На одном проекте я вёл всю документацию по БД в dbdiagram. Схема выглядела красиво, но поддерживать её было тяжело, а главное — в ней не было места для бизнес-логики данных: что вычисляется, откуда берётся, при каких условиях заполняется. Голая структура без контекста.
На другом проекте я пошёл в обратную сторону — описывал только бизнесовые сущности, без технических деталей. Никаких связей между таблицами, никакой информации о том, что одна сущность в реальности разбита на несколько таблиц. Разработчики получали описание «Договор», а что за ним стоят три таблицы с разными ключами — додумывали сами. Вопросы сыпались постоянно.
В обоих случаях у меня была одна и та же проблема: писать требования к данным было мучительно. Приходилось дублировать информацию — то со схемы, то с других страниц вики. А дублирование всегда ведёт к одному: таблицы устаревают, связи теряются, ошибки множатся.
Со временем я выделил для себя три задачи, которые закрывают требования к данным: описание изменений в таблицах и маппинг, миграции существующих данных и описание данных вне БД (enum'ы). Это не строгое разделение: описание некоторых таблиц может оказаться невозможным без описания миграции, а enum'ы могут также требовать маппинга. Поэтому шаблоны гибкие и могут содержать разную информацию в зависимости от самого требования. Но прежде чем писать требования, нужен фундамент — документированная база данных.
Документирование базы данных в вики
Я работаю с PostgreSQL, поэтому примеры и структура заточены под реляционные базы данных.
Зачем вообще описывать базу данных в вики — разве физической структуры недостаточно? Самое важное тут, что именно описание в вики позволяет видеть не только структуру базы, но и логику данных — и писать точные требования. Физическая модель может быть перегружена таблицами связей и лишними данными. Её нельзя исключать, но не на ней фокус во время написания требований.
Чтобы документация по базе данных была наглядна и полна, я использую следующий подход: описываю базу данных в диаграмме, но в ней только основные таблицы, без таблиц связей и служебных, а из данных — только внешние и первичные ключи. Всё описание таблиц вместе с содержимым и бизнес-логикой хранится на отдельных страницах. То есть я описываю именно концептуальную схему, а если нужно во время написания требований указать таблицу связей — указываю её только там. Такие таблицы не имеют самостоятельного веса, они нужны только в контексте конкретных требований.
Для описания базы данных важна целая структура в вики, а не просто шаблон страницы.
> База данных >> Схема (public) >>> Таблица (order) >>> Таблица (customer) >> Схема (integrations) >>> Таблица (sync_tasks)
Три уровня. На уровне схемы — просто группировка таблиц по PostgreSQL-схемам. Интереснее два других.
Уровень «База данных»
На корневой странице живёт концептуальная ER-диаграмма и общие правила. Структура:
Информация — назначение страницы. Ничего индивидуального придумывать не нужно, просто дать понять читателю, что перед ним.
ER-диаграмма — скриншот и ссылка на редактор (я использую dbdiagram.io). Под диаграммой — пояснение, что схема концептуальная и не содержит всех таблиц.
Архитектурные принципы — то, что относится ко всей базе:
Модель и нормальная форма
Стандарт именования
Общие данные по таблицам — то, что повторяется везде и не имеет смысла дублировать на каждой странице. Например, аудитные атрибуты. Вынос их на уровень базы данных — не мелочь. Если описывать на каждой странице таблицы, при изменении придётся обновлять десятки страниц. Здесь — обновил один раз.
Шаблон
# База данных — заголовок страницы ## Информация **Назначение:** ## ER-диаграмма [скриншот] [ссылка на редактор] ## Архитектурные принципы **Модель:** **Именование:** ### Общие атрибуты таблиц | Атрибут | Тип | Описание | Ограничения | | --- | --- | --- | --- | | | | | |
Пример заполнения
База данных
Информация
Назначение: На этой странице представлена общая концептуальная схема базы данных системы. Она показывает основные бизнес-сущности и их взаимосвязи на высоком уровне
ER-диаграмма

ER-диаграмма концептуальная. Из нее удалены таблицы связей много-ко-многим и другие таблицы, не имеющие бизнес-ценности, но в физической модели эти таблицы есть. Все атрибуты для всех таблиц можно найти на отдельных страницах
Архитектурные принципы
Модель: Реляционная, 1НФ
Именование: Используется нотация snake_case для всех объектов БД
Во многих таблицах есть аудитные атрибуты
Атрибут | Тип | Описание | Ограничения |
|---|---|---|---|
created_at |
| Дата и время создания записи | |
created_by |
| Идентификатор пользователя, создавшего запись | |
updated_at |
| Дата и время обновления записи | |
updated_by |
| Идентификатор пользователя, обновившего запись | |
deleted_at |
| Дата и время удаления з��писи | |
deleted_by |
| Идентификатор пользователя, удалившего запись |
Уровень «Таблица»
Каждая бизнесовая таблица — отдельная страница. Структура:
Информация — назначение таблицы. Назначение — одно предложение о том, что хранит таблица.
Атрибуты — таблица с полями: название, тип, описание, ограничения. Первичные ключи помечаю 🔑, внешние — 🔐. Вычисляемые поля помечаю явно.
Ключи и связи — первичные, внешние и уникальные ключи отдельным блоком.
Аудитные поля — если стандартные, ссылаюсь на страницу БД. Если набор отличается — указываю явно.
Бизнес-правила — формулы вычисляемых полей, условия заполнения, зависимости между атрибутами. Это то, ради чего стоит вести описание таблиц в вики — этой информации нет в физической модели, и без неё при написании требований приходится каждый раз заново разбираться в логике.
Шаблон
# [название таблицы] — заголовок страницы ## Информация **Назначение:** ## Атрибуты | Атрибут | Тип | Описание | Ограничения | | --- | --- | --- | --- | | | | | | ## Ключи и связи * **🔑 Первичный ключ:** * **🔐 Внешние ключи:** * **Уникальные ключи:** ## Аудитные поля ## Бизнес-правила
Пример заполнения
order
Информация
Назначение: хранение данных о заказах клиентов
Атрибуты
Атрибут | Тип | Описание | Ограничения |
|---|---|---|---|
🔑 id |
| Идентификатор заказа | NOT NULL |
🔐 customer_id |
| Идентификатор клиента | NOT NULL |
status |
| Статус заказа | NOT NULL, DEFAULT 'CREATED' |
total_amount |
| (вычисляемое поле) Итоговая сумма заказа | NOT NULL |
discount_amount |
| Сумма скидки | DEFAULT 0 |
final_amount |
| (вычисляемое) Итоговая сумма к оплате | NOT NULL |
Ключи и связи
🔑 Первичный ключ:
id🔐 Внешние ключи:
customer_id→customer.idУникальные ключи: —
Аудитные поля
Стандартные (описаны на странице БД)
Бизнес-правила
final_amount = total_amount - discount_amountЗаказ не может быть удалён, если его статус
DELIVERED
Требования к данным: таблицы и маппинг
Когда база данных описана, требования к данным становятся лаконичнее и понятнее. Не нужно дублировать всё содержимое таблицы, чтобы отразить её итоговое состояние: достаточно написать только те атрибуты, которые будут добавлены, и указать, какие будут удалены или изменены (и как).
Требования к таблицам и маппингу состоят из ER-диаграммы изменений, описания изменений, миграций (если есть), бизнес-правил (если есть изменения) и раздела с маппингом (если есть).
ER-диаграмма — это не полное содержимое таблиц. Она должна показывать изменения. В ней отображаются все таблицы, которые затронуты в требовании, их связи, а содержимое таблиц — только те атрибуты, которые изменяются или добавляются.
Описание изменений — в табличном виде для каждой таблицы выписываются атрибуты и их изменения. Здесь также появляются атрибуты, которые подлежат удалению.
Миграции — указываются при наличии правил переноса или заполнения информации в атрибутах таблицы.
Бизнес-правила — описываются новые правила, касающиеся таблицы. Если какое-то правило удаляется, оно тоже присутствует и обозначается как подлежащее удалению.
Маппинг — табличный вид сопоставления полей между внешним ресурсом и проектируемыми таблицами базы данных.
Если таблица новая — она описывается полностью и имеет такую же структуру, как описание таблицы в вики. Эта страница также создаётся в структуре описания базы данных — информация идёт туда. Зачем тогда дублировать? Требования к данным должны быть самодостаточными, чтобы разработчик не перескакивал со страницы на страницу и видел все изменения здесь и сразу. Ссылки на другие ресурсы нужны, но они должны дополнять информацию, а не быть обязательными для посещения.
Шаблон
# [ТД-N] Название требования — заголовок страницы ## ER-диаграмма [скриншот] [ссылка на редактор] ## Изменения в базе данных ### Таблица [название] — добавление полей | Поле | Тип | Описание | Ограничения | | --- | --- | --- | --- | | | | | | ### Таблица [название] — изменение полей | Поле | Изменение | Было | Стало | | --- | --- | --- | --- | | | | | | **Миграция данных:** **Бизнес-правила:** ### Таблица [название] — новая **Назначение:** **Атрибуты** | Атрибут | Тип | Описание | Ограничения | | --- | --- | --- | --- | | | | | | **Ключи и связи** * **🔑 Первичный ключ:** * **🔐 Внешние ключи:** * **Уникальные ключи:** **Аудитные поля** **Бизнес-правила** ## Маппинг полей (Источник → БД) | Поле источника | Поле БД | Комментарий | | --- | --- | --- | | | | | ## Таблицы * [список затронутых таблиц со ссылками]
Как работает на практике
[ТД-4] Внедрение программы лояльности
ER-диаграмма

Изменения в базе данных
Таблица order — добавление полей
Поле | Тип | Описание | Ограничения |
|---|---|---|---|
points_earned |
| Количество баллов, начисленных за заказ | NOT NULL, DEFAULT 0 |
points_used |
| Количество баллов, списанных при оплате | NOT NULL, DEFAULT 0 |
Миграция данных:
Для всех существующих заказов со статусом
DELIVEREDустановитьpoints_earned = floor(final_amount / 100)Для всех существующих заказов установить
points_used = 0
Бизнес-правила:
points_earnedзаполняется при смене статуса заказа наDELIVEREDpoints_usedне может превышать доступный баланс баллов клиентаpoints_usedне может превышатьfinal_amountзаказа (в пересчёте 1 балл = 1 рубль)
Таблица loyalty_points — новая
Назначение: хранение транзакций по бонусным баллам клиентов
Атрибуты
Атрибут | Тип | Описание | Ограничения |
|---|---|---|---|
🔑 id |
| Идентификатор транзакции | NOT NULL |
🔐 customer_id |
| Идентификатор клиента | NOT NULL |
🔐 order_id |
| Идентифик��тор заказа | NOT NULL |
type |
| Тип транзакции: | NOT NULL |
amount |
| Количество баллов | NOT NULL, CHECK (>0) |
balance |
| (вычисляемое поле) Баланс баллов клиента после транзакции | NOT NULL |
Ключи и связи
🔑 Первичный ключ:
id🔐 Внешние ключи:
customer_id→customer.id,order_id→order.idУникальные ключи: —
Аудитные поля
Стандартные (описаны на странице БД)
Бизнес-правила
balance= суммаamountгдеtype = 'EARN'минус суммаamountгдеtype = 'SPEND'для данногоcustomer_idbalanceне может быть отрицательным
Маппинг полей (Loyalty API → БД)
loyalty_points
Поле API | Поле БД | Комментарий |
|---|---|---|
|
| |
|
| Маппинг: найти |
|
| Маппинг: найти |
|
|
|
|
| |
— |
| Вычисляется в БД |
order
Поле API | Поле БД | Комментарий |
|---|---|---|
|
| |
|
|
Таблицы
Требования к данным: миграции
Есть задачи, в которых нужно изменять не сами таблицы, а существующую в них информацию. Например, при появлении нового бизнес-правила старые данные могут оказаться в неконсистентном состоянии и их нужно привести в порядок. Полноценная страница с ER-диаграммой и описанием таблиц здесь не требуется. Достаточно описать суть изменений.
# [ТД-N] Название миграции — заголовок страницы ## Изменения в базе данных **Описание:** **Миграция данных:** **Таблицы**
Как работает на практике
[ТД-5] Миграция данных: начисление бонусных баллов по существующим заказам
Изменения в базе данных
Описание: После внедрения программы лояльности система начисляет баллы за заказы автоматически. Существующие заказы со статусом DELIVERED не имеют начисленных баллов. Необходимо привести данные в консистентное состояние.
Миграция данных:
Для всех заказов со статусом
DELIVEREDиpoints_earned = 0: установитьpoints_earned = floor(final_amount / 100)Для каждого такого заказа создать запись в
loyalty_pointsсtype = 'EARN',amount = points_earnedПересчитать
balanceвloyalty_pointsдля каждого клиентаИспользуется soft-подход:
created_byне заполняется — системная миграция (NULL)
Таблицы
Требования к данным: enum’ы
Данные существуют не только в рамках БД, но и в коде. Чаще всего я встречался с enum'ами, другие виды данных в коде — редко. Поэтому заострю внимание именно на них.
Описание требований к enum'ам чем-то похоже на описание изменений в таблице БД, но короче:
Название enum
Назначение — для чего создаётся/существует enum и как от него зависят другие сущности: например, используется как query-параметр или нужен для валидации
Значения — записываются в табличном виде. Заголовки таблицы определяют атрибуты enum, поэтому этот раздел убивает сразу двух зайцев
Миграции (если есть)
Бизнес-правила (если есть)
Примечание (если есть) — для информации, которая не помещается в остальные пункты шаблона. Чаще отсутствует, но нужно его учитывать
Отдельная тема — enum'ы статусов, которые по сути описывают конечный автомат с переходами между состояниями. Это заслуживает отдельного разговора.
# [ТД-N] Название требования — заголовок страницы ## Изменения в коде ## [Название enum] **Назначение:** ### Значения | Значение | ... | ... | | --- | --- | --- | | | | | ### Миграции ### Бизнес-правила ### Примечание
Как работает на практике
[ТД-6] Создание enum'а для типов транзакций лояльности
Изменения в коде
PointsTransactionType
Назначение:
Определение типов транзакций в программе лояльности
Enum используется для валидации поля
typeв таблицеloyalty_pointsИспользуется как query-параметр в GET /loyalty/transactions для фильтрации по типу
Значения
Значение | displayName | Описание |
|---|---|---|
EARN | Начисление | Начисление баллов за завершённый заказ |
SPEND | Списание | Списание баллов при оплате заказа |
EXPIRE | Сгорание | Автоматическое сгорание баллов по истечении срока |
Бизнес-правила
При создании транзакции с типом
SPENDпроверяется, что баланс клиента не станет отрицательнымТранзакции с типом
EXPIREсоздаются автоматически по расписанию
Примечание
Enum OrderStatus остаётся без изменений — новых статусов для программы лояльности не требуется
Итог
Работа с требованиями к данным — комплексная задача. Хорошо описанная база данных в вики помогает сэкономить много времени при написании требований. Как вы описываете изменения в базе данных — в отдельных документах или прямо в задачах для разработки?
