The boilerplate follows a carefully designed project structure that promotes maintainability, scalability, and developer productivity. The structure enforces clear boundaries between different concerns and provides consistent patterns across the entire codebase.
Perfect for: Understanding the codebase organization, following established patterns, and maintaining consistency as your application grows.
Project Overview
ShipSaaS is a monolithic Next.js SaaS boilerplate with a well-organized layered architecture. It emphasizes clear separation of concerns with dedicated layers for routing, components, services, and database access.
Consistent import patterns improve code readability and dependency tracking:
Import Order and Grouping (ShipSaaS Standard):
typescript
// โ GOOD: Organized import groups// 1. Node.js built-insimport fs from 'fs'import path from 'path'// 2. External librariesimport React from 'react'import { z } from 'zod'import { eq } from 'drizzle-orm'// 3. Internal services and business logicimport { getUserByIdService } from '@/services/facades/user-service-facade'import { getUserByIdRepository } from '@/db/repositories/user-repository'// 4. UI components and utilitiesimport { Button, Card } from '@/components/ui'import { UserProfile } from '@/components/features/user'import { formatDate } from '@/lib/helper/date-helper'// 5. Types and constantsimport type { User } from '@/services/types/domain'import { MAX_RETRY_ATTEMPTS } from '@/lib/constants'// 6. Relative imports (local to current file)import { localHelper } from './local-helper'import './styles.css'export function MyComponent() { // Component implementation}
// โ GOOD: Import from service facades// user-service-facade.ts - Public interfaceimport { getUserByIdService, updateUserService, createUserService} from '@/services/facades/user-service-facade'// โ GOOD: Type imports from servicesimport type { User, CreateUserInput } from '@/services/types/domain'// โ BAD: Direct imports from service implementationimport { getUserByIdService } from '@/services/user-service'// โ BAD: Importing repository directly in componentsimport { getUserByIdRepository } from '@/db/repositories/user-repository'
Component Imports (Feature-Based Organization):
typescript
// โ GOOD: UI component importsimport { Button, Card, Input, Dialog } from '@/components/ui'// โ GOOD: Feature component importsimport { UserProfile } from '@/components/features/user'import { SubscriptionCard } from '@/components/features/subscription'import { CheckoutForm } from '@/components/features/checkout-stripe'// โ GOOD: Custom hooks from featuresimport { useUserData } from '@/components/features/user/hooks'// โ GOOD: Relative imports for local componentsimport { UserAvatar } from './UserAvatar'import { UserActions } from './UserActions'// โ BAD: Importing deeply nested componentsimport { UserAvatar } from '@/components/features/user/UserAvatar'// Use relative imports instead: './UserAvatar'
Database and Repository Imports (Server-Side Only):
typescript
// โ GOOD: Import from repositories (in services only)// user-service.tsimport { getUserByIdRepository, createUserRepository} from '@/db/repositories/user-repository'import type { UserModel, NewUserModel } from '@/db/models/user'// โ GOOD: Type imports from modelsimport type { Organization } from '@/db/models/organization'// โ BAD: Repository imports in components// Components should use service facades, not repositories// โ BAD: Direct database imports anywhereimport db from '@/db'
Validation and Helper Imports:
typescript
// โ GOOD: Validation schema importsimport { createUserValidationSchema } from '@/services/validation/user-validation'// โ GOOD: Helper function importsimport { formatDate } from '@/lib/helper/date-helper'import { calculateTotal } from '@/lib/helper/common-helper'// โ GOOD: Authorization helper imports (server-side)import { canEditUser } from '@/services/authorization/user-authorization'
Circular Dependency Prevention:
typescript
// โ GOOD: Layered imports prevent cycles// Components โ Services (via facades)// Services โ Database (via repositories)// Database โ Types (never upward)// types/domain.tsexport interface User { id: string name: string}// db/models/user.ts - Defines schemaimport type { User } from '@/services/types/domain'// services/user-service.ts - Business logicimport { getUserByIdRepository } from '@/db/repositories/user-repository'import type { User } from '@/services/types/domain'// services/facades/user-service-facade.ts - Public APIexport { getUserByIdService } from '../user-service'// components/features/user/UserProfile.tsx - UIimport { getUserByIdService } from '@/services/facades/user-service-facade'// โ BAD: Circular imports// user-service.ts imports from user-repository.ts// user-repository.ts imports from user-service.ts โ Never!
Export Consistency (ShipSaaS Patterns):
typescript
// โ GOOD: Named exports for utility functions// lib/helper/date-helper.tsexport function formatDate(date: Date): string { }export function parseDate(dateString: string): Date { }export function isDateInPast(date: Date): boolean { }// โ GOOD: Default export for main React component// components/features/user/UserProfile.tsxexport default function UserProfile() { }// โ GOOD: Named exports for sub-componentsexport function UserCard() { }export function UserList() { }// โ GOOD: Service function exports// services/user-service.tsexport const getUserByIdService = async (id: string): Promise<User | null> => { }export const createUserService = async (data: CreateUserInput): Promise<User> => { }export const updateUserService = async (id: string, data: UserUpdate): Promise<User> => { }// โ GOOD: Facade re-exports for public API// services/facades/user-service-facade.tsexport { getUserByIdService, createUserService, updateUserService, deleteUserService} from '../user-service'// โ GOOD: Type exportsexport type { User, CreateUserInput, UserUpdate } from '../types/domain'// โ GOOD: Barrel exports for feature components// components/features/user/index.tsexport { default as UserProfile } from './UserProfile'export { UserCard } from './UserCard'export { UserList } from './UserList'export { useUserData } from './hooks'// โ GOOD: Validation schema exports// services/validation/user-validation.tsexport const createUserValidationSchema = z.object({ /* ... */ })export const updateUserValidationSchema = z.object({ /* ... */ })
Facade Pattern (Service Public API):
typescript
// services/facades/user-service-facade.ts// This is the ONLY way external code accesses user service functionsexport { getUserByIdService, createUserService, updateUserService, deleteUserService, listUsersService} from '../user-service'export type { User, CreateUserInput, UserUpdate } from '../types/domain'// โ BAD: Direct imports from service// Instead of: import { getUserByIdService } from '@/services/user-service'// Use: import { getUserByIdService } from '@/services/facades/user-service-facade'
Component & Feature Exports:
typescript
// components/ui/Button.tsx - Single componentexport function Button({ children, ...props }: ButtonProps) { }// Use: import { Button } from '@/components/ui'// components/features/user/index.ts - Feature barrel exportexport { default as UserProfile } from './UserProfile'export { UserCard } from './UserCard'export { UserList } from './UserList'export * from './types'export * from './hooks'// Use: import { UserProfile, UserCard } from '@/components/features/user'// components/features/user/UserProfile.tsx - Main feature componentexport default function UserProfile({ userId }: UserProfileProps) { }
Code Organization Patterns
Consistent patterns for organizing code within files and directories:
Service File Organization (ShipSaaS Pattern):
typescript
// src/services/user-service.ts// 1. Imports (organized by groups)import { eq } from 'drizzle-orm'import type { User, CreateUserInput, UserUpdate } from './types/domain'import { createUserValidationSchema } from './validation/user-validation'import { canEditUser, canDeleteUser } from './authorization/user-authorization'import { getUserByIdRepository, createUserRepository } from '@/db/repositories/user-repository'// 2. Main service functions (public)export const getUserByIdService = async (id: string): Promise<User | null> => { const user = await getUserByIdRepository(id) return user ?? null}export const createUserService = async (data: CreateUserInput): Promise<User> => { const validated = createUserValidationSchema.parse(data) return await createUserRepository(validated)}export const updateUserService = async ( id: string, data: UserUpdate, userId: string): Promise<User> => { // Authorization check if (!canEditUser(userId, id)) { throw new AuthorizationError('Cannot edit this user') } // Validation and update return await updateUserRepository(id, data)}export const deleteUserService = async (id: string, userId: string): Promise<void> => { if (!canDeleteUser(userId, id)) { throw new AuthorizationError('Cannot delete this user') } await deleteUserRepository(id)}
Service Facade Organization:
typescript
// src/services/facades/user-service-facade.ts// Public API - only exports from this file should be imported externallyexport { getUserByIdService, createUserService, updateUserService, deleteUserService, listUsersService} from '../user-service'export type { User, CreateUserInput, UserUpdate } from '../types/domain'
Component File Organization (ShipSaaS Pattern):
typescript
// src/components/features/user/UserProfile.tsx'use client'// 1. Imports (organized)import { useState } from 'react'import { Button, Card } from '@/components/ui'import { useUserData } from './hooks'import type { User } from '@/services/types/domain'// 2. Types and interfaces (local to component)interface UserProfileProps { userId: string onEdit?: () => void}// 3. Component implementationexport default function UserProfile({ userId, onEdit }: UserProfileProps) { const [isLoading, setIsLoading] = useState(false) const { user, error } = useUserData(userId) if (isLoading) return <div>Loading...</div> if (error) return <div>Error loading user</div> if (!user) return <div>User not found</div> return ( <Card> <h1>{user.name}</h1> <p>Email: {user.email}</p> {onEdit && <Button onClick={onEdit}>Edit Profile</Button>} </Card> )}
ShipSaaS structure mastered! Your codebase follows clean, layered patterns that make development fast and maintainable. The monolithic structure with clear separation of concerns allows you to scale features efficiently while keeping the code organized and type-safe.