Стартапы, Проекты | Олег Брагинский, Марина Строева
Основатель «Школы траблшутеров» Олег Брагинский и ученица Марина Строева расскажут о разработке маркетплейса, углубятся в технологии и особенности микросервисной архитектуры, оптимизируют CRM-систему для удобства работы и анализа данных о продажах.
Разработка масштабируемого маркетплейса – разговор не только о витрине и заказах, но и об архитектуре каталога, динамической атрибутике, интерфейсе для продавцов поваров и услуг, поиске с сотнями категорий и тысячами подкатегорий, интеграция с базой данных и событийной шиной, а также операционная CRM, которая видит этапы жизненного цикла услуги и пользователя.
Стек проекта
Архитектура сервиса выглядит так: фронтенд на React + TypeScript с SSR/ISR (Next.js) для SEO и скорости первичной отрисовки; управление состоянием через Context, Zustand; работа с серверными данными через провайдера запросов.
Хранение и бизнес-логика – Node.js с ORM (Sequelize), PostgreSQL, кеш и очереди; поиск и фасеты – OpenSearch; медиа – объектное хранилище с CDN; real-time – WebSocket. React-форма в модальном окне, Request-провайдер, FileSelect-хук, BASE_FILE_URL для доступа к медиа.
Проект отделяет React-клиент от легаси-фронтенда, выделяет слой моделей Sequelize, контроллеров API и репозиториев, а также миграций, что создаёт фундамент для эволюции к микросервисам и событийно-ориентированной интеграции с CRM.
CRM в маркетплейсе
Встроенная система связывается с маркетплейсом событийно: регистрация продавца, покупателя, создание услуги, заказы, изменение цены, удаление. Модули включают сделки, воронки и источники коммуникации – встроенный мессенджер, аналитические дашборды.
Модальные окна управления
ServiceModal.tsx играет важную роль при управлении услугами. Решает три ключевых сценария: показ списка созданных услуг или товаров, удаление и открытие формы для создания или редактирования.
Компонент работает в связке с контекстом услуг и экшенами, что обеспечивает разделение логики и UI: список и действия живут вовне, а форма получает запись на вход и отдаёт результат наружу через onSave. Это делает компонент переиспользуемым и предсказуемым.
Внутри модалки – простой, но функциональный UX: пока форма открыта для записи, эта же запись скрывается из списка, чтобы избежать дублирования; при успешном сохранении или удалении пользователю летит toast-уведомление, локальный список обновляется без перезагрузки страницы.
Эта часть отлично ложится на real-time обновления: если внешний источник данных изменит услугу, контекст сможет подхватить обновление через WebSocket и отрисовать актуальное состояние.
Статические поля и динамические атрибуты
Редактирует объект ServiceType. Статические поля ожидаемы: категория, подкатегория, название, описание, цена, условия предоставления услуги. Важная фишка – динамические атрибуты: набор полей зависит от выбранной подкатегории. Это главный кирпич для масштабируемого каталога.
Вместо того чтобы жёстко кодировать фильтры и атрибуты для каждой категории, принимаем конфигурацию полей на вход из API, а UI универсально визуализирует и сохраняет значения в JSON-поле dynamic_properties.
Компонент DynamicFieldRenderer умеет отображать select, radio и checkbox. Для чекбоксов хранится объект ключ – boolean, для селекта – одно значение, для радио – одно значение из fields.
Медиа загружается через useFileSelect: обложка – одиночный файл, портфолио – до 9 изображений. После upload формируется путь BASE_FILE_URL + path, а для портфолио доступна сортировка через SortContainer и удаление отдельных элементов.
Среди минимально необходимых проверок: выбранные категория, подкатегория, длина названия, положительная цена, заполненность обязательных dynamic-полей и корректные типы значений. Используется схему валидации Zod и показываются ошибки под полями.
Фасетная фильтрация и поиск в каталоге
Чтобы поддержать сотни категорий и тысячи подкатегорий с динамическими фильтрами, нужен поисковый движок с фасетами. При запросе пользователь передаёт категорию, подкатегорию и набор фильтров, сервер строит bool-запрос, отдаёт товары, услуги и агрегаты по доступным полям.
На фронтенде универсальная панель фильтров читает схему, facets и рисует подходящие контролы – списки чекбоксов, селекторы, ползунки диапазонов, сетки размеров/цветов. Для удобства пользователя все фильтры синхронизируются с URL.
Слои интеграции
Стандартный паттерн: пустая строка – нестрогий список по умолчанию, введено что-то – серверный поиск с limit и parent_id. На стороне данных в PostgreSQL есть иерархия категорий и сущность service.
Чтобы динамические атрибуты вошли, в таблицу service добавляем колонку dynamic_properties JSONB с индексом GIN, а конфигурацию атрибутов выносим в отдельную таблицу, которая хранит subcategory_id, code, type, options (JSONB), is_required и sort_order.
Таким образом, фронтенд получает схему по подкатегории, рисует поля и обратно отправляет JSON вида { [code]: value }. Удобно индексировать, считать фасеты и строить фильтры на витрине. Плюс, CRM сможет отображать и валидировать поля на карточке услуги.
Связь с real-time достигается через WebSocket-подписки на канал сущности service: при обновлениях с сервера контекст диспатчит updateOne и UI синхронизируется. На уровне API экшен saveService публикует событие, а CRM, индексатор поиска и аналитика их потребляют.
Безопасность
Валидация двусторонняя: на клиенте – быстрый UX и подсказки, на сервере – гарантия целостности. Для dynamic_properties сервер должен проверять, что все is_required заполнены, значения соответствуют типам и опциям, а лишних полей нет (JSON Schema/Zod на сервере).
Для HTML-полей (description/terms) обязательна серверная санитизация. Для загрузки медиа – проверка сигнатур и размера, ограничение типов, защита путей, временные подписанные URL для приватных ресурсов.
Итог
Правильно спроектированный маркетплейс услуг и товаров – система, где динамические атрибуты не зашиты в код, а приезжают из БД; медиа подключены и удобно управляются; список обновляется реактивно; поле приготовлено к тому, чтобы стать частью фасетного поиска и CRM-аналитики.
Сервис естественным образом масштабируется на сотни категорий и тысячи подкатегорий, выдерживает изменения требований без релиза фронтенда, а CRM получает данные, которые нужны для поддержки, продаж и операционного контроля.