Создание кастомного модуля для Medusa.js: примеры и архитектура

Доработки магазина на Medusa.js часто ломаются при каждом обновлении платформы, и команда тратит недели на восстановление логики. Мы проектируем и внедряем кастомные модули под ключ — изолированно от ядра, чтобы обновления проходили без простоев и потери функциональности. Логика остаётся рабочей, а поддержка занимает в разы меньше времени.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Создание кастомного модуля для Medusa.js: примеры и архитектура
Средний
~3-5 дней
Часто задаваемые вопросы

Почему кастомный модуль для Medusa.js ломается при обновлении

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

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

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

Третья и самая дорогая — потеря связей сущностей. Модуль ссылается на сущности ядра по идентификаторам, а при обновлении меняется структура или каскад удаления: связи рвутся, появляются висячие записи, отчёты и корзины показывают неполные данные. Обнаруживается это не сразу, а через недели, когда расхождение уже накопилось. Защита — явные внешние ключи с продуманной политикой удаления и проверки целостности в миграциях.

Мы закладываем эти три сценария на этапе проектирования модуля, а не после первого апдейта: изолированные миграции, расширение через штатные точки, контроль ссылочной целостности. Дальше — как это выглядит в архитектуре и на реальных примерах.

Что даёт правильно изолированный модуль

Границы модуля и скорость сборки

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

Время восстановления после сбоя

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

Доля ручных операций

Конфигурация, миграции и регистрация модуля описываются декларативно, поэтому подключение новой предметной области не требует правок в ядре и ручной синхронизации окружений. Обновление модуля — это замена версии и прогон миграций, а не набор ручных шагов по переносу данных. В результате доля операций, которые инженер делает руками при выкатке, падает, а вместе с ней — вероятность ошибки по невнимательности.

Что это даёт на длинной дистанции

Модуль можно развивать и тестировать отдельно: свои проверки, свои фикстуры, предсказуемая зона ответственности. Новый инженер входит в задачу быстрее, потому что видит границы, а не разбирает связанный клубок. Именно эта предсказуемость и есть основной эффект изоляции — она окупается на каждой следующей доработке.

Способы расширения: сравнение подходов

В Medusa.js расширение функциональности почти всегда можно сделать тремя путями, и выбор между ними определяет, как система будет вести себя при обновлениях и росте нагрузки. Встроенное расширение живёт внутри ядра и переиспользует его сервисы напрямую, отдельный модуль выносит логику в изолированный пакет со своим жизненным циклом, а внешний сервис выносит её за пределы приложения целиком. Ниже — сравнение по критериям, которые реально влияют на поддержку.

Вариант Когда подходит Ограничения подхода
Встроенное расширение Логика тесно связана с заказами, корзиной или каталогом и должна читать их состояние в той же транзакции Обновление ядра требует ручного переноса правок; растёт связность, тесты замедляются
Отдельный модуль Нужна переиспользуемая доменная логика: интеграции, расчёты, собственные сущности Требует явного описания зависимостей и миграций; межмодульные вызовы надо проектировать заранее
Внешний сервис Нагрузка пиковая, логика на другом стеке или уже есть готовый сервис Появляется сетевой вызов: нужны идемпотентность, повторные попытки и обработка частичных отказов

Разница проявляется на горизонте: встроенное расширение быстрее собрать, но каждое обновление ядра превращается в ревизию правок. Отдельный модуль дороже на старте — зато обновляется независимо и покрывается тестами изолированно. Внешний сервис даёт свободу по стеку, но переносит часть ответственности за согласованность данных на инфраструктуру.

На практике выбор редко бывает «или-или»: ядро заказа остаётся встроенным, расчёты и интеграции уходят в модуль, а тяжёлые операции — во внешний сервис. Опишите задачу — вернёмся с оценкой и планом, какой вариант закроет её без лишней связности.

