Split a NestJS app the way Lahebo billing forced me to

A module per folder type looks tidy until subscriptions, auth, and invoices import each other. Follow this split on any NestJS SaaS.

Lahebo is NestJS + Vue with Stripe subscriptions. Early on I grouped every controller in one folder. Changing a billing rule meant touching five directories. This is the layout I use now.

Step 1: One module per product capability

orders, vendors, subscriptions, media. Not controllers/, services/, dto/ at the root.

import { Module } from "@nestjs/common";
import { KernelModule } from "../kernel/kernel.module";
import { SubscriptionsController } from "./subscriptions.controller";
import { SubscriptionsService } from "./subscriptions.service";

@Module({
  imports: [KernelModule],
  controllers: [SubscriptionsController],
  providers: [SubscriptionsService],
  exports: [SubscriptionsService],
})
export class SubscriptionsModule {}

Step 2: Put auth, logging, and config in a small kernel

Features import the kernel. Features do not import each other unless there is a real domain reason — same rule I would apply to WebPasal’s site-builder modules.

Step 3: Keep DTOs next to the controller that owns them

Step 4: Ban feature-to-feature imports in lint if you have to

The learning: NestJS folders are how Lahebo stays shippable after year two. Copy the rule, not the folder names.