Перейти к основному содержимому

Архитектура и состав монорепы

Репозиторий — npm workspaces + Turborepo. Node 20+ (витрина требует 22.13+), npm 10.

Приложения​

КаталогПакетЧто это
apps/backend@dtc/backendMedusaJS v2 (2.17.2): Store/Admin API, админка на /app, фоновые задачи
apps/web@auto-paint-store/webВитрина, Next.js App Router + Tailwind
apps/cms@auto-paint-store/cmsStrapi: содержимое главной страницы, баннеры, вкладки
apps/e2e@auto-paint-store/e2ePlaywright
apps/docsapps-docsЭта документация (Docusaurus)

Инфраструктура​

docker-compose.yml поднимает postgres, redis, meilisearch, minio, backend, cms, web и одноразовые сервисы migrate, backend-init, minio-init, cms-db-init, cms-minio-init.

  • Redis обязателен: на нём кэш, шина событий и движок workflow (medusa-config.ts).
  • PostgreSQL — источник истины. Meilisearch — только публичный поисковый индекс каталога, он синхронизируется по событиям (subscribers/catalog-index-sync.ts). Любое расхождение решается в пользу данных Medusa: карточка товара дополнительно проверяет доступность через backend, чтобы снятый с публикации товар давал 404 даже при устаревшем индексе.
  • MinIO / S3 — изображения товаров и загрузки Strapi.

Как данные ходят между частями​

Каталог и заказы. Витрина ходит в backend: стандартные Store API — для корзины и аккаунтов, но не для оформления и не для чтения истории заказов покупателем — эти два кастомные: оформление идёт через POST /catalog/checkout (placeCatalogOrderWorkflow), список/карточка/отмена заказов покупателя — через store/account/orders/*. Плюс кастомные catalog/* там, где стандартного API не хватает вовсе (поиск, карточка товара, валидация корзины). См. Кастомные API.

Контент. Редактор пишет в Strapi → Strapi зовёт вебхук backend → backend перечитывает снимок. См. Контент из Strapi на витрину.

Остатки и заказы склада. Модуль moysklad-integration тянет остатки по расписанию и отправляет заказы покупателей. См. Модуль МойСклад.

Фоновые задачи​

apps/backend/src/jobs — расписание в config.schedule каждого файла:

ЗадачаРасписаниеЧто делает
cms-snapshot-refreshкаждую минутуподстраховка вебхука Strapi
moysklad-order-sync-retryкаждую минутуповтор неудачной отправки заказа
moysklad-stock-syncкаждые 15 минутостатки из МойСклад
expire-catalog-payment-cartsежечасносброс корзин с брошенной оплатой
poll-apiship-tracking-statusежечасностатусы отправлений ApiShip
abandoned-cart-reminder03:00письмо о брошенной корзине
sync-cdek-pickup-points-daily03:00справочник ПВЗ СДЭК

Детали устройства платёжных/складских/доставочных заданий — в соответствующих страницах (Оплата, Модуль МойСклад, Доставка). Что стоит знать о самих job'ах отдельно:

  • expire-catalog-payment-carts читает платёжные сессии страницами по 100 записей до полного обхода (jobs/expire-catalog-payment-carts.ts:14). (*) Обработка корзин не изолирована try/catch — исключение одного workflow прерывает весь текущий цикл и оставляет последующие просроченные корзины до следующего запуска (jobs/expire-catalog-payment-carts.ts:72) — в отличие от moysklad-stock-sync, где ошибка одной позиции не мешает остальным.
  • abandoned-cart-reminder выбирает только зарегистрированного покупателя с непустой корзиной, простаивающей больше 7 дней (abandoned-cart-reminder.ts:43). Повторное напоминание по той же корзине разрешается только после нового изменения корзины (:113). (*) Если SMTP уже отправил письмо, но запись об этом не сохранилась, ошибка лишь логируется — следующий запуск может отправить письмо повторно (jobs/abandoned-cart-reminder.ts:99). Отписка (workflows/opt-out-abandoned-cart-reminders.ts) добавляет флаг abandoned_cart_reminders_opt_out через merge в существующий customer.metadata, не перезаписывая объект целиком — прямая замена metadata в этом workflow стёрла бы любые другие кастомные поля покупателя.
  • cms-snapshot-refresh делает то же, что вебхук Strapi — ошибка одного прогона изолируется и только логируется, не роняет остальные задачи. Подробнее — Контент из Strapi.

Кастомные модули​

apps/backend/src/modules: moysklad-integration, cms-snapshot, meilisearch, site-content, payment-provider-config, shipping-provider-registry, abandoned-cart-reminder, wishlist, notification-smtp.

Правило проекта: сначала стандартное решение Medusa, кастомный модуль — только там, где стандартного API действительно нет.