Architecture Overview
The three services
A CarphaCom instance runs three Node.js processes managed by PM2:
- medusa-backend (port
9000) — Medusa v2 server. All business logic, database access, REST APIs. - admin-panel (port
3001, basePath/app) — Next.js 16 admin UI. Talks to the backend via cookie-authenticated proxy routes. - nextjs-storefront (port
8000) — Next.js 15 customer-facing storefront. Reads product data via the Medusa store APIs.
A single nginx fronts all three: /app/* → admin, /store/* and /admin/* → backend,
everything else → storefront.
Request flow: customer adds to cart
- Browser →
nextjs-storefront(POST /api/cart/add) - Storefront calls
medusa-backend(POST /store/carts/{id}/line-items) - Backend writes to PostgreSQL, returns updated cart JSON.
- Storefront returns to browser, triggers cart-drawer revalidation.
Request flow: merchant installs a plugin
- Browser →
admin-panel(POST /app/api/marketplace/install) - Admin proxy →
medusa-backend(POST /admin/plugins/install) with the cookie'smedusa_tokenpromoted toAuthorization: Bearer. - Backend downloads plugin ZIP from
carphacom.com, verifies signature, extracts to/opt/carphacom/installed/<id>/, runs migrations. - Backend writes a row to
carphacom_plugin, thenpm2 reload medusa-backend. - After reload, the plugin's menu items, providers, and routes are live.
Where to extend
| Goal | Where to add code |
|---|---|
| New REST endpoint (admin) | medusa-backend/src/api/admin/<name>/route.ts |
| New REST endpoint (store) | medusa-backend/src/api/store/<name>/route.ts |
| New database table | medusa-backend/src/modules/<module>/models/... + migration |
| New scheduled job | medusa-backend/src/jobs/<name>.ts |
| New admin page | admin-panel/src/app/(admin)/<route>/page.tsx |
| New storefront page | nextjs-storefront/src/app/[countryCode]/(main)/<route>/page.tsx |
| Payment provider | Plugin extending AbstractPaymentProvider |
| Fulfillment provider | Plugin extending AbstractFulfillmentProviderService |
Federation
The marketplace at carphacom.com is itself a CarphaCom instance with one extra module
(marketplace_product). Tenant instances call its public store APIs:
GET /store/marketplace/feed— catalogGET /store/marketplace/updates?versions=...— update checkGET /store/marketplace/download/<id>/<version>— signed download URL
No customer data ever leaves the tenant instance. Only catalog reads + license verification
go to carphacom.com.