Real World Example

From ShopEase to a Real Company

The architecture you built is close to what real Indian e-commerce and D2C companies (boAt, Mamaearth, Lenskart-style brands on custom stacks) run in their first years: a modular NestJS monolith on PostgreSQL and Redis, a Next.js storefront, a payment gateway with webhooks, queues for notifications and a few cron jobs. As traffic grows, the usual next steps are:

  • Read replicas for the database and a CDN in front of public GET endpoints.
  • Splitting the busiest or most independent module (notifications, search) into its own service with @nestjs/microservices and Kafka/RabbitMQ.
  • A search engine (Elasticsearch / OpenSearch / Typesense) fed by product events.
  • OpenTelemetry tracing across services.

src/app.module.ts (final)

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true, cache: true, load: [databaseConfig, paymentsConfig], validate }),
    LoggerModule.forRoot({ pinoHttp: { redact: ['req.headers.authorization'] } }),
    ThrottlerModule.forRoot([{ name: 'long', ttl: 60_000, limit: 200 }]),
    CacheModule.registerAsync({ isGlobal: true, inject: [ConfigService], useFactory: redisCacheFactory }),
    BullModule.forRootAsync({ inject: [ConfigService], useFactory: bullFactory }),
    ScheduleModule.forRoot(),
    PrismaModule,
    AuthModule, UsersModule, CategoriesModule, ProductsModule, CartModule,
    OrdersModule, PaymentsModule, NotificationsModule, TasksModule, HealthModule,
  ],
  providers: [
    { provide: APP_GUARD, useClass: ThrottlerGuard },
    { provide: APP_GUARD, useClass: JwtAuthGuard },
    { provide: APP_GUARD, useClass: RolesGuard },
    { provide: APP_INTERCEPTOR, useClass: TransformInterceptor },
    { provide: APP_FILTER, useClass: AllExceptionsFilter },
    { provide: APP_PIPE, useValue: new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }) },
  ],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(RequestIdMiddleware).forRoutes('{*splat}');
  }
}