• ShipSaaSDocumentation
  • Introduction
  • Getting Started
    • Installation
    • Environment Setup
    • Database Setup
    • Development Guide
  • Database
    • Repository Pattern
    • Drizzle Queries
    • Migration Workflow
    • Drizzle Studio
    • Best Practices
  • File Management
    • File Upload Component
    • Server Actions
    • Storage Configuration
  • Authentication Methods
    • Credentials Authentication
    • Magic Link Authentication
    • Two-Factor Authentication (2FA)
    • Social Authentication
    • API Keys & Bearer Tokens
    • Organizations (Multi-tenant)
  • Authorization & Permissions
    • Roles & Hierarchy
    • Route Protection
    • API Protection
    • CASL Permissions
  • Email & Notifications
    • Resend Setup
    • React Email Templates
    • Notification System
    • Email Workflows
    • Customization & Extension
  • Stripe & Payments
    • Stripe Setup
    • Better Auth Integration
    • Checkout Methods
    • Subscription Management
    • Admin Dashboard
    • Webhook Events
  • Architecture & Development
    • Presentation Layer
    • Service Layer
    • Persistence Layer
    • Helpers Architecture
    • Project Structure
    • Development Workflow
  • Testing
    • General Testing
    • Unit Testing
    • UI Component Testing
    • Repository Testing
    • Service Testing
    • End-to-End Testing
  • Deployment
    • Vercel Deployment
    • GitHub Actions CI/CD
    • Database Backup & Restore
    • Database Migrations
    • Troubleshooting Deployment
  • AI Agents Integration
  • Blog Management
    • Blog Setup
    • MDX Blog
    • Database Blog
  • Async Tasks
    • Setup
    • Email Workflows
    • Payment Processing
    • Maintenance Jobs
  1. Documentation
  2. Architecture & Development
Buydocschangelogsdemocommunity

Architecture & Development

PreviousWebhook EventsNextPresentation Layer
On this page
Architecture OverviewCore Architecture PrinciplesExample: Complete Data FlowKey BenefitsArchitecture Documentation

The boilerplate implements a strict 3-layer architecture that ensures separation of concerns, maintainability, and scalability. Each layer has specific responsibilities and communicates through well-defined interfaces.

Perfect for: Building maintainable, testable, and scalable SaaS applications with clear separation between UI, business logic, and data access.

Architecture Overview

The system follows a unidirectional data flow through three main layers:

3-Layer Architecture:

๐Ÿ–ฅ๏ธ  PRESENTATION LAYER
โ”œโ”€โ”€ React Server Components (RSC)
โ”œโ”€โ”€ Client Components ("use client")
โ”œโ”€โ”€ Server Actions (mutations)
โ””โ”€โ”€ DAL (Data Access Layer with cache)
                โฌ‡๏ธ
โš™๏ธ  SERVICE LAYER
โ”œโ”€โ”€ Business Logic
โ”œโ”€โ”€ Validation (Zod schemas)
โ”œโ”€โ”€ Authorization (CASL RBAC)
โ”œโ”€โ”€ Facades (interfaces)
โ””โ”€โ”€ Interceptors (logging, audit)
                โฌ‡๏ธ
๐Ÿ—„๏ธ  PERSISTENCE LAYER
โ”œโ”€โ”€ Drizzle ORM
โ”œโ”€โ”€ Database Models
โ”œโ”€โ”€ Repositories (DAO pattern)
โ””โ”€โ”€ Type Transformations

Read Operations (Queries):

Component โ†’ DAL (with cache) โ†’ Facade โ†’ Service โ†’ Repository โ†’ Database

Write Operations (Mutations):

Server Action โ†’ Facade โ†’ Service (validation + auth) โ†’ Repository โ†’ Database

Key Benefits:

  • Cached reads with 'use cache' in the DAL function
  • Validated writes with business rules
  • Authorized access at service level
  • Clean separation of concerns

Strict Layer Communication:

  • Presentation โ†’ Only calls DAL (reads) or Server Actions (writes)
  • DAL โ†’ Transforms DB models to domain types
  • Service โ†’ Contains business logic + validation + authorization
  • Repository โ†’ Handles database operations only
  • No layer bypassing - Each layer can only call the next one

Type Separation:

  • Presentation uses Domain Types
  • Services use Domain Types
  • Persistence uses Drizzle Types

Core Architecture Principles

๐Ÿ—๏ธ

Layered Architecture

Strict 3-layer separation

โ€ข Presentation: RSC, Client Components, Server Actions
โ€ข Service: Business logic, validation, authorization
โ€ข Persistence: Drizzle ORM, repositories, models
โ€ข No layer bypassing: Each layer calls only the next
๐Ÿ”„

Unidirectional Flow

Clear data movement

