База данных интернет-магазина: что хранить и где начинаются проблемы

Покупатель открывает интернет-магазин и видит простую картину: категории, товары, цены и кнопку «Купить». Внутри всё устроено сложнее.

За одной карточкой товара могут стоять несколько вариантов, десятки характеристик, разные цены, остатки на нескольких складах и идентификаторы из внешних систем. После оформления заказа появляются клиент, состав покупки, оплата, доставка и история изменений.

Пока магазин небольшой, многие ошибки структуры данных почти незаметны. Можно записать цвет в название, хранить один остаток на весь товар, а цену менять вручную.

11 мин чтения2 305 словE-commerce и AI
Александр Колотов
Александр Колотов
Автор CompanionAI
База данных интернет-магазина: что хранить и где начинаются проблемы

Проблемы начинаются с ростом: появляется второй склад, 1С, маркетплейс или несколько тысяч SKU. И вдруг один товар существует три раза, остатки не сходятся, а фильтр считает «Белый», «белый» и WHITE разными цветами.

Поэтому при проектировании интернет-магазина важно решить не только, какие страницы увидит покупатель. Нужно заранее понять, какие данные существуют в системе, как они связаны и кто имеет право их менять.

Из каких данных на самом деле состоит интернет-магазин

Самая простая модель выглядит примерно так:

категория → товар → цена → заказ.

Для учебного проекта хватит. Для реального магазина — ненадолго.

Даже обычный e-commerce работает с системой связанных сущностей:

Категория → Товар → Вариант → SKU → Цена / Остаток

и отдельно:

Клиент → Заказ → Позиции заказа → Оплата / Доставка

Добавьте характеристики, фотографии, склады, скидки, статусы, способы доставки и идентификаторы из внешних систем — и становится понятно, почему база данных интернет-магазина не сводится к таблице products.

Главная сложность здесь даже не в количестве данных, а в связях.

Один товар может иметь несколько SKU. Один SKU может находиться на нескольких складах. Один заказ содержит несколько позиций. Один клиент может сделать много заказов.

Например, остаток нельзя просто «прикрепить к шкафу». Он относится к конкретному варианту шкафа на конкретном складе.

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

Товар, вариант и SKU — разные сущности

Возьмём интернет-магазин мебели.

В каталоге есть шкаф ARCO. Покупатель воспринимает его как один товар, но шкаф можно заказать шириной 120 или 160 см, в медовом или тёмном дубе.

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

СущностьПримерЧто относится к ней
ТоварШкаф ARCOназвание, описание, коллекция, общие фотографии
Вариант160 см / тёмный дубвыбранные параметры и комплектация
SKUARCO-160-DARKидентификатор позиции, штрихкод, цена, остаток

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

Если у товара нет вариантов, эти уровни могут почти совпадать. Но когда появляются размеры, цвета и комплектации, попытка хранить всё внутри одной записи товара быстро становится неудобной.

Сначала появляется второй цвет. Потом три размера. Затем отдельные остатки. Одна карточка товара незаметно превращается в небольшой филиал бухгалтерии.

Характеристики должны храниться как данные

Допустим, в описании шкафа написано:

Шкаф из медового дуба шириной 120 см и высотой 210 см.

Человек всё понял.

Для системы эта строка почти бесполезна.

Если характеристики нужны магазину для работы, лучше хранить их отдельно:

  • материал: дуб;
  • цвет: медовый;
  • ширина: 1200 мм;
  • высота: 2100 мм.

Тогда эти значения можно использовать повторно.

От структуры характеристик зависят фильтры

Если покупатель выбирает:

Материал → дуб

Ширина → до 120 см

магазину нужны отдельные значения материала и ширины.

Фильтр не должен каждый раз пытаться понять текст описания и догадаться, что «1,2 м», «120 см» и «1200 мм» означают одно и то же.

Поэтому архитектура данных напрямую продолжает архитектуру каталога. Сначала мы определяем, как покупатель будет искать и выбирать товар, затем — какие данные нужны системе для этого сценария.

