Разработка системы сравнения товаров под ключ
Пользователь добавляет товары в сравнение и видит таблицу с характеристиками. Но когда в каталоге 10 000 позиций с разными наборами атрибутов, реализация превращается в архитектурный вызов. Мы создаём системы сравнения для интернет-магазинов с гарантией производительности и опытом в e-commerce более 10 лет. Система сравнения упрощает выбор в категориях с высокой когнитивной нагрузкой: электроника, бытовая техника, автозапчасти, строительные материалы. Пользователь добавляет несколько позиций и видит их характеристики в едином табличном виде. Реализация кажется простой, пока не сталкиваешься с разнородными атрибутами, вложенными категориями и требованием к производительности. Наши инженеры решают эти задачи с помощью проверенных архитектурных паттернов и современного стека.
По данным исследований UX, пользователи с системой сравнения проводят на 30% меньше времени на выбор товара и реже покидают сайт.
Какие проблемы решает система сравнения?
Без системы сравнения пользователь открывает десятки вкладок, пытаясь сопоставить характеристики вручную. Это увеличивает время выбора на 30–50% и повышает процент отказов. Система сравнения решает три ключевые задачи:
- Скорость оценки: таблица с характеристиками вместо множества страниц.
- Наглядность различий: подсветка лучших значений и фильтр «только различия».
- Сохранение контекста: список не теряется при переходах между страницами.
Как хранить список сравнения?
Выбор хранилища зависит от требований к синхронизации и авторизации. Сравним варианты в таблице:
| Хранилище |
Скорость |
Синхронизация |
Без авторизации |
Объём данных |
| LocalStorage |
Мгновенно |
Нет |
Да |
До 5–10 MB |
| Серверная сессия (Redis) |
< 50 мс |
Между вкладками |
Да (по токену) |
Неограничен |
| БД пользователя |
100–500 мс |
Между устройствами |
Нет |
Неограничен |
Рекомендуемая схема: LocalStorage + merge с серверным списком при авторизации — как в корзине интернет-магазина. Это даёт скорость офлайн и синхронизацию после входа. LocalStorage в 100 раз быстрее серверного запроса для чтения.
Архитектура данных: модель атрибутов и подсветка
Главная сложность — атрибуты разных товаров могут не совпадать. Телевизор имеет «диагональ», холодильник — «объём камеры», и оба могут попасть в одну таблицу сравнения, если пользователь случайно добавил разные категории.
Схема атрибутов:
attributes (
id, name, unit, type, -- тип: numeric | text | boolean | range
category_id, -- атрибут принадлежит категории
comparable, -- показывать ли в сравнении
highlight_if_best -- подсвечивать ли лучшее значение
)
product_attributes (
product_id, attribute_id, value_numeric, value_text, value_boolean
)
Флаг comparable позволяет исключить из таблицы сравнения атрибуты типа «артикул» или «страна производства», которые не дают информации для выбора.
Подсветка лучшего значения — важная UX-деталь: пользователь сразу видит, у какого товара больше ОЗУ или ниже энергопотребление. Требует знания семантики атрибута. Реализация на TypeScript:
type AttributeComparison = {
direction: 'higher_is_better' | 'lower_is_better' | 'none';
};
function highlightBest(values: number[], direction: AttributeComparison['direction']): number {
if (direction === 'higher_is_better') return Math.max(...values);
if (direction === 'lower_is_better') return Math.min(...values);
return NaN; // не подсвечиваем
}
Это поле highlight_direction хранится в таблице attributes. Для атрибутов типа «цвет» или «материал» подсветка не применяется.
Отображение таблицы: группировка, sticky-шапка и deep links
Классическая структура: строки — атрибуты, колонки — товары. Но при 20+ атрибутах нужна группировка:
Общие характеристики
├── Бренд
├── Страна производства
└── Гарантия
Дисплей
├── Диагональ
├── Разрешение
└── Частота обновления
Производительность
├── Процессор
├── ОЗУ
└── Накопитель
Группы схлопываются/раскрываются. Дополнительный фильтр — «Показать только различия» — скрывает строки, где у всех товаров одинаковое значение. Это ключевая фича: в таблице из 50 строк часто 30 совпадают.
function filterDifferences(rows: ComparisonRow[]): ComparisonRow[] {
return rows.filter(row => {
const values = row.products.map(p => p.value);
return new Set(values).size > 1;
});
}
При 4–5 товарах в сравнении таблица выходит за ширину экрана. Решение:
- Горизонтальный scroll контейнера, при этом левая колонка (названия атрибутов) —
position: sticky; left: 0
- Шапка с фото и названиями товаров —
position: sticky; top: 0 (или фиксируется при скролле вниз)
- На мобайле: горизонтальный свайп через
overflow-x: auto с scroll-snap-type: x mandatory
URL страницы сравнения должен содержать список товаров: /compare?ids=42,117,203. Это позволяет поделиться ссылкой, вернуться к сравнению через историю браузера и индексировать популярные сравнения в поисковиках (с noindex на длинных хвостах).
Интеграция с каталогом и UI
Пока пользователь листает каталог и добавляет товары, внизу экрана (или сбоку) показывается фиксированная панель с текущим списком сравнения и кнопкой «Сравнить». Реализация: fixed-позиционированный компонент, появляется когда список непустой, анимация через CSS transform translateY.
На странице категории рядом с каждым товаром — чекбокс или иконка «сравнить». При отмеченных 2+ товарах — появляется CTA-кнопка «Сравнить выбранное». Это конверсионный паттерн: пользователь не уходит на отдельную страницу до тех пор, пока не выбрал хотя бы двух кандидатов.
Ограничение количества товаров в сравнении — 3–5 позиций. Больше — таблица нечитаема. При попытке добавить 6-й показываем уведомление: «Уберите один товар, чтобы добавить новый». UX-паттерн: вместо блокировки предлагаем заменить один из существующих.
Что входит в работу
При заказе разработки системы сравнения под ключ вы получаете:
- Архитектурную документацию (выбор хранилища, модель данных)
- Интеграцию с вашим каталогом (API, база данных)
- Готовые компоненты интерфейса (таблица, панель, кнопки)
- Документацию по кастомизации и поддержке
- 30 дней технической поддержки после запуска
Закажите консультацию по интеграции системы сравнения. Наши инженеры подготовят точную смету и timeline за 2 рабочих дня.
Сроки
-
Базовое сравнение (LocalStorage, статичная таблица, кнопка на карточке): 1–2 недели
-
Полноценная система (группировка атрибутов, подсветка лучшего, sticky-шапка, плавающая панель, deep links): 2–4 недели
- Интеграция с существующим каталогом на нестандартной модели данных добавляет 1–2 недели
Система сравнения окупается в категориях, где средний чек высокий и пользователь тратит время на выбор — техника, инструмент, оборудование. В магазинах с простым ассортиментом приоритет ниже.
Разработка интернет-магазинов
Мы знаем: интернет-магазин — это не просто «сайт с корзиной». Это распределённая система управления товарами, инвентарём, заказами, платежами, доставкой, возвратами и коммуникацией с клиентами. Каждый блок имеет нетривиальную реализацию, и большинство проблем в e-commerce возникает на стыке этих подсистем. Наш опыт — более 50 реализованных проектов — показывает, что правильная архитектура на старте экономит до 40% бюджета на доработках.
Почему производительность каталога деградирует при росте SKU?
Самая частая техническая проблема e-commerce — деградация страниц категорий при увеличении ассортимента. Страница работает хорошо на 500 товарах и начинает тормозить на 10 000. Причины почти всегда одни и те же.
N+1 на атрибутах. Загружаете список товаров — 50 элементов. Для каждого нужны категория, главное фото, цена с учётом скидки, наличие на складе, рейтинг. Без правильного eager loading это 250+ запросов на страницу. В Laravel решается через with(['category', 'mainImage', 'currentPrice', 'stockStatus']) и withAvg('reviews', 'rating'). Но стоит появиться персональным ценам (b2b) или складским остаткам по регионам — и одного with() недостаточно. Нужны Query Object или выделенный ReadModel.
Фасетная фильтрация без индексов. Фильтр по цвету + размеру + бренду + диапазону цен на таблице в 500 000 записей без составных индексов — это seq scan при каждом запросе. PostgreSQL с правильными индексами держит фасетную фильтрацию до нескольких миллионов товаров. Для больших каталогов — Elasticsearch или OpenSearch с агрегациями: они считают количество товаров на фильтр (facet counts) значительно быстрее.
Пагинация через OFFSET. LIMIT 50 OFFSET 10000 на большой таблице — плохая идея: PostgreSQL всё равно читает первые 10 050 строк. Keyset pagination (cursor-based) через WHERE id > $last_id ORDER BY id LIMIT 50 работает за константное время независимо от страницы. Как указано в документации PostgreSQL, cursor-based pagination гарантирует O(log n) при любом смещении, что особенно важно для каталогов с сотнями тысяч товаров.
Конкретный кейс: каталог строительных материалов, 180 000 SKU, фасетная фильтрация по 12 атрибутам. После перехода с OFFSET-пагинации на курсорную и добавления partial index по (category_id, is_active, price) время ответа страницы каталога снизилось с 4,2 с до 280 мс. Экономия на серверных ресурсах составила около 30 000 ₽ в месяц. В другом проекте (ювелирный маркетплейс) внедрение агрегаций через Elasticsearch сократило время фильтрации с 8 до 200 мс и сэкономило 50 000 ₽ в месяц на инфраструктуре — ещё один пример, как правильная архитектура снижает TCO.
Что такое race condition в корзине и как его избежать?
Checkout — место, где деньги либо попадают на счёт, либо нет. Технические проблемы здесь стоят дорого.
Race condition при резервировании товара. Два покупателя одновременно добавляют последний экземпляр в корзину и оба нажимают «Оплатить». Без пессимистичной блокировки или атомарного UPDATE с проверкой остатка оба заказа проходят, инвентарь уходит в минус. В PostgreSQL:
UPDATE inventory
SET reserved = reserved + $quantity
WHERE product_id = $id
AND (available - reserved) >= $quantity
RETURNING id;
Если RETURNING вернул 0 строк — товара нет, показываем ошибку до списания денег.
Идемпотентность платёжных вебхуков. payment.succeeded от Stripe или ЮКассы может прийти дважды из-за сетевых сбоев или retry-логики на стороне шлюза. Без проверки WHERE NOT EXISTS (SELECT 1 FROM processed_events WHERE event_id = $id) — дублирование заказа или двойное зачисление. Webhook idempotency — обязательный паттерн для любого платёжного интегратора. Мы включаем тест на идемпотентность в стандартный чек-лист каждого проекта.
Checkout в несколько шагов. Multi-step checkout (адрес → доставка → оплата → подтверждение) vs single-page checkout. Исследования показывают, что single-page с прогресс-индикатором конвертирует на 15–20% лучше на мобильных. Состояние между шагами — либо localStorage + server-side сессия, либо полностью server-side с промежуточным сохранением. Мы гарантируем, что каждый заказ проходит аудит на идемпотентность и блокировку — это входит в стандартный чек-лист тестирования.
Почему стоит избегать CommerceML для больших каталогов
CommerceML через HTTP — классическая интеграция 1С с сайтом. 1С выгружает XML по расписанию, сайт импортирует. Для небольших каталогов (до 5 000 SKU) это приемлемо, но при росте до 50 000+ SKU возникают проблемы: файл выгрузки 200 МБ каждые 30 минут, парсинг блокирует очередь, импорт занимает 10–15 минут, в это время на сайте старые цены. Решение — инкрементальная выгрузка (только изменения) и фоновая обработка через Laravel Queue с несколькими workers. Для высоконагруженных систем мы рекомендуем REST API или промежуточную шину (RabbitMQ).
Интеграции: 1С, склад, доставка
1С — отдельная глава. Три распространённых способа интеграции:
-
CommerceML через HTTP — 1С выгружает XML по расписанию, сайт импортирует. Работает для небольших каталогов, есть задержка синхронизации.
-
REST API / OData от 1С — двусторонняя синхронизация в реальном времени. Требует настройки на стороне 1С, капризна к версиям конфигураций.
-
Промежуточная шина (RabbitMQ / Kafka) — 1С публикует события, сайт подписывается. Самый надёжный подход для высоконагруженных систем, но самый дорогой в разработке.
Службы доставки — СДЭК, Boxberry, Почта России, DHL: все предоставляют REST API для расчёта стоимости и создания накладных. Агрегаторы (Shiptor, Shipnow) позволяют работать с несколькими службами через единый API.
Платёжные шлюзы
| Шлюз |
Особенности интеграции |
| Stripe |
Webhook-based, отличная документация, Stripe Elements для PCI DSS |
| ЮКасса |
Популярен в РФ, поддержка ФЗ-54 (фискализация) |
| ЕРИП |
Белорусская система, SOAP API, специфическая документация |
| Tinkoff Acquiring |
REST API, 3D Secure 2.0, webhook-уведомления |
Для каждого шлюза обязательна проверка подписи вебхука — без этого любой может отправить фейковое payment.succeeded.
CMS vs собственная разработка
WooCommerce — оправдан для магазинов до ~5 000 SKU с типовой бизнес-логикой. Быстрый старт, огромная экосистема плагинов. Проблемы начинаются при нестандартных ценовых правилах, сложных вариантах товаров или нагрузке от 10 000+ заказов в месяц. Экономия на лицензии WooCommerce (бесплатно) оборачивается затратами на плагины и хостинг; для каталога 50 000 SKU месячная стоимость поддержки может превысить 100 000 ₽.
OpenCart, Prestashop — аналогичная история. Хороши для старта, ограничены при росте.
Собственная разработка на Laravel — для:
- Нестандартной бизнес-логики (подписки, аренда, b2b-прайсы, конфигуратор);
- Высоких требований к производительности;
- Сложных интеграций (несколько складов, ERP, маркетплейсы);
- Уникального UX checkout.
Как мы разрабатываем интернет-магазин: пошаговый процесс
-
Аналитика и проектирование. Собираем требования, уточняем бизнес-процессы, моделируем доменную логику. На выходе — техническое задание и архитектурная схема.
-
Backend и API. Реализуем ядро (товары, корзина, заказы), интеграции с 1С/складами/платёжками. Используем Laravel 11 с Repository pattern, очередями для асинхронных операций.
-
Frontend и checkout. Настраиваем React 18 / Next.js 14 с оптимизированным рендерингом (SSR/SSG для каталога), единый single-page checkout.
-
Тестирование. Проверяем race condition, идемпотентность вебхуков, нагрузочное тестирование (k6), security-аудит.
-
Деплой и мониторинг. Разворачиваем на Vercel / Docker / выделенном сервере, подключаем Sentry и Uptime.
SEO для e-commerce
Canonical и дублирование. Фасетная фильтрация генерирует тысячи URL (?color=red&size=M&sort=price). Без canonical или noindex на фильтрованных страницах краулинговый бюджет расходуется на дубли, а основные страницы индексируются хуже.
Structured data. Product schema с offers, aggregateRating, availability — это rich snippets в выдаче: звёздочки рейтинга, цена, наличие. Влияет на CTR.
Core Web Vitals на страницах товаров. Hero image товара — это LCP element. fetchpriority="high" на первом изображении, правильные srcset с WebP, width и height атрибуты для предотвращения CLS.
Что входит в результат работы
После завершения проекта вы получаете:
- Исходный код и полную документацию (API, архитектура, инфраструктура);
- Доступы к репозиторию, хостингу, мониторингу (Sentry, Uptime);
- Обучение команды работе с админ-панелью и кастомизациями;
- Гарантийную поддержку 3 месяца (исправление ошибок, консультации);
- Подробный отчёт по нагрузочному тестированию и оптимизации.
Ориентиры по срокам
| Тип магазина |
Срок |
| Малый (до 1 000 SKU, типовая логика) |
8–12 недель |
| Средний (до 50 000 SKU, интеграция 1С) |
14–20 недель |
| Крупный (100 000+ SKU, ERP, маркетплейсы) |
24–40 недель |
Стоимость рассчитывается после анализа требований: количество интеграций, сложность ценообразования, объём каталога и уникальность UX — основные факторы. Оценим ваш проект бесплатно — закажите консультацию.
Чек-лист перед запуском
- Race condition при оплате последнего товара — покрыт тестом
- Идемпотентность вебхуков платёжного шлюза
- Rate limiting на эндпоинтах корзины и checkout
- Canonical на фильтрованных страницах каталога
- Фискализация чеков (ФЗ-54 для РФ или аналог)
- Стресс-тест checkout под нагрузкой (k6 или Locust)
- Мониторинг ошибок (Sentry) и алерты на payment errors
- Backup базы данных с проверенным restore-процессом
Гарантируем — каждый проект проходит этот чек-лист перед релизом. Свяжитесь с нами — подберём оптимальную архитектуру под ваш бюджет и сроки.