Ошибки в структуре данных всплывают не сразу. Первые месяцы всё работает: товары добавляются, заказы оформляются, отчёты выгружаются. Проблемы начинаются через год, когда появляется вторая складская точка, старые заказы вдруг «переоцениваются» по новому прайсу, а простая просьба «покажи выручку по филиалам за прошлый квартал» оказывается невыполнимой. Переделывать в этот момент дорого: данных много, они уже связаны с бухгалтерией и рекламой. Ниже — правила, которые мы соблюдаем при проектировании, чтобы этой ситуации не было.
С чего начинается проектирование
До первой таблицы мы отвечаем на четыре вопроса вместе с заказчиком. Это занимает пару встреч и экономит месяцы переделок.
- Какие сущности есть в бизнесе. Не «что будет на сайте», а что реально существует: клиент, заказ, товар, услуга, договор, объект, сотрудник, филиал.
- Как они связаны. Один клиент — много заказов. Один заказ — много позиций. Товар может быть в нескольких категориях. Эти связи важнее, чем набор полей.
- Что должно храниться исторически. Что именно нельзя терять при изменениях: цены, статусы, состав заказа, переписку.
- Какие отчёты понадобятся. Отчёты определяют структуру не меньше, чем сами данные. Если нужен разрез по филиалам, филиал должен быть отдельной сущностью, а не текстом в примечании.
Схема, нарисованная на этом этапе на одном листе, обычно понятнее любого технического задания и лучше проверяется заказчиком.
Базовые принципы
- Одна сущность — одна таблица. Товары, заказы, клиенты хранятся отдельно и связываются между собой, а не складываются в общую таблицу с полем «тип записи».
- У каждой записи постоянный идентификатор. Он не меняется никогда, даже если поменялось название, артикул или адрес. Всё остальное — атрибуты, которые могут меняться.
- Связи через идентификаторы, а не через названия. Название компании клиент однажды напишет иначе, и связь развалится молча.
- Единый формат дат, сумм и телефонов. Формат задаётся на уровне хранения, а не на уровне договорённостей между разработчиками.
- Историю не переписываем. Цена, наименование и состав в заказе фиксируются на момент покупки и живут вместе с заказом.
- Одно значение — одно поле. Ни списков через запятую, ни «города и области» в одной строке.
Типичные ошибки и чем они оборачиваются
| Ошибка | Чем оборачивается | Как правильно |
|---|---|---|
| Список значений через запятую в одном поле | Нельзя фильтровать, считать и связывать | Отдельная таблица связей |
| Цена только в справочнике товара | Старые заказы пересчитываются по новому прайсу | Копия цены и наименования в позиции заказа |
| Физическое удаление записей | Теряется история и ломаются связи | Пометка об удалении, запись остаётся |
| Универсальная таблица «ключ-значение» для всего | Медленные запросы, невозможность проверить данные | Явные поля для того, что известно заранее |
| Разные форматы телефонов и почты | Дубли клиентов, письма не доходят | Нормализация при сохранении |
| Статус заказа текстом в свободной форме | «Отгружен», «отгружено», «Отгр.» — три разных статуса | Ограниченный список значений |
| Хранение вычисляемых сумм без исходных данных | Сумму нельзя перепроверить и пересчитать | Хранить и составляющие, и итог |
Что важно заложить сразу
Есть вещи, которые почти ничего не стоят в начале и дорого добавляются потом.
- Даты создания и изменения у всех основных сущностей. Без них невозможно разобраться, когда и что произошло.
- Кто изменил запись. В системах, где работают несколько человек, это первый вопрос при любом разборе.
- Мягкое удаление там, где важна история: клиенты, заказы, документы.
- Индексы под частые запросы. Поиск по телефону, выборка заказов клиента, фильтр по статусу и датам.
- Ограничения целостности. Нельзя создать заказ без клиента, позицию без заказа, платёж без договора. Проверка на уровне базы надёжнее проверки в коде, потому что данные попадают в базу не только через интерфейс.
- Внешние идентификаторы для связи с учётной системой, если обмен планируется хотя бы теоретически.
Про историю и «текущее состояние»
Самая частая архитектурная ошибка — хранить только текущее состояние. Пока система маленькая, это удобно: одна строка, одно значение. Как только появляется вопрос «а какой был статус на конец месяца» или «почему в отчёте за прошлый год другая сумма», выясняется, что ответа нет.
Мы разделяем данные на три типа и обращаемся с ними по-разному:
| Тип данных | Пример | Как храним |
|---|---|---|
| Справочники | Товары, услуги, сотрудники | Текущее состояние, изменения допустимы |
| Документы | Заказ, счёт, договор | Фиксируются целиком на момент создания |
| События | Смена статуса, платёж, звонок | Только добавление, ничего не переписывается |
Такое разделение снимает большинство споров о том, где хранить цену и что делать при переименовании товара.
Производительность закладывается структурой
Медленная система редко бывает медленной из-за сервера. Чаще причина в структуре и запросах.
- Индексы ставим под те запросы, которые действительно выполняются, а не под каждое поле: лишние индексы замедляют запись.
- Списки с постраничным выводом делаем так, чтобы не считать всю таблицу при каждом открытии страницы.
- Тяжёлые отчёты считаем отдельно и сохраняем результат, а не пересчитываем при каждом открытии.
- Заранее решаем, что делать с ростом: миллион строк в таблице заказов ведёт себя не так, как десять тысяч.
- Проверяем систему на объёме, близком к боевому, а не на двадцати тестовых записях.
Структура будет меняться — это нормально
Ни одна схема не остаётся неизменной. Бизнес меняется, появляются новые сущности и разрезы. Важно не угадать всё заранее, а сделать изменения предсказуемыми.
- Миграции хранятся в репозитории вместе с кодом, а не выполняются руками через панель управления.
- Одинаково применяются на тестовом и боевом контуре, в одном и том же порядке.
- Перед применением делается свежая копия, и восстановление из неё проверено хотя бы однажды.
- Крупные изменения разбиваются на шаги: добавить поле, заполнить, переключить код, убрать старое.
- Схема описана в одном месте, и это описание обновляется вместе с изменениями.
Что в итоге
Хорошая структура данных не выглядит остроумной — она выглядит скучно и предсказуемо. Явные сущности, постоянные идентификаторы, зафиксированная история, ограничения на уровне базы. Именно эта скука позволяет через два года добавить филиал, новый канал продаж или обмен с учётной системой, не переписывая систему целиком.
Если вы планируете внутреннюю систему или чувствуете, что текущая база данных мешает развиваться, мы спроектируем структуру под ваши процессы и отчёты: разработаем информационную систему — CRM, ERP или учётный контур под конкретные задачи. Начинаем с разбора сущностей и связей, а не с выбора технологий.