Этапы проектирования и внедрения модуля

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

  1. Аудит кодовой базы. Смотрим структуру проекта, версию ядра, набор установленных модулей и то, как в них организованы сервисы и подписки на события. Фиксируем точки расширения, которые уже используются, и те, что остались нетронутыми.
  2. Проектирование схемы данных. Определяем сущности модуля, их связи с базовыми (товар, заказ, покупатель), правила миграций и стратегию отката. Отдельно проговариваем, какие поля обязательны, а какие заполняются по событию.
  3. Настройка модуля. Собираем конфигурацию: регистрация в основном приложении, параметры подключения, права доступа, переменные окружения для разных сред. Модуль должен подниматься и в разработке, и в проде без ручных правок.
  4. Реализация и проверка интеграции. Пишем логику, покрываем ключевые сценарии автотестами и проверяем стыковку с существующими сервисами — оформление заказа, расчёт доставки, обмен с внешними системами.
  5. Нагрузочная проверка. Прогоняем сценарии под нагрузкой, близкой к боевой, и смотрим, не деградируют ли запросы к каталогу и корзине после подключения модуля.
  6. Передача и документация. Отдаём описание архитектуры, схему данных, инструкцию по развёртыванию и чек-лист типовых сбоев. Команда заказчика должна уметь обновлять модуль без нас.

Каждый этап заканчивается артефактом, который можно проверить: карта точек расширения, схема данных, конфигурация, отчёт о тестах. Так на любом шаге видно, что уже сделано и что осталось, — и риск скрытых доработок в конце проекта уходит.

Артефакты, которые остаются у команды заказчика

Работа с кастомным модулем для Medusa не заканчивается деплоем — после сдачи у команды заказчика должен остаться комплект, по которому модуль можно развивать, обновлять и передавать новым инженерам без нас. Ниже — что именно фиксируется в передаваемом пакете.

  • Конфигурация модуля — рабочие файлы настроек с описанием каждого параметра: какие значения обязательны, какие зависят от окружения, что меняется при подключении нового провайдера оплаты или доставки.
  • Схема данных — описание сущностей, связей и миграций: какие таблицы создаёт модуль, как они соотносятся с ядром, что произойдёт при откате миграции.
  • Политики доступа — матрица ролей и прав: кто из администраторов видит и редактирует данные модуля, какие операции доступны через API, где стоят ограничения на уровне запросов.
  • Документация — инструкция по развёртыванию с нуля, описание точек расширения и примеры вызовов, чтобы новую логику можно было добавить без чтения всего исходного кода.
  • Регламент обновления — порядок действий при выходе новой версии ядра: какие проверки прогоняются, как тестируется совместимость модуля, в какой момент обновление откатывается.
  • Набор автотестов — покрытие ключевых сценариев модуля: оформление заказа, расчёт стоимости, синхронизация с внешними сервисами. Тесты показывают, что обновление не сломало поведение.
  • Схема интеграций — карта внешних сервисов, с которыми связан модуль, с указанием, какие данные куда передаются и что происходит при недоступности сервиса.

Каждый пункт — не формальность ради отчёта, а ответ на конкретный риск: смена подрядчика, обновление ядра, подключение нового канала продаж. Если команда заказчика открывает документацию и по ней воспроизводит окружение с нуля — пакет собран правильно.

Кейс: модуль лояльности для интернет-магазина

Что было до внедрения

Интернет-магазин на Medusa.js с накоплением баллов лояльности прямо в основном приложении: логика начисления, сгорания и списания жила в контроллерах заказов и в фоновых заданиях. Любое изменение правил — правка ядра, полный прогон тестов и пересборка всего приложения. Сборка занимала около 12 минут, а релиз правил лояльности приходилось согласовывать с командой, отвечавшей за оформление заказа, — очередь на выкатку растягивалась на дни.

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

Что изменили

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

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

Что стало

