Створення кастомного модуля для 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
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1141
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060