为什么需要 Monorepo 结构
在微服务开发中,如果每个服务都作为独立项目来管理,会面临以下问题:
- 代码重复 -- 公共模块(如 DTO、工具函数)需要在多个项目中复制
- 开发不便 -- 需要同时打开多个项目窗口进行调试
- 部署复杂 -- 每个项目有独立的 CI/CD 配置
- 版本管理困难 -- 多个 Git 仓库之间的版本同步
NestJS 提供了 Monorepo(单体仓库)模式来组织微服务项目,将多个服务放在同一个仓库中管理。
创建 Monorepo 项目
使用 NestJS CLI 可以将现有项目转换为 Monorepo 结构:
# 将现有项目转换为 Monorepo,生成 client 子应用
nest generate app client --no-spec -d
bash
执行后,NestJS CLI 会自动完成以下操作:
- 将原有代码移动到
apps/目录下 - 创建新的
apps/client子应用 - 更新根目录的
nest-cli.json配置
转换后的目录结构
project-root/
├── apps/
│ ├── main/ # 主应用(微服务服务端)
│ │ ├── src/
│ │ │ ├── main.ts
│ │ │ ├── app.module.ts
│ │ │ └── math/
│ │ │ └── math.controller.ts
│ │ ├── tsconfig.app.json
│ │ └── test/
│ └── client/ # 客户端应用(微服务客户端)
│ ├── src/
│ │ ├── main.ts
│ │ ├── app.module.ts
│ │ └── app.service.ts
│ ├── tsconfig.app.json
│ └── test/
├── libs/ # 共享库
│ └── shared/
│ └── src/
├── nest-cli.json # NestJS CLI 配置
├── tsconfig.json
├── tsconfig.build.json
└── package.json
text
nest-cli.json 配置
转换后,nest-cli.json 会包含 Monorepo 的配置:
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"monorepo": true,
"root": "apps/main",
"compilerOptions": {
"webpack": true,
"tsConfigPath": "apps/main/tsconfig.app.json"
},
"projects": {
"main": {
"type": "application",
"root": "apps/main",
"entryFile": "main",
"sourceRoot": "apps/main/src",
"compilerOptions": {
"tsConfigPath": "apps/main/tsconfig.app.json"
}
},
"client": {
"type": "application",
"root": "apps/client",
"entryFile": "main",
"sourceRoot": "apps/client/src",
"compilerOptions": {
"tsConfigPath": "apps/client/tsconfig.app.json"
}
}
}
}
json
启动和管理子应用
查看默认项目
nest-cli.json 中的 root 字段指定了默认项目。执行 pnpm start:dev 时会启动默认项目。
启动特定子应用
# 方式 1:通过 npm scripts
# 在根 package.json 中添加:
{
"scripts": {
"start:main": "nest start main --watch",
"start:client": "nest start client --watch"
}
}
# 方式 2:直接使用 Nest CLI
npx nest start main --watch
npx nest start client --watch
bash
构建特定子应用
npx nest build main
npx nest build client
bash
共享库(libs)
Monorepo 的一个重要优势是可以在多个子应用之间共享代码。NestJS 提供了 libs 目录来存放共享库:
# 创建共享库
nest generate library shared
bash
libs/
└── shared/
├── src/
│ ├── shared.module.ts
│ ├── shared.service.ts
│ └── index.ts
└── tsconfig.lib.json
text
在子应用中使用共享库:
// apps/client/src/app.module.ts
import { SharedModule } from '@app/shared';
@Module({
imports: [SharedModule],
})
export class AppModule {}
typescript
共享库的路径别名在 tsconfig.json 中配置:
{
"compilerOptions": {
"paths": {
"@app/shared": ["libs/shared/src"],
"@app/shared/*": ["libs/shared/src/*"]
}
}
}
json
Monorepo 的优势总结
| 优势 | 说明 |
|---|---|
| 代码共享 | DTO、接口、工具函数等可以在子应用间共享 |
| 统一管理 | 所有服务的依赖版本、代码风格、CI/CD 统一管理 |
| 开发效率 | 一个 IDE 窗口管理所有服务,方便跨服务调试 |
| 原子提交 | 跨服务的修改可以在一个 commit 中完成 |
| 独立部署 | 每个子应用仍然可以独立构建和部署 |
↑