Skip to main content
← Back to articles

Scaling a White-Label SaaS for 30+ Brands

Deploy frequency, blast-radius isolation, and infra cost trade-offs from a multi-tenant refactor.

Nestlancer Editorial

Share

Scaling a White-Label SaaS for 30+ Brands

Deploy frequency, blast-radius isolation, and infra cost trade-offs from a multi-tenant refactor. The team prioritized reversible migrations while migrating on a Nestlancer-style stack—gateway at the edge, domain NestJS services, Prisma on Postgres, async work through RabbitMQ, and media on Backblaze B2.

Where we started

The team inherited one codebase serving 30+ white-label brands with per-tenant theming. The triggering incident was clear: noisy neighbor queries on shared tables spiked P95 latency for everyone. Leadership funded the refactor when customer-visible latency and support load rose together—not when the diagram looked messy.

Architecture before

  • Single deploy artifact coupling unrelated domains
  • Synchronous cross-module HTTP with partial timeouts
  • Mixed read/write traffic on one database primary
  • Ad-hoc file storage complicating virus scan and CDN caching

Migration timeline

Discovery

Mapped one codebase serving 30+ white-label brands with per-tenant theming and inventoried which routes could move behind the gateway without user-visible changes.

Strangler cutover

Routed new traffic through NestJS handlers while legacy paths drained over 7 weeks.

Reliability hardening

Added read replicas, Redis, gateway with explicit SLOs owned by a 8-engineer platform squad.

Stabilize and learn

Ran game days for DLQ replay, replica lag failover, and presigned upload expiry edge cases.

Patterns we applied

  • Read replicas via a dedicated Prisma read client served list and analytics traffic.
  • Redis guarded hot list endpoints without stale writes after publish events.
  • API gateway centralized auth, OpenAPI, and aggregation for mobile clients.

Code sketch from the cutover

@Injectable()
export class PostsRepository {
  constructor(
    @Inject('PRISMA_WRITE') private write: PrismaClient,
    @Inject('PRISMA_READ') private read: PrismaClient,
  ) {}

  listPublished() {
    return this.read.post.findMany({ where: { status: 'PUBLISHED' } });
  }
}

Results

  • isolated heavy reporting to replicas and cut P95 list latency 38%
  • On-call pages for queue backlog fell after outbox lag dashboards went live
  • Product teams could ship blog and portfolio changes without redeploying payments
  • Support tickets citing 'stale listings' dropped once read paths moved to replicas

Retrospective checklist

  • Game day DLQ replay documented with ordering notes
  • Replica lag runbook tested in staging monthly
  • Presigned upload TTL aligned with mobile retry policy
  • Gateway error envelope consistent across all domain services
  • Post-incident templates link to dashboards—not screenshots

Lesson

Microservices did not arrive on day one. The team earned splits by proving operational ownership per domain—not by copying a reference diagram. The Nestlancer patterns above were adopted only after metrics justified the coordination cost.

Comments

Loading comments…

Related posts