开发者指南
项目结构
Better SaaS 遵循基于 Next.js 15 App Router 的良好组织的项目结构,具有清晰的关注点分离和模块化架构。
概述
项目结构支持:
- 可扩展架构: 模块化组件和清晰分离
- 类型安全: 整个代码库使用 TypeScript
- 国际化: 使用 next-intl 的多语言支持
- 现代工具: 最新的 Next.js 功能和最佳实践
- 测试: 多级别的全面测试设置
根目录结构
better-saas/
├── .env.example # 环境变量模板
├── .gitignore # Git 忽略规则
├── biome.jsonc # 代码格式化和检查配置
├── components.json # Shadcn/ui 组件配置
├── drizzle.config.ts # 数据库 ORM 配置
├── jest.config.js # Jest 测试配置
├── next.config.ts # Next.js 配置
├── package.json # 依赖和脚本
├── playwright.config.ts # E2E 测试配置
├── postcss.config.js # PostCSS 配置
├── README.md # 项目文档
├── source.config.ts # Fumadocs 配置
├── tailwind.config.ts # Tailwind CSS 配置
├── tsconfig.json # TypeScript 配置
├── docs/ # 文档文件
├── drizzle/ # 数据库迁移
├── public/ # 静态资源
├── scripts/ # 构建和工具脚本
├── src/ # 源代码
└── tests/ # 测试文件
源代码结构 (src/
)
应用目录 (src/app/
)
遵循 Next.js 15 App Router 约定:
src/app/
├── [locale]/ # 国际化路由
│ ├── (home)/ # 首页组
│ │ ├── blocks/ # 块组件展示
│ │ ├── blog/ # 博客页面
│ │ ├── layout.tsx # 首页布局
│ │ └── page.tsx # 首页
│ ├── (protected)/ # 受保护路由组
│ │ ├── dashboard/ # 仪表板页面
│ │ └── settings/ # 设置页面
│ ├── docs/ # 文档页面
│ ├── login/ # 认证页面
│ ├── signup/
│ ├── layout.tsx # 特定语言布局
│ └── page.tsx # 语言根页面
├── api/ # API 路由
│ ├── auth/ # 认证端点
│ └── webhooks/ # Webhook 处理器
├── layout.tsx # 根布局
└── not-found.tsx # 404 页面
组件目录 (src/components/
)
按功能和可重用性组织:
src/components/
├── ui/ # 基础 UI 组件 (shadcn/ui)
│ ├── button.tsx
│ ├── card.tsx
│ ├── input.tsx
│ └── ...
├── blocks/ # 页面块和部分
│ ├── hero/
│ ├── features/
│ ├── pricing/
│ ├── navbar/
│ └── footer/
├── auth/ # 认证组件
│ ├── login-form.tsx
│ ├── signup-form.tsx
│ └── permission-provider.tsx
├── dashboard/ # 仪表板组件
│ ├── dashboard-content.tsx
│ ├── dashboard-header.tsx
│ └── protected-layout-client.tsx
├── file-manager/ # 文件管理组件
│ ├── file-upload.tsx
│ ├── file-grid.tsx
│ └── file-table.tsx
├── payment/ # 支付组件
│ ├── subscription-card.tsx
│ └── checkout-form.tsx
├── billing/ # 账单组件
│ └── billing-page.tsx
├── settings/ # 设置组件
│ ├── profile-content.tsx
│ └── security-content.tsx
├── providers/ # 上下文提供者
│ ├── auth-provider.tsx
│ └── theme-provider.tsx
└── widget/ # 可重用小部件
├── language-switcher.tsx
├── theme-toggle.tsx
└── user-avatar-menu.tsx
配置目录 (src/config/
)
集中配置管理:
src/config/
├── app.config.ts # 应用程序配置
├── features.config.ts # 功能标志和设置
├── i18n.config.ts # 国际化配置
├── navbar.config.ts # 导航配置
├── payment.config.ts # 支付计划和定价
├── theme.config.ts # 主题配置
└── index.ts # 配置导出
库目录 (src/lib/
)
工具函数和服务:
src/lib/
├── auth/ # 认证工具
│ ├── auth.ts # 服务器端认证配置
│ ├── auth-client.ts # 客户端认证
│ └── permissions.ts # 权限系统
├── fumadocs/ # 文档系统
│ ├── blog.ts
│ └── docs.ts
├── logger/ # 日志工具
│ ├── logger.ts
│ └── logger-utils.ts
├── blocks-registry.ts # 组件注册表
├── config-validation.ts # 配置验证
├── file-service.ts # 文件管理服务
├── r2-client.ts # 云存储客户端
└── utils.ts # 通用工具
服务器目录 (src/server/
)
服务器端代码和数据库操作:
src/server/
├── actions/ # 服务器操作
│ ├── auth-actions.ts
│ ├── file-actions.ts
│ ├── user-actions.ts
│ ├── payment/
│ │ ├── create-subscription.ts
│ │ ├── cancel-subscription.ts
│ │ └── get-billing-info.ts
│ └── error-messages.ts
├── db/ # 数据库层
│ ├── index.ts # 数据库连接
│ ├── schema.ts # 数据库架构
│ ├── types.ts # 数据库类型
│ ├── repositories/ # 数据访问层
│ │ ├── base-repository.ts
│ │ ├── file-repository.ts
│ │ └── payment-repository.ts
│ └── services/ # 业务逻辑层
│ └── index.ts
└── middleware/ # 服务器中间件
国际化 (src/i18n/
)
多语言支持:
src/i18n/
├── messages/ # 翻译文件
│ ├── en.json # 英文翻译
│ └── zh.json # 中文翻译
├── navigation.ts # 本地化导航
├── request.ts # 请求配置
└── routing.ts # 路由配置
钩子目录 (src/hooks/
)
自定义 React 钩子:
src/hooks/
├── use-config.ts # 配置钩子
├── use-debounce.ts # 防抖钩子
├── use-files.ts # 文件管理钩子
├── use-login.ts # 认证钩子
├── use-navbar.ts # 导航钩子
├── use-profile.ts # 个人资料钩子
└── use-toast-messages.ts # 消息通知钩子
类型目录 (src/types/
)
TypeScript 类型定义:
src/types/
├── index.d.ts # 全局类型定义
├── blocks.ts # 块组件类型
├── login.ts # 认证类型
├── navbar.ts # 导航类型
├── profile.ts # 个人资料类型
└── stripe-extended.ts # 扩展的 Stripe 类型
内容目录 (src/content/
)
静态内容和文档:
src/content/
├── blog/ # 博客文章
│ ├── en/ # 英文博客文章
│ └── zh/ # 中文博客文章
└── docs/ # 文档
├── en/ # 英文文档
└── zh/ # 中文文档
测试结构 (tests/
)
全面的测试设置:
tests/
├── __mocks__/ # 模拟文件
├── e2e/ # 端到端测试
│ ├── auth/
│ ├── dashboard/
│ ├── payment/
│ └── fixtures/
├── integration/ # 集成测试
│ ├── api/
│ ├── database/
│ └── services/
├── unit/ # 单元测试
│ ├── components/
│ ├── hooks/
│ ├── lib/
│ └── server/
├── setup/ # 测试设置文件
└── utils/ # 测试工具
文件命名约定
组件
- React 组件: PascalCase (例如:
UserProfile.tsx
) - 组件文件: kebab-case (例如:
user-profile.tsx
) - 钩子文件: kebab-case 带
use-
前缀 (例如:use-user-profile.ts
)
API 路由
- 路由文件: 目录结构中的
route.ts
- 中间件:
middleware.ts
- 服务器操作: kebab-case 带
-actions
后缀
配置
- 配置文件: kebab-case 带
.config.ts
后缀 - 环境变量:
.env.local
,.env.example
数据库
- 架构文件:
schema.ts
- 迁移文件: 时间戳前缀 (例如:
0001_initial.sql
) - 仓库文件: kebab-case 带
-repository
后缀
导入/导出约定
桶导出
使用索引文件进行清晰的导入:
// src/components/ui/index.ts
export { Button } from './button'
export { Card } from './card'
export { Input } from './input'
// 使用
import { Button, Card, Input } from '@/components/ui'
路径映射
在 tsconfig.json
中配置:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@/components/*": ["./src/components/*"],
"@/lib/*": ["./src/lib/*"],
"@/hooks/*": ["./src/hooks/*"],
"@/types/*": ["./src/types/*"],
"@/config/*": ["./src/config/*"]
}
}
}
导入顺序
遵循以下导入顺序:
// 1. React 和 Next.js 导入
import React from 'react'
import { NextRequest, NextResponse } from 'next/server'
// 2. 第三方库
import { z } from 'zod'
import { clsx } from 'clsx'
// 3. 内部导入(绝对路径)
import { Button } from '@/components/ui/button'
import { useAuth } from '@/hooks/use-auth'
import { db } from '@/server/db'
// 4. 相对导入
import './styles.css'
代码组织原则
单一职责
每个文件应该有一个单一、明确定义的目的:
// 好:专注于用户认证
// src/lib/auth/auth-client.ts
export const authClient = createAuthClient({...})
// 好:专注于文件操作
// src/lib/file-service.ts
export class FileService {
async uploadFile() {...}
async deleteFile() {...}
}
依赖方向
依赖应该向内流动:
UI 组件 → 钩子 → 服务 → 数据库
基于功能的组织
将相关功能组合在一起:
src/features/
├── auth/
│ ├── components/
│ ├── hooks/
│ ├── services/
│ └── types/
├── files/
│ ├── components/
│ ├── hooks/
│ ├── services/
│ └── types/
└── billing/
├── components/
├── hooks/
├── services/
└── types/
环境配置
环境文件
.env.local
- 本地开发(不提交).env.example
- 环境变量模板.env.production
- 生产环境变量
环境变量结构
# 数据库
DATABASE_URL=postgresql://...
# 认证
BETTER_AUTH_SECRET=...
BETTER_AUTH_URL=...
# OAuth 提供商
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
# 支付
STRIPE_SECRET_KEY=...
STRIPE_PUBLISHABLE_KEY=...
# 文件存储
CLOUDFLARE_R2_ACCESS_KEY_ID=...
CLOUDFLARE_R2_SECRET_ACCESS_KEY=...
# 外部服务
RESEND_API_KEY=...
构建和开发脚本
Package.json 脚本
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "biome check .",
"lint:fix": "biome check . --apply",
"type-check": "tsc --noEmit",
"test": "jest",
"test:watch": "jest --watch",
"test:integration": "jest --config=jest.integration.config.js",
"test:e2e": "playwright test",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio"
}
}
性能考虑
代码分割
- 对大型组件使用动态导入
- 实现基于路由的代码分割
- 延迟加载非关键功能
包分析
# 分析包大小
npm run build -- --analyze
# 检查包组成
npx @next/bundle-analyzer
树摇
- 使用 ES 模块以获得更好的树摇效果
- 避免导入整个库
- 谨慎使用桶导出
安全考虑
文件组织
- 将敏感配置保存在环境变量中
- 清晰地分离客户端和服务器代码
- 使用 TypeScript 确保类型安全
API 安全
- 使用 Zod 模式验证所有输入
- 实现适当的认证检查
- 对表单使用 CSRF 保护
最佳实践
组件设计
- 保持组件小而专注
- 使用组合而不是继承
- 实现适当的属性验证
- 遵循无障碍指南
状态管理
- 对本地状态使用 React 钩子
- 实现适当的错误边界
- 使用服务器状态进行数据获取
- 最小化全局状态
性能
- 实现适当的加载状态
- 对昂贵的组件使用 React.memo
- 优化图像和资源
- 实现适当的缓存策略