Красивый фильтр бесполезен, если фильтровать ему нечего.

Эти же данные нужны другим системам

Структурированные характеристики можно использовать не только на сайте, но и передавать:

  • в 1С или другую учётную систему;
  • на маркетплейсы;
  • в рекламные фиды;
  • в PIM — систему управления товарной информацией;
  • во внутренний поиск;
  • в системы рекомендаций.

Если материал существует только внутри описания, его придётся каждый раз заново извлекать из текста.

Значения нужно приводить к одному формату

Даже хорошо спроектированное поле не поможет, если данные заполняются как попало.

Например:

Белый

белый

WHITE

бел.

Для человека это один цвет. Для системы без дополнительных правил — четыре разных значения.

То же происходит с размерами, брендами, единицами измерения и названиями производителей.

Если значение нужно фильтровать, сравнивать, передавать или анализировать, оно должно существовать как структурированное значение, а не прятаться в тексте.

Почему одной цены и одного остатка быстро становится мало

На простой карточке есть цена:

89 000 ₽

Кажется, что достаточно одного поля price.

Иногда действительно достаточно.

Но затем появляются старая цена, акция, B2B, дилеры, разные каналы продаж или регионы.

Один и тот же шкаф может одновременно стоить:

89 000 ₽ в розницу;

76 000 ₽ для дилера;

84 900 ₽ по акции до конца недели.

Какое из значений считать «ценой товара»?

Ответ зависит от бизнес-логики компании. Поэтому при создании базы данных интернет-магазина нужно понимать, какие сценарии ценообразования реально используются.

При этом строить собственную биржу ради ста товаров тоже не требуется. Модель должна соответствовать задаче.

Остаток относится к SKU и складу

С остатками похожая история.

Значение:

ARCO — 12 шт.

перестаёт что-либо объяснять, как только появляются варианты.

Реальная картина может выглядеть так:

SKUЕкатеринбургМосква
ARCO-120-OAK36
ARCO-160-OAK02
ARCO-160-DARK10

Теперь система может корректно показать, что шкаф ARCO шириной 160 см в медовом дубе закончился в Екатеринбурге, но доступен в Москве.

То есть остаток связан как минимум с двумя объектами:

SKU + склад.

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

Заказ должен хранить прошлое, а каталог — настоящее

Допустим, покупатель оформил шкаф ARCO за 89 000 ₽.

Через неделю магазин:

  • поднял цену до 94 000 ₽;
  • изменил название;
  • заменил фотографию;
  • скорректировал характеристики;
  • снял эту комплектацию с продажи.

Старый заказ при этом меняться не должен.

Покупатель купил конкретную позицию по конкретной цене. История покупки должна сохранять состояние на момент оформления.

Поэтому в позиции заказа обычно фиксируют не только ссылку на товар, но и необходимые данные:

  • название;
  • SKU;
  • выбранный вариант;
  • количество;
  • цену;
  • скидку;
  • итоговую стоимость;
  • важные для заказа характеристики.

Если товар позже удалят из каталога, заказ всё равно останется читаемым.

Представим обратную модель: в заказе хранится только product_id = 128.

Прошло два года. Товар переименовали, стоимость изменилась, старый SKU сняли с продажи.

Если старый заказ каждый раз подставляет текущие данные карточки, получится, что два года назад клиент каким-то образом купил сегодняшний товар по сегодняшней цене.

Карточка товара показывает настоящее. Заказ должен хранить прошлое.

ID, SKU, артикул и внешний ID: где начинается путаница

Пока интернет-магазин работает отдельно, вопрос идентификаторов кажется второстепенным.

Потом подключается 1С.

Затем маркетплейс.

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

ИдентификаторДля чего используется
Внутренний IDидентификация записи внутри конкретной системы
SKUидентификация конкретной продаваемой позиции
Артикулвнутреннее обозначение товара или варианта в компании
Внешний IDсопоставление объекта с записью в другой системе

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

