Skip to content
Mohammad.
All posts

Building a Multi-Vendor Marketplace with NestJS

Lessons from architecting a 35+ module marketplace and logistics platform — modular boundaries, real-time features, and keeping it maintainable.

NestJSArchitectureBackend

A marketplace looks simple from the outside: buyers, sellers, products, orders. Under the hood, it fans out fast — providers, listings, carts, coupons, reviews, logistics, shipments, chat, moderation, notifications. Here's how I kept a 35+ module NestJS backend from turning into a big ball of mud.

Modules are boundaries, not folders

The temptation is to group code by type (controllers/, services/, entities/). That scales badly. Instead, each domain owns its full vertical slice:

// orders/orders.module.ts
@Module({
  imports: [TypeOrmModule.forFeature([Order, OrderItem]), PaymentsModule],
  controllers: [OrdersController],
  providers: [OrdersService],
  exports: [OrdersService],
})
export class OrdersModule {}

When a module only exports its service, the dependency graph stays explicit and you can reason about blast radius before you touch anything.

Push real-time to the edges

In-app chat (Ably) and audio/video calls (Agora) don't belong in your request/response core. Treat them as integrations behind a thin service so the rest of the app depends on an interface, not a vendor.

Migrations are part of the design

With TypeORM across a core and a logistics database, versioned migrations are the contract between code and data. Generate them, review them, and never let the schema drift.

The result: a platform that's still understandable a year in — which is the only metric that matters for maintainability.