โ€ข Reads: Component โ†’ DAL โ†’ Service โ†’ Repository
โ€ข Writes: Server Action โ†’ Service โ†’ Repository
โ€ข Caching: DAL layer with 'use cache' + cacheTag
โ€ข Validation: Service layer with Zod schemas
๐Ÿ”

Security & Authorization

RBAC with CASL

โ€ข Authorization: Every service operation checked
โ€ข Validation: Input validation at service boundary
โ€ข Type Safety: Domain types prevent data leaks
โ€ข Audit Trail: Interceptors log all operations
โšก

Performance

Optimized at every layer

โ€ข Server-first: RSC by default
โ€ข Caching: DAL layer, Partial Prerendering
โ€ข Database: Connection pooling with Drizzle
โ€ข Client: Minimal client-side JavaScript

Example: Complete Data Flow

Here's how a typical user operation flows through all layers:

Get User Profile:

typescript
// ๐Ÿ–ฅ๏ธ PRESENTATION (RSC Component)
import { getUserByIdDal } from '@/app/dal/user-dal'

export default async function UserProfile({ userId }: { userId: string }) {
  const user = await getUserByIdDal(userId) // Cached read
  return <div>{user.name}</div>
}

// ๐Ÿ“Š DAL (Data Access Layer)
import { cache } from 'react'
import { cacheLife, cacheTag } from 'next/cache'
import { getUserByIdService } from '@/services/facades/user-service-facade'

// 'use cache' makes the read cacheable across requests; cache() from React
// still deduplicates it within a single request.
export const getUserByIdDal = cache(async (id: string) => {
  'use cache'
  cacheLife('hours')
  cacheTag(`user:${id}`)

  const userModel = await getUserByIdService(id)
  return transformToUserDTO(userModel) // Transform to domain type
})

// โš™๏ธ SERVICE (Business Logic)
export const getUserByIdService = async (id: string) => {
  const parsed = userUuidSchema.safeParse(id) // Validation
  if (!parsed.success) throw new ValidationError()

  const granted = await canReadUser(parsed.data) // Authorization
  if (!granted) throw new AuthorizationError()

  return await getUserByIdDao(parsed.data) // Call repository
}

// ๐Ÿ—„๏ธ REPOSITORY (Data Access)
export async function getUserByIdDao(id: string): Promise<UserModel> {
  return await db.query.users.findFirst({
    where: eq(users.id, id)
  })
}

Update User Profile:

typescript
// ๐Ÿ–ฅ๏ธ PRESENTATION (Server Action)
import { updateUserService } from '@/services/facades/user-service-facade'

export async function updateUserAction(formData: FormData) {
  "use server"

  const result = await updateUserService({
    id: formData.get('id') as string,
    name: formData.get('name') as string,
    email: formData.get('email') as string
  })

  revalidatePath('/account')
  return result
}

// โš™๏ธ SERVICE (Business Logic)
export const updateUserService = async (data: UpdateUserInput) => {
  // 1. Validation
  const parsed = updateUserSchema.safeParse(data)
  if (!parsed.success) throw new ValidationError()

  // 2. Authorization
  const granted = await canUpdateUser(parsed.data.id)
  if (!granted) throw new AuthorizationError()

  // 3. Business Logic
  const updatedUser = await updateUserDao(parsed.data)

  // 4. Side Effects (optional)
  await sendUserUpdatedEmail(updatedUser)

  return updatedUser
}

// ๐Ÿ—„๏ธ REPOSITORY (Data Access)
export async function updateUserDao(data: UpdateUserData) {
  return await db.update(users)
    .set({
      name: data.name,
      email: data.email,
      updatedAt: new Date()
    })
    .where(eq(users.id, data.id))
    .returning()
}

Key Benefits

โœ…

Maintainability

  • โ€ข Clear separation of concerns
  • โ€ข Easy to locate and fix issues
  • โ€ข Consistent patterns across features
  • โ€ข Self-documenting code structure
โœ…

Testability

  • โ€ข Each layer can be tested independently
  • โ€ข Easy mocking of dependencies
  • โ€ข Business logic isolated in services
  • โ€ข Database operations isolated in repositories
โœ…

Scalability

  • โ€ข Layers can scale independently
  • โ€ข Easy to add new features
  • โ€ข Caching at appropriate layers
  • โ€ข Performance optimizations per layer

Architecture Documentation

๐Ÿ–ฅ๏ธ

Presentation Layer

RSC, Client Components, Server Actions, DAL, and Forms

โš™๏ธ

Service Layer

Business logic, validation, authorization, facades, and interceptors

๐Ÿ—„๏ธ

Persistence Layer

Drizzle ORM, models, repositories, and type transformations

๐Ÿ”ง

Helpers Architecture

Client/server separation and utility organization

๐Ÿ“

Project Structure

File organization, naming conventions, and import rules

๐Ÿงช

Development Workflow

Feature development process and testing strategies

Architecture ready! Your application follows enterprise-grade patterns that ensure maintainability, testability, and scalability as your SaaS grows.