В каталоге атрибуты товаров различаются по типу. Как выбрать модель хранения, чтобы сохранить проверяемость данных?
Выбор зависит от того, насколько атрибуты известны заранее, важны ли для них строгие ограничения и как выполняется поиск. Для стабильного набора атрибутов предпочтительны обычные столбцы; для существенно разных типов товаров — отдельные таблицы или таблицы подтипов; для редко используемых и изменчивых свойств допустимо структурированное JSON-поле. Универсальная модель с произвольными парами «ключ–значение» обычно ухудшает проверяемость, поиск и сопровождение.
Реляционные базы данных изначально ориентированы на табличную структуру, где столбцы имеют определённый тип, а ограничения целостности проверяются самой базой данных. Это упрощает запросы, индексацию и контроль качества данных.
Позднее появились каталоги с большим числом разновидностей товаров, где заранее описать все свойства трудно. Полуструктурированные форматы, включая JSON, позволили хранить изменчивые атрибуты без постоянного изменения схемы. Однако гибкость была получена ценой более слабой типизации и переноса части проверок в приложение.
Например, у ноутбука есть объём оперативной памяти, у обуви — размер, у холодильника — класс энергопотребления. Если все свойства поместить в одну широкую таблицу, появится множество пустых столбцов, а добавление нового типа потребует изменений схемы.
Если хранить всё в произвольных парах «имя–значение», база может принять строку вместо числа, неизвестное имя свойства или недопустимую единицу измерения. Это приводит к ошибкам фильтрации, разным трактовкам одного атрибута и сложным отчётам.
Неверный выбор также влияет на API: клиенту становится трудно понять, какие поля обязательны для конкретного типа товара и какие значения допустимы. Поэтому модель хранения должна учитывать не только экономию места, но и контракт данных.
Сначала определяют характеристики каждого атрибута:
Обычные столбцы подходят для общих атрибутов: идентификатора, названия, производителя, цены. Их преимущества — типизация, ограничения, понятные индексы и простые запросы. Недостаток — изменение схемы при появлении новых стабильных свойств.
Таблицы подтипов подходят, когда типы товаров имеют разные обязательные поля и собственные правила. Общие данные хранятся в основной сущности, а специализированные — в связанных таблицах. Это обеспечивает строгую модель, но усложняет запросы и требует явного управления иерархией типов.
JSON-поле оправдано для редко используемых, нестабильных или второстепенных свойств. Его следует применять вместе с явным описанием схемы на уровне API или приложения, а критичные свойства при необходимости выносить в отдельные столбцы. Важно заранее проверить возможности конкретной СУБД для индексации и ограничений JSON.
EAV-модель, где каждая характеристика хранится отдельной строкой, даёт максимальную гибкость, но усложняет типизацию, уникальность, агрегирование и производительность. Её применяют только при действительно динамическом наборе характеристик и при готовности поддерживать отдельный слой метаданных и валидации.
Практичный компромисс — гибридная модель: общие и часто используемые атрибуты сделать типизированными столбцами, специфические свойства хранить в структурированном JSON, а набор допустимых характеристик описать каталогом метаданных. Для каждого свойства фиксируют тип, обязательность, единицу измерения, допустимый диапазон и правила отображения.
Главный критерий — не максимальная гибкость, а стоимость нарушения качества данных. Если атрибут участвует в юридически значимом расчёте или массовой аналитике, его лучше хранить в типизированной структуре с контролируемыми ограничениями.
Интернет-магазин продаёт электронику, одежду и бытовую технику. Команда рассмотрела три варианта: широкую таблицу с колонками под все возможные свойства, EAV-модель и гибридную схему.
Широкая таблица давала простые запросы, но содержала много пустых полей и плохо масштабировалась при появлении новых категорий. EAV позволяла добавлять характеристики без изменения схемы, однако фильтр по нескольким параметрам требовал сложных соединений, а контроль типов и диапазонов становился обязанностью приложения.
Выбрали гибридную схему. Общие поля и часто используемые фильтры сделали типизированными, характеристики конкретной категории — JSON-объектом с версией схемы, а описание допустимых свойств — отдельным справочником. Для нескольких наиболее популярных JSON-атрибутов добавили специализированные индексы после измерения реальных запросов.
Такое решение сохранило быстрые основные сценарии, позволило расширять каталог без постоянных миграций и оставило правила валидации явными. Компромисс — необходимость поддерживать схему характеристик, тестировать совместимость её версий и контролировать, какие свойства действительно должны быть вынесены из JSON.
Поддержка индексов решает только часть задачи — ускоряет некоторые операции поиска. Она не гарантирует, что каждый товар содержит обязательное свойство, что значение имеет нужный тип, что единицы измерения согласованы и что название атрибута не дублируется в разных вариантах.
JSON может быть хорошим физическим форматом, но не заменяет логическую схему. Для критичных свойств всё равно нужны явные правила валидации, версионирование и понятный контракт API.
EAV может быть оправдана, если характеристики управляются пользователями или администраторами, постоянно меняются, имеют собственные метаданные и должны участвовать в универсальных операциях над атрибутами. Например, для конструктора пользовательских полей важна возможность добавлять новые свойства без выпуска новой версии приложения.
Но EAV требует отдельного описания типов, правил обязательности, единиц измерения и индексации. Если набор свойств известен разработчикам и меняется редко, JSON или обычные таблицы обычно проще и надёжнее.
Экран отражает один способ представления данных, но не обязательно будущие требования к поиску, обмену, отчётности и истории изменений. Поле, которое сегодня показывается как произвольный текст, завтра может понадобиться для диапазонного фильтра или расчёта.
На этапе анализа нужно определить жизненный цикл атрибута, владельца его значений, правила изменения и потребителей. Модель должна поддерживать не только текущий интерфейс, но и согласованный контракт данных; при этом не следует заранее выносить в отдельные столбцы свойства, для которых нет реальной потребности в строгой обработке.