Время сборки приложения сократилось с 12 до 4 минут — правки лояльности больше не тянут за собой пересборку всего проекта. Ручные корректировки баллов упали с 8–10 на сотню заказов до единичных случаев в месяц. Класс отказов «задвоенное начисление при повторной обработке события» ушёл полностью: операции стали идемпотентными, а расхождения в балансах теперь ловятся на этапе тестов, а не в поддержке.

Модуль изолирован, поэтому его можно развивать отдельным потоком: подключать новые уровни, акции и партнёрские начисления, не трогая оформление заказа.

Чем отличается кастомный модуль от плагина?

Плагин в Medusa — это надстройка над уже существующим контуром: он подписывается на события, добавляет маршруты, расширяет сервисы, но живёт внутри ядра и наследует его жизненный цикл. Кастомный модуль — самостоятельная единица с собственными сущностями, миграциями, сервисами и точками подключения. Разница проявляется не в объёме кода, а в уровне изоляции: плагин делит пространство с ядром, модуль держит свою границу и общается с остальным приложением через явные интерфейсы.

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

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

Есть и промежуточный путь: начать с плагина и вынести код в модуль, когда он обрастает состоянием и связями. Мы оцениваем не «модно ли делать модуль», а где пролегает граница домена — от этого зависит, во сколько обойдётся следующее обновление платформы.

Частые технические сомнения при выборе подрядчика

Кастомный модуль в Medusa — это изолированный пакет, который подключается к ядру через механизм модулей и не трогает его исходники. Поэтому главный вопрос не «сработает ли», а «как это переживёт обновления и смену подрядчика». Ниже — то, что чаще всего проверяют на нашей стороне до старта.

Как модуль поведёт себя при обновлении ядра?

Мы не патчим ядро и не форкаем его — вся логика живёт в отдельном модуле с явно объявленными зависимостями. При переходе на актуальную ветку обновляется ядро, а модуль проходит проверку совместимости отдельно: контракты, точки расширения, миграции. Если версия ядра меняет поведение, это видно на тестах, а не в продакшене.

Что с миграцией уже накопленных данных?

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

Как проверяется, что модуль не ломает магазин?

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

Что остаётся у нас после сдачи?

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

Эти четыре пункта закрывают основную часть рисков. Опишите задачу — вернёмся с оценкой и планом.

Обсудим вашу задачу и вернёмся с планом

Чтобы оценка была предметной, а не «плюс-минус квартал», предварительный разбор идёт по вашей текущей кодовой базе и данным магазина. Дальше фиксируем план работ и сроки по этапам — так видно, что делается первым и где точки проверки.

  • Короткий разговор по задаче: какие процессы модуль закрывает, что уже работает и что мешает — со слов вашей команды и владельца продукта.
  • Доступ к репозиторию и конфигурации Medusa: смотрим версию ядра, структуру модулей, кастомизации ядра и подписки на события.
  • Разбор схемы данных: сущности, связи, миграции, объёмы — от этого зависит, будет модуль отдельным пакетом или расширением существующего.
  • Проверка интеграций: платёжные провайдеры, склад, доставка, учётные системы, очереди — что модуль должен вызывать и что вызывать его.
  • Согласование границ: что входит в модуль, что остаётся в ядре, какие интерфейсы фиксируем как публичные.
  • Оценка сроков по этапам: проектирование, реализация, миграции, тесты, выкатка — с отдельным запасом на несовместимости.
  • План выката и отката: как модуль включается на стенде, как проверяется на боевых данных и как отключается без потери заказов.

Что нужно от вас для старта: доступ к репозиторию и тестовому окружению, описание процессов, которые модуль закрывает, и контакт инженера, который ведёт проект со вашей стороны. Если части данных нет — разбор всё равно возможен, просто часть оценок пойдёт с допущениями, и мы это отметим прямо в плане.

Опишите задачу — вернёмся с оценкой сроков и планом работ по этапам, с явно отмеченными рисками и точками проверки.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060