Например, один шкаф может иметь:

  • ID магазина 128;
  • SKU ARCO-160-DARK;
  • артикул SHK-0481;
  • отдельный GUID в 1С;
  • ID карточки на маркетплейсе.

Все они относятся к одному объекту, но используются разными системами.

Особенно важен внешний ID при интеграциях. Если его нет, систему приходится учить сопоставлять товары по артикулам, названиям или другим косвенным признакам.

А название «Шкаф ARCO 160 тёмный дуб» кто-нибудь однажды обязательно изменит.

После этого начинается цифровая археология.

У каждого вида данных должен быть главный источник

Когда несколько систем работают с одними данными, нужно определить главный источник данных — source of truth.

Одинаковая информация физически может присутствовать в интернет-магазине, 1С, CRM и других сервисах. Но желательно заранее решить, где её создают и где разрешено изменять.

Например:

ДанныеВозможный главный источник
Описание товараCMS или PIM
ХарактеристикиPIM или интернет-магазин
Цена1С / ERP
ОстатокERP / складская система
Заказинтернет-магазин
Работа с клиентомCRM

ERP здесь — учётная система компании; в российском бизнесе эту роль часто выполняет 1С.

Таблица выше — пример, а не универсальная схема. В конкретном проекте распределение может быть другим.

Практическое правило проще:

для каждого важного типа данных ответьте на три вопроса: кто его создаёт, где его разрешено менять и куда оно передаётся.

Что происходит, когда главных систем две

Менеджер меняет цену ARCO на сайте на 92 000 ₽.

В 1С остаётся 89 000 ₽.

Запускается синхронизация.

Какое значение правильное?

Вернуть на сайт 89 000 ₽? Передать 92 000 ₽ в 1С? Сравнить время изменения? Создать конфликт?

Если это правило не определили заранее, его придётся придумать уже во время разработки.

А потом бухгалтер будет считать правильной цену из 1С, маркетолог — цену на сайте, а руководитель пытаться понять, откуда взялась третья.

Проблема не в обмене.

У данных просто нет однозначного хозяина.

Интеграция не исправляет хаос

Обмен между системами умеет передавать информацию.

Но он не решает сам:

  • какое значение считать правильным;
  • кому разрешено его менять;
  • как сопоставлять сущности;
  • что делать при конфликте.

Если данные изначально организованы плохо, интеграция просто начинает переносить этот хаос быстрее.

Поэтому до интеграции интернет-магазина с 1С стоит определить модель товаров, SKU, цен, остатков и идентификаторов. А уже затем проектировать сам обмен.

Где начинаются проблемы с базой данных

Ошибочная структура редко сообщает о себе фразой «неправильно спроектирована модель данных».

Обычно бизнес видит симптомы.

Один товар существует несколько раз

Причина: нет стабильного идентификатора для сопоставления.

Результат: дубли товаров, разные остатки и ошибки при обновлении каталога.

Характеристики записаны как попало

Причина: нет единого формата и справочников значений.

Результат: фильтры и интеграции считают одинаковые свойства разными.

Цена меняется в нескольких местах

Причина: не определён главный источник данных.

Результат: одна система регулярно перезаписывает другую.

Остаток хранится только у товара

Причина: модель не учитывает варианты и склады.

Результат: после появления нескольких SKU корректный учёт наличия становится невозможен.

Важные данные спрятаны в описании

Причина: материал, размер, производитель или назначение существуют только как текст.

Результат: их нельзя надёжно использовать в фильтрах, фидах и интеграциях.

Старые заказы зависят от текущего каталога

Причина: заказ хранит только ссылку на актуальную карточку.

Результат: история покупки меняется вслед за товаром.

И есть ещё отдельная категория проблем — качество самих данных.

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

База данных не делает плохие данные хорошими. Она лишь аккуратно их хранит.

Когда простой структуры становится недостаточно

Не каждому магазину нужна индивидуальная база данных или сложная архитектура.

Если компания продаёт 80 простых товаров без вариантов с одного склада, стандартной модели CMS или e-commerce-платформы может быть вполне достаточно.

