02 / Case Study
Multi-Tenant Seller Marketplace
Seller operations, order management, and Shopify synchronisation for a multi-tenant marketplace, rebuilt on NestJS with a migration of 800+ sellers and zero data loss.
The problem
The marketplace runs hundreds of independent sellers on one platform — each with their own products, orders, subscriptions, and payout terms — synced against Shopify as the storefront of record. The existing system had grown organically; migrating it onto a new schema without breaking a single seller's live orders, and without the query latency that comes with millions of rows across shared tables, was the core problem.
Architecture
NestJS with TypeORM over MySQL for the transactional core, MongoDB for a subset of flexible document data, and a dedicated sync layer reconciling seller catalogues and orders against the Shopify Admin API via webhooks. Inbound Shopify data lands in a raw dump table before it is mapped into live order entities, so a re-run of a sync stages rather than corrupts — the staging table is the idempotency boundary. A status mapper translates Shopify's and each carrier's own vocabulary into one internal order-status model, which is what keeps the rest of the system from growing per-integration conditionals. Carriers themselves are modelled as data — a shipping-partner entity plus a per-seller onboarded-partner join — so adding one is configuration rather than a branch. Role-based access control is modelled directly in the schema (userRole / userPermission entities) rather than bolted on, so the same authorization path serves sellers, staff, and admin.
Decisions
Migrate seller-by-seller, not table-by-table
800+ sellers — products, orders, and subscriptions — were moved with full validation per seller rather than a single big-bang schema cutover, so a bad record surfaced against one seller instead of failing the whole migration.
Index for the queries that actually run
Schema and indexing were reworked against real query patterns across roughly 2.7 million records, cutting query latency by 25% — the kind of gain that only shows up once you profile production traffic rather than guess at indexes upfront.
Payments split by purpose
Razorpay handles subscription billing; Cashfree handles invoice payments and automated seller settlements — two different money paths kept deliberately separate rather than forced through one abstraction.
Instrument before optimising
OpenTelemetry was wired in with a custom interceptor and a tracer decorator that could be dropped onto specific methods, feeding Signoz. That is what turned the latency work from guesswork into targeted fixes — the 25% figure is measured against traces, not inferred from a stopwatch.
Long-running reports leave the request path
Seller exports run through a dedicated queue with its own entities, so generating a large report is a job with a status a seller can poll rather than a request that times out.