monorepo-structure
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMonorepo Structure
Monorepo 结构
One repo, multiple packages, shared types, parallel builds.
单仓库多包结构,支持共享类型、并行构建。
When to Use This Skill
何时采用该方案
- Sharing code between frontend and backend
- Multiple apps need common types/utilities
- Want atomic commits across packages
- Tired of version hell with separate repos
- Need parallel builds with caching
- 前端与后端之间共享代码
- 多个应用需要通用类型/工具函数
- 希望跨包进行原子提交
- 受够了多仓库的版本混乱问题
- 需要带缓存的并行构建能力
Core Concepts
核心概念
- Workspaces - pnpm manages multiple packages in one repo
- Turborepo - Orchestrates builds with caching and parallelization
- Shared types - Single source of truth for TypeScript types
- Build order - Dependencies build before dependents
- 工作区(Workspaces) - pnpm 管理单仓库中的多个包
- Turborepo - 负责编排构建流程,支持缓存与并行化
- 共享类型 - TypeScript 类型的单一可信来源
- 构建顺序 - 依赖包先于依赖它的模块构建
Project Structure
项目结构
project-root/
├── apps/
│ ├── web/ # Next.js frontend
│ │ ├── app/
│ │ ├── components/
│ │ └── package.json
│ ├── api/ # Backend API
│ │ ├── src/
│ │ └── package.json
│ └── worker/ # Background worker
│ ├── src/
│ └── package.json
│
├── packages/
│ ├── types/ # Shared TypeScript types
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── user.ts
│ │ │ └── schemas.ts
│ │ └── package.json
│ ├── utils/ # Shared utilities
│ │ └── package.json
│ └── config/ # Shared configs (eslint, tsconfig)
│ └── package.json
│
├── package.json # Root package.json
├── pnpm-workspace.yaml
├── turbo.json
└── tsconfig.base.jsonproject-root/
├── apps/
│ ├── web/ # Next.js 前端应用
│ │ ├── app/
│ │ ├── components/
│ │ └── package.json
│ ├── api/ # 后端API服务
│ │ ├── src/
│ │ └── package.json
│ └── worker/ # 后台工作进程
│ ├── src/
│ └── package.json
│
├── packages/
│ ├── types/ # 共享TypeScript类型定义
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── user.ts
│ │ │ └── schemas.ts
│ │ └── package.json
│ ├── utils/ # 共享工具函数包
│ │ └── package.json
│ └── config/ # 共享配置(eslint、tsconfig等)
│ └── package.json
│
├── package.json # 根目录package.json
├── pnpm-workspace.yaml
├── turbo.json
└── tsconfig.base.jsonTypeScript Implementation
TypeScript 实现细节
pnpm-workspace.yaml
pnpm-workspace.yaml
yaml
packages:
- "apps/*"
- "packages/*"yaml
packages:
- "apps/*"
- "packages/*"Root package.json
根目录 package.json
json
{
"name": "my-saas",
"private": true,
"scripts": {
"dev": "turbo dev",
"build": "turbo build",
"test": "turbo test",
"lint": "turbo lint",
"typecheck": "turbo typecheck",
"clean": "turbo clean && rm -rf node_modules"
},
"devDependencies": {
"turbo": "^2.0.0",
"typescript": "^5.4.0"
},
"packageManager": "pnpm@9.0.0"
}json
{
"name": "my-saas",
"private": true,
"scripts": {
"dev": "turbo dev",
"build": "turbo build",
"test": "turbo test",
"lint": "turbo lint",
"typecheck": "turbo typecheck",
"clean": "turbo clean && rm -rf node_modules"
},
"devDependencies": {
"turbo": "^2.0.0",
"typescript": "^5.4.0"
},
"packageManager": "pnpm@9.0.0"
}turbo.json
turbo.json
json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"test": {
"dependsOn": ["^build"]
},
"typecheck": {
"dependsOn": ["^build"]
},
"lint": {
"dependsOn": ["^build"]
},
"clean": {
"cache": false
}
}
}json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"test": {
"dependsOn": ["^build"]
},
"typecheck": {
"dependsOn": ["^build"]
},
"lint": {
"dependsOn": ["^build"]
},
"clean": {
"cache": false
}
}
}tsconfig.base.json
tsconfig.base.json
json
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true
}
}json
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true
}
}Shared Types Package
共享类型包
json
// packages/types/package.json
{
"name": "@myapp/types",
"version": "0.0.1",
"private": true,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"typescript": "^5.4.0"
},
"dependencies": {
"zod": "^3.23.0"
}
}json
// packages/types/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}typescript
// packages/types/src/index.ts
export * from './user';
export * from './schemas';typescript
// packages/types/src/user.ts
export interface User {
id: string;
email: string;
name: string;
role: 'admin' | 'user' | 'guest';
createdAt: Date;
}
export interface CreateUserInput {
email: string;
name: string;
role?: 'admin' | 'user' | 'guest';
}typescript
// packages/types/src/schemas.ts
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1),
role: z.enum(['admin', 'user', 'guest']),
createdAt: z.coerce.date(),
});
export const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
role: z.enum(['admin', 'user', 'guest']).default('user'),
});json
// packages/types/package.json
{
"name": "@myapp/types",
"version": "0.0.1",
"private": true,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"typescript": "^5.4.0"
},
"dependencies": {
"zod": "^3.23.0"
}
}json
// packages/types/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}typescript
// packages/types/src/index.ts
export * from './user';
export * from './schemas';typescript
// packages/types/src/user.ts
export interface User {
id: string;
email: string;
name: string;
role: 'admin' | 'user' | 'guest';
createdAt: Date;
}
export interface CreateUserInput {
email: string;
name: string;
role?: 'admin' | 'user' | 'guest';
}typescript
// packages/types/src/schemas.ts
import { z } from 'zod';
export const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(1),
role: z.enum(['admin', 'user', 'guest']),
createdAt: z.coerce.date(),
});
export const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
role: z.enum(['admin', 'user', 'guest']).default('user'),
});App Package Using Shared Types
引用共享类型的应用包
json
// apps/web/package.json
{
"name": "@myapp/web",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@myapp/types": "workspace:*",
"next": "^14.0.0",
"react": "^18.0.0"
}
}typescript
// apps/web/app/api/users/route.ts
import type { User, CreateUserInput } from '@myapp/types';
import { CreateUserSchema } from '@myapp/types';
export async function POST(request: Request) {
const body = await request.json();
// Validate with shared schema
const input = CreateUserSchema.parse(body);
// Create user...
const user: User = await createUser(input);
return Response.json(user);
}json
// apps/web/package.json
{
"name": "@myapp/web",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@myapp/types": "workspace:*",
"next": "^14.0.0",
"react": "^18.0.0"
}
}typescript
// apps/web/app/api/users/route.ts
import type { User, CreateUserInput } from '@myapp/types';
import { CreateUserSchema } from '@myapp/types';
export async function POST(request: Request) {
const body = await request.json();
// 使用共享Schema验证
const input = CreateUserSchema.parse(body);
// 创建用户...
const user: User = await createUser(input);
return Response.json(user);
}Shared Utils Package
共享工具函数包
json
// packages/utils/package.json
{
"name": "@myapp/utils",
"version": "0.0.1",
"private": true,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
},
"devDependencies": {
"typescript": "^5.4.0"
}
}typescript
// packages/utils/src/index.ts
export function formatDate(date: Date): string {
return date.toISOString().split('T')[0];
}
export function slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '-');
}
export function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}json
// packages/utils/package.json
{
"name": "@myapp/utils",
"version": "0.0.1",
"private": true,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
},
"devDependencies": {
"typescript": "^5.4.0"
}
}typescript
// packages/utils/src/index.ts
export function formatDate(date: Date): string {
return date.toISOString().split('T')[0];
}
export function slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '-');
}
export function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}Common Commands
常用命令
bash
undefinedbash
undefinedInstall all dependencies
安装所有依赖
pnpm install
pnpm install
Run all dev servers in parallel
并行启动所有开发服务
pnpm dev
pnpm dev
Build everything (respects dependency order)
构建所有模块(遵循依赖顺序)
pnpm build
pnpm build
Run tests across all packages
跨所有包运行测试
pnpm test
pnpm test
Add dependency to specific package
为指定包添加依赖
pnpm add zod --filter @myapp/types
pnpm add zod --filter @myapp/types
Add dev dependency to root
为根目录添加开发依赖
pnpm add -D prettier -w
pnpm add -D prettier -w
Run command in specific package
在指定包中运行命令
pnpm --filter @myapp/web dev
pnpm --filter @myapp/web dev
Run command in all packages matching pattern
在所有匹配规则的包中运行命令
pnpm --filter "@myapp/*" build
undefinedpnpm --filter "@myapp/*" build
undefinedDependency Flow
依赖流向
packages/types (source of truth)
↓
packages/utils (may import types)
↓
apps/web, apps/api, apps/worker (import both)Turborepo handles build order via - packages always build before apps that depend on them.
dependsOn: ["^build"]packages/types(类型可信来源)
↓
packages/utils(可导入类型)
↓
apps/web, apps/api, apps/worker(同时导入类型与工具)Turborepo 通过 处理构建顺序 - 包总是先于依赖它的应用构建。
dependsOn: ["^build"].gitignore
.gitignore
gitignore
undefinedgitignore
undefinedDependencies
依赖目录
node_modules/
node_modules/
Build outputs
构建产物
dist/
.next/
.turbo/
dist/
.next/
.turbo/
Environment
环境变量文件
.env
.env.local
.env.*.local
.env
.env.local
.env.*.local
IDE
IDE 配置
.idea/
.vscode/
.idea/
.vscode/
OS
系统文件
.DS_Store
undefined.DS_Store
undefinedBest Practices
最佳实践
- Use - Always for internal dependencies
workspace:* - Types flow down - Shared types package is the source of truth
- One tsconfig.base - Extend from root, override only what's needed
- Atomic commits - Change types and consumers in same commit
- Cache builds - Turborepo caches unchanged packages
- 使用 - 内部依赖始终使用该标识
workspace:* - 类型向下传递 - 共享类型包是唯一的类型可信来源
- 单一基础TS配置 - 所有包继承根目录配置,仅覆盖必要项
- 原子提交 - 在同一提交中修改类型及其所有消费者
- 缓存构建结果 - Turborepo 会缓存未修改的包构建产物
Common Mistakes
常见错误
- Using instead of
^1.0.0for internal depsworkspace:* - Building packages individually instead of
turbo build - Circular dependencies between packages
- Not including in
dist/.gitignore - Forgetting in turbo.json
dependsOn: ["^build"]
- 内部依赖使用 而非
^1.0.0workspace:* - 单独构建包而非使用
turbo build - 包之间存在循环依赖
- 未将 添加到
dist/.gitignore - 在 turbo.json 中遗漏
dependsOn: ["^build"]
Related Skills
相关方案
- TypeScript Strict
- Environment Config
- TypeScript 严格模式
- 环境变量配置