Строить распределённую систему ради каталога из сотни позиций — примерно как покупать грузовой терминал для доставки одного дивана.

Сложность становится оправданной, когда появляются:

  • тысячи SKU;
  • варианты товаров;
  • несколько складов;
  • разные типы цен;
  • B2B-клиенты;
  • 1С или другая ERP;
  • CRM;
  • маркетплейсы;
  • несколько регионов;
  • PIM;
  • несколько каналов продаж.

Тогда приходится проектировать уже не только хранение товара, но и движение данных между системами.

Правильная архитектура не должна быть максимально сложной. Она должна выдерживать реальные задачи бизнеса и его ближайший рост.

Что определить до разработки базы интернет-магазина

Большую часть архитектурных проблем дешевле решить вопросами до начала разработки, чем миграцией рабочей базы через два года.

Каталог

Определите:

  • какие типы товаров есть;
  • существуют ли варианты;
  • что считается товаром, а что SKU;
  • какие характеристики используются;
  • какие характеристики нужны для фильтров;
  • есть ли комплекты и связанные товары;
  • как устроены категории.

Здесь база данных напрямую связана с архитектурой каталога. В отдельном материале «Как спроектировать каталог интернет-магазина: категории, фильтры и карточки» мы рассматриваем ту же систему со стороны покупателя: как человек ищет и выбирает товар.

Цены и остатки

Нужно понять:

  • сколько типов цен используется;
  • существуют ли B2B или персональные цены;
  • где разрешено менять цену;
  • сколько складов;
  • где ведётся складской учёт;
  • нужны ли резервы;
  • какой остаток должен видеть покупатель.

Интеграции

Перечислите системы, с которыми будет работать магазин:

  • 1С или ERP;
  • CRM;
  • PIM;
  • маркетплейсы;
  • службы доставки;
  • рекламные площадки.

Для каждого типа данных определите простую цепочку:

кто создаёт → кто изменяет → кто получает.

Следующий отдельный вопрос — уже сама интеграция интернет-магазина с 1С: товары, остатки, цены и заказы. Там важен не только состав данных, но и направление обмена, правила синхронизации и обработка конфликтов.

Заказы и клиенты

Заранее определите:

  • что фиксируется в момент заказа;
  • какие данные потом могут измениться;
  • что необходимо сохранить для истории;
  • какие сведения о клиенте действительно нужны магазину;
  • кому нужен доступ к этим данным.

Хранить персональные данные «на всякий случай» — плохая практика. У каждого поля должна быть понятная задача.

Отдельно нужно предусмотреть разграничение доступа, резервное копирование и восстановление.

Хорошая резервная копия — не та, которая где-то существует. Хорошая — та, из которой однажды проверили восстановление.

Рост бизнеса

Не нужно реализовывать будущие функции заранее, но стоит учитывать уже известные планы:

  • выход на маркетплейсы;
  • B2B;
  • новые склады;
  • рост ассортимента;
  • несколько регионов;
  • автоматическое обновление каталога.

Структура данных не должна мешать сценарию, который компания собирается запускать через полгода.

База данных хороша тогда, когда о ней не приходится вспоминать

В нормально спроектированном интернет-магазине данные просто работают.

  1. Менеджер обновляет карточку.
  2. Склад меняет остаток.
  3. Покупатель оформляет заказ.
  4. Учётная система получает нужную информацию.
  5. CRM видит клиента.

Никто не собирает срочное совещание, чтобы выяснить, почему один шкаф одновременно стоит 89 000, 92 000 и 94 000 рублей.

Проблемы базы данных становятся заметны не в момент создания первой таблицы. Они проявляются позже — когда магазин растёт, ассортимент усложняется и к системе подключаются другие сервисы.

Поэтому проектировать нужно не просто набор полей.

Нужно определить правила жизни данных: что существует в системе, как объекты связаны, где данные создаются, кто их меняет и что происходит при обмене с другими системами.

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

Если ответов нет, код проблему не решит. Он только зафиксирует неопределённость в более дорогой форме.