Architecture

Monorepo architecture, designed to help you build scalable and maintainable applications.

Following a monorepo architecture, AI Boilerplate is designed to help you build scalable and maintainable applications.

The boilerplate uses a Turborepo monorepo, making it easy to extend your project with features like a mobile application and share code and configs between applications.

With the modular architecture, you can add or remove features and customize the application to fit your needs.

You can manage code for different projects, like web applications and marketing websites, all in one place.

Folder Structure

In a Turborepo setup, different parts of the project are organized into apps and packages to facilitate modular development, reuse, and maintenance.

Inside apps are the different applications we can deploy. Inside packages are reusable packages shared among multiple applications.

  • dashboard: Main SaaS application.
  • marketing: Public website, docs, blog, and legal pages.
  • api: tRPC routers, services, and repositories.
  • auth: Better Auth configuration with server/client exports.
  • configs: Shared configurations for ESLint, Prettier, Tailwind CSS, and TypeScript.
  • db: Prisma schema, client, and extensions.
  • email: Email sending and templates.
  • payments: Stripe integration.
  • ui: Shared UI components built on shadcn/ui.
  • utils: Shared feature flags, helpers, constants, and types.
  • validations: Shared Zod schemas.

Feature-Based Folder Structure

The applications use a feature-based folder structure to organize code.

This structure groups related code together, making it easier to manage and scale the project.

We create folders adhering to a specific feature/route inside each route in the app folder. By prefixing the folder name with _ (underscore), we can ensure that the folder is excluded from the Next.js file-system routing.

  • _components: Reusable components scoped to the route.
  • _contexts: Contexts for the route.
  • (auth): Public pages (sign in, sign up).
  • (main): Authenticated pages (dashboard, settings).
  • api: Route handlers for tRPC, Better Auth, and Stripe.
  • lib: Shared libraries and utilities.

You can add more folders as needed for each feature to keep the code organized.

To avoid circular dependencies, use relative paths when importing within the same folder (e.g., the account feature).

Use absolute paths only when importing from different features.

API Architecture

The API follows a three-layer architecture: Router → Service → Repository.

billing.router.ts
billing.service.ts
billing.repository.ts
billing.input.ts
  • Router: Thin controller layer that maps procedures to service methods.
  • Service: Business logic, feature flag checks, error handling.
  • Repository: Pure Prisma queries with explicit field selection.
  • Input: Zod schemas for procedure input validation.

Database Schema

The database schema is defined using Prisma in packages/db/prisma/schema.prisma.

This file contains the models that define the database tables and their relationships.

Models

User

The User model stores information such as name, email, role, plan, and credits.

Links to accounts, sessions, subscription, and credit transactions.

Users can have multiple accounts, sessions, credit transactions, and a subscription.

You can extend this model to include additional fields as needed.

Account

The Account model manages authentication details for various providers. It stores tokens and metadata for third-party authentication, ensuring secure access.

Supports multiple providers for the same user.

Do not modify this model. It is tightly coupled to Better Auth.

Session

The Session model tracks active user sessions. It stores the token, expiration, user agent, and IP address.

Do not modify this model. It is tightly coupled to Better Auth.

Verification

The Verification model stores tokens for email verification and magic links.

Do not modify this model. It is tightly coupled to Better Auth.

Subscription

The Subscription model stores Stripe subscription details linked to a user. It tracks the product, status, billing interval, current period, trial dates, and cancellation state.

Do not modify this model. It is tightly coupled to Stripe.

CreditTransaction

The CreditTransaction model stores credit history. Each entry records the type, amount, running balance, description, and optional payment reference.

When a new credit transaction is created, the running balance in the User model (User.credits) is automatically updated.

You can extend this model to include additional fields as needed.

Enums

Role

User roles.

  • admin
  • user

SubscriptionStatus

Stripe subscription statuses.

  • active
  • canceled
  • incomplete
  • incomplete_expired
  • past_due
  • paused
  • trialing
  • unpaid

BillingInterval

Billing intervals.

  • monthly
  • yearly

CreditTransactionType

Credit transaction types.

  • grant: Granted by admin or system (e.g., welcome gift, trial grant, renewal grant).
  • purchase: Card purchase.
  • consumption: Consumed by a feature or action.
  • adjustment: Manual or automatic correction.
  • refund: Card refund.

Enums are synced between the Prisma schema and TypeScript types, so changes in one are reflected in the other.

Import Paths

We employ absolute import paths to make it easier to import files and components across the project.

It also makes it easier to move files around without breaking imports.

Root

To import from the root of the project, you can use the ~ (tilde) alias.

import Logo from "~/public/logo.svg";

App

To import from the app (or src) folder, you can use the @ (at) alias.

import { api } from "@/lib/trpc/react";

Cross-Package

To import from a package, you can use the @package alias.

import { auth } from "@package/auth/server";
import { db } from "@package/db";
import { sendEmail, MagicLink } from "@package/email";
import { Button } from "@package/ui/button";
import { features } from "@package/utils";
import { signInSchema } from "@package/validations";