1. 主题背景

单体应用架构 vs 多仓多模块管理 vs 单仓多模块管理 三种代码组织和维护的方式各有有缺点:

  • 单体应用架构(monolithic): 软件开发前期, 业务不复杂, 依赖较少, 使用分层方式组织代码, 简单易用, 维护效率较高
  • 多仓多模块管理(multirepo): 因为业务复杂和代码量增多, 单体架构难以有效组织代码, 编译时间过长, code review和代码维护也面临困难, 通常从单体架构拆分为微服务, 每个微服务是一个代码仓库, 独立升/编译/维护/上线
  • 单仓多模块管理(monorepo): 多仓库模式在仓库数量持续增加后面临公共资源重复引用, CI/CD等脚本重复分散到各个项目维护, 单仓多模块管理的组织方式减轻了重复项维护的痛点, 另外在主干开发的GitFlow模式中单仓库更简单, 无需在多仓库模式中分别创建和维护主干分支

参考文档:

2. 演练

nx命令行不需要额外, npm安装后默认自带这个子命令, 可以使用一下命令验证:

1
npx nx --version

演练主要使用nest插件作为脚手架工具

  • 创建工作区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
npx create-nx-workspace nx-tutorial --preset=nest --appName tutotial --nxCloud false
cd nx-tutorial/ && npm install -D @nrwl/nest

# or
yarn create nx-workspace nx-tutorial --preset=nest --appName tutotial --nxCloud false
cd nx-tutorial/ && yarn add -D @nrwl/nest

tree . -L 1
.
├── README.md
├── node_modules
├── nx.json
├── package-lock.json
├── package.json
├── apps
├── libs
├── tools
└── tsconfig.base.json

上面的apps和libs是存放自项目的位置

  • 创建演练项目
1
2
3
4
yarn nx generate @nrwl/nest:app main-api

yarn nx generate @nrwl/nest:lib auth --publishable --importPath=@org/auth --buildable

  • 编写测试代码

在main-api中调用auth lib中的一个方法

  • 运行和验证
1
2
3
4
5
6
7
8
9
10
11
12
# 手动执行
yarn nx build auth
yarn nx build main-api
# 一次性执行多个
yarn nx run-many --projects=auth,main-api --target=build
# 使用affect执行
yarn nx affected --target=build
# 启动服务
yarn nx serve main-api
# or
yarn nx affected --target=serve

**演练代码仓库: **nx-tutorial

3. Nx核心

  • 工作区 workspace
    • 工作区是monorepo组织多个项目框架, 这个概念和golang/yarn等技术中的工作区概念一致
    • 工作区是nx.json所在的文件夹, 而项目一般存放到 packages/apps/libs等目录下
    • 项目存放的目录名称可以在在nx.json中配置, 并不一定使用自动生成的目录
1
2
3
4
"workspaceLayout": {
"appsDir": "demos",
"libsDir": "packages"
},
  • 项目 project
    • 项目是组织单仓库多项目架构的基础概念, 项目会被放置在一个workspace中
  • 命令 target
    • 提供一种命令的定义, 可以定义在工作区(nx.json)和项目(project.json)级别
    • 优先使用项目级别的, 项目级别的target是和工作区的target做合并的
  • 执行任务
    • 任务定义位置有两处
      • package.json中的scripts
      • project.json中的targets
    • 执行任务的方式
      • nx [project] [target]
      • nx run [project][:target][:configuration] [_..]
      • nx run-many: 执行全部
    • 支持并行
    • 支持依赖执行: nx affected –targets=a,b,c,d
  • 缓存任务结果
    • 目的: 方式代码未变更却重复的执行任务, 减少执行次数, 增加执行效率
    • 执行任务缓存可配置(nx.json)
1
2
3
4
5
6
7
8
9
10
{
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "test"]
}
}
}
}
  • 使用缓存: 任务执行之前通过任务的input结合缓存判断代码是否发生变更, 如果未发生变更则从缓存中恢复output, 例如: 文件/终端输出
  • 缓存位置和文件: node_modules/.cache/nx/run.json
  • 清除缓存: nx reset
  • 生成器 generator
    • 对工作区和项目进行代码组织和修改的工具
  • 命令行 CLI
    • nx affected:graph: 可以开启一个本地website,在该界面上可以查看依赖关系和target
  • 自动更新依赖
1
2
3
4
5
6
# Step1: 检查和更新依赖, 修改package.json生成migrations.json
yarn nx migrate latest
# Step2: 执行更新依赖
yarn nx migrate --run-migrations
# Step3: 清除
rm migrations.json

为了方式项目引用到其他项目内部的的对象, 让依赖关系变得凌乱, 为此可以通过强制限定项目边界限定依赖关系仅通过项目引用.

  • 安装插件
1
yarn add npm i @nrwl/eslint-plugin-nx @nrwl/devkit -D
  • 设置tags, 可选
1
2
3
4
5
6
{
"tags": [
"scope:lib",
"scope:app"
]
}
  • 配置eslint检查规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
// ... more ESLint config here

// @nrwl/nx/enforce-module-boundaries should already exist within an "overrides" block using `"files": ["*.ts", "*.tsx", "*.js", "*.jsx",]`
"@nrwl/nx/enforce-module-boundaries": [
"error",
{
"allow": [],
// update depConstraints based on your tags
"depConstraints": [
{
"sourceTag": "scope:lib",
"onlyDependOnLibsWithTags": ["scope:lib"]
},
{
"sourceTag": "scope:app",
"onlyDependOnLibsWithTags": ["scope:app", "scope:lib"]
}
]
}
]

// ... more ESLint config here
}