bitarch.dev
Architecture

The SaaS Factory: Bootstrapping Products Faster with Monorepos

I got tired of setting up the same infrastructure for every new project. Here is how I turned my workflow into an assembly line.

Dhruba Baishya
Dhruba Baishya
Software Engineer
Mar 04, 2026
8 min read

For years, I'd start a new app and end up solving the same problems from scratch. I was reinventing logic differently every time because I didn't have a reliable foundation to build on. I'd spend weeks on infrastructure before writing a single line of product code. It wasn't just slow—it was exhausting.

I've moved away from this "start-from-scratch" mentality. Instead, I treat my entire setup as a single machine: a **SaaS Factory**. By using a monorepo, I've turned those weeks of setup into minutes of configuration.

The Journey to the Factory

This approach didn't happen overnight. It's the result of building dozens of apps over the years, many of which never really went anywhere. I'm still in the middle of it—trying to find that one SaaS that pays the bills and feels right to work on.

Along the way, I've spent a lot of time in big companies as a software engineer. Working on enterprise systems taught me which tools are reliable and which ones just add noise. I've taken those enterprise patterns and tech choices and simplified them into something I can actually use alone.

The monorepo is the core of the factory. It's the one place where all these pieces live together. These days, I don't start from zero. I just add a new folder to the factory. It's how I build everything now.

Separating Implementation from Logic

The system works because of a strict separation between implementation and logic. My monorepo is divided into two primary directories:

  • apps/: This is where the products live. Each folder is a deployable application—a Next.js dashboard, a React Native mobile app, or a documentation site. These are focused solely on user experience and routing.
  • packages/: This is where the core logic lives. It contains shared libraries like the UI component library, the database SDK, and internal tooling. Any app can import these locally without needing to republish to a registry.

Anatomy of an App

Inside the `apps/` directory, each project follows a predictable, modular structure. I don't believe in monolithic "all-in-one" frameworks for complex products.

frontend/

A Next.js or React application dedicated purely to the UI and client-side state.

backend/

A dedicated FastAPI service handling the core business logic and data processing.

supabase/ or infra/

Where the plumbing lives. `supabase/` handles SQL migrations and edge functions, while `infra/` manages local or cloud setups (AWS/GCP) via SST or Terraform.

Makefile

The glue that standardizes commands like `make dev` or `make deploy`.

Anatomy of Packages

The `packages/` directory is where I store everything that can be reused. Instead of copy-pasting code, I build internal libraries that every app can import. Here's a quick look at the core of my shared infrastructure:

supabase-sdk

This is the source of truth for my data layer. It provides a set of shared utilities and clients pre-configured for Next.js environments (SSR, Middleware, and Client Components). By centralizing authentication guards, global state listeners, and storage hooks here, every app in the monorepo handles identity and data consistently without redundant setup.

python/payment

Handling money is one of those things you don't want to mess up. This is a dedicated Python package for managing payment lifecycles and subscriptions. It implements a **Factory Pattern** to support multiple payment providers. While I primarily use Stripe, the abstraction allows me to switch or add providers by simply adding a new gateway implementation, keeping the core business logic in my apps completely decoupled from the specific payment API.

Why FastAPI over a Next.js BFF?

A common question is: "Why not just use Next.js API routes or a BFF (Backend-for-Frontend)?" While Next.js works well for the web, I prefer keeping my core logic in **FastAPI** for a few specific reasons:

Comfort and Productivity

Honestly, I'm simply more comfortable building production-ready backends with Python and FastAPI. I prefer the clean, readable syntax of Python over TypeScript for complex business logic. When I return to my own code months later, I find FastAPI services significantly easier to read and maintain than their TypeScript counterparts.

The developer experience—especially with Pydantic for data validation and automatic OpenAPI documentation—is hard to beat when building complex systems.

True Separation of Concerns

By decoupling the backend from the frontend framework, the business logic isn't tied to the web's lifecycle. This makes it straightforward to share the same API between a web dashboard, a mobile app, and even background worker scripts without duplication.

Performance and Async Support

FastAPI is built on top of Starlette and Pydantic, making it one of the fastest Python frameworks available. Its native support for `async/await` and easy integration with heavy data processing libraries (like those in the Python ecosystem) gives it an advantage over standard Node.js API routes for resource-intensive tasks.

The Trade-off: Deployment Complexity

Of course, there's one major caveat: I can't just hit "deploy" on Vercel for the entire stack. While Next.js makes deployment straightforward, a FastAPI backend requires a dedicated provider like AWS, Google Cloud, or DigitalOcean.

For many SaaS apps, staying entirely within the Supabase ecosystem is more than enough. However, because I prefer the control and maintainability of a FastAPI backend, I choose to manage the additional infrastructure complexity on AWS. For me, that trade-off is worth it for the long-term health of the codebase.

Keeping Mobile and Web in Sync

My mobile applications aren't isolated islands. In this monorepo, the React Native and Flutter apps live right next to the web apps.

When I update a validation schema or a business rule in a shared package, both the web and mobile apps get that update instantly. There's no "syncing" repositories or wondering if the iOS app is using the latest API types. The monorepo enforces that synchronization by design.

The Workflow in Practice

The "factory" approach pays for itself the moment I start a second or third project. It's no longer about starting from zero; it's about assembling existing components into a new configuration.

The process for every new app is straightforward:

  • Bootstrap in minutes: I create a new directory in `apps/` and mirror the standard structure—`frontend/`, `backend/`, and `infra/`.
  • Using Shared Packages: Instead of building a new UI or auth system, I simply import `@repo/ui` and `@repo/supabase-sdk`. These provide the stuff I've already debugged and improved across other projects.
  • Plugging in Logic: If the app needs payments, I plug in the existing `python/payment` package. If it needs specialized processing, I pull from my collection of shared utilities.

Because the "plumbing"—the auth, the UI, the database, the payment lifecycle—is already solved in the `packages/` directory, I can focus 100% of my energy on the actual features that matter.

Technical Agility: Failing Fast

While this setup makes it easier to bootstrap, it also makes it easier to fail. I don't build every feature on day one.

Because the core business logic is centralized in shared packages, tearing down an app that doesn't work out is as simple as deleting a folder in `apps/`. The valuable logic remains in the `packages/` directory, ready to be reused in the next experiment. This architecture doesn't just help me build faster; it allows me to pivot without losing the technical progress I've made.

A monorepo is a multiplier. It lets you build the hard stuff once and reuse it everywhere. If you're tired of the "Day 0" setup grind, it might be time to stop building apps and start building your own factory.

Read More from the Author

The Technical Debt of AI Speed

AI helps us ship faster than ever, but it's also creating a new kind of technical debt. Here is how to manage the speed without losing quality.