Skip to content
On this page

工程设计范式

  • 工程设计范式有哪些种类

Rails Style / Domain Style

  • 相信很多有一定经验的开发者都会遇到选择这种问题
    • 一个看似分层良好的古老工程,随着业务发展却越来越臃肿和低效
    • 一个新的项目,因为这种隐形问题上的纠结,而耽误了自己时间和精力

工程范式分类

🍅 Rails Style

js
// egg应用典型结构
|── app
|   ├── config
|   ├── controller
|   ├── extend
|   ├── public
|   ├── router.ts
|   ├── service
|   └── view
├── app.ts
├── agent.ts
├── config
├── logs
├── test
├── typings
├── README.md
├── package.json
└── yarn.lock
js
// Rails Style TodoList
root
|── reducers
|   ├── todoReducer.js
|   └── fileterReducer.js
├── actions
|   ├── todoActions.js
|   └── filterAction.js
├── components
|   ├── todoList.jsx
|   ├── todoItem.jsx
|   └── filter.jsx
├── containers
|   ├── todoListContainer.jsx
|   ├── todoItemContainer.jsx
|   └── filterContainer.jsx
├── App.jsx
└── index.js
  • Rails Style 的特点

    • 专注于纵向的“层”的划分
    • 同一类文件放置在同一目录下
  • 优势:

    • 便于合并导出
    • 便于进行“层”的扩展
  • 不足:

    • 依赖关系难以直接地分析
    • 对功能的修改会涉及到大量的目录切换
    • 难以水平拆分

🍅 Domain style

js
// Aant Design
├── ...
├── components
|   ├── util
        └── ...
|   ├── alert
|   |   ├── demo
|   |   ├── index.en-US.msd
|   |   ├── index.tsx
|   |   ├── index.zh-CN.md
|   |   └── style
|   ├── anchor
|   |   ├── Anchor.tsx
|   |   ├── AnchorLink.tsx
|   |   ├── __tests__
|   |   ├── demo
|   |   ├── index.en-US.msd
|   |   ├── index.tsx
|   |   ├── index.zh-CN.md
|   |   └── style
└── ...
  • Domain style 的特点

    • 专注于横向的 “功能” 的划分
    • 同一个 feature 放置在同一目录下
  • 优势:

    • 便于水平拆分
    • 便于进行“功能”的扩展
  • 不足

    • 会产生大量的重复结构
    • 难以垂直拆分

如何选择工程范式

  • 单一功能的项目
    • 库、三方包:fs-extra、axios 等
    • 由于不存在水平拆分的必要性、故可以选择 Rails Style
    • 易于扩展
    • 减少重复代码
js
// Axios
root
|── dist
|   └── ...
├── examples
|   └── ...
├── lib
|   ├── adapters
|   |    └── ...
|   ├── cancel
|   |    └── ...
|   ├── core
|   |     └── ...
|   ├── defaults.js
|   └── axios.js
├── sandbox
|   └── ...
├── test
|   └── ...
├── package.json
└── webpack.config.js
  • 聚合功能型项目

    • 组件池、utils:ant-design、element-ui、lodash 等
    • 纵向分层少,极易横向扩展-故选择 Domain Style
    • 易于添加新 feature
    • 便于横向拆分
  • 业务工程项目

    • 即有大量的垂直分层,又有大量的 feature 聚合
    • Rails Style + Domain Style

🍅 Rails Style 对比 Domain Style

工程设计范式特点优势不足适用项目
Rails Style纵向“层”的划分,同一类文件放置在同一目录下便于合并导出,便于进行“层”的扩展依赖关系难以直观地分析,对功能的修改会涉及到大量的目录切换 ,难以水平拆分单一功能的项目
Domain Style横向“功能”的划分,同一 feature 放置在同一目录下便于水平拆分,便于进行“功能”的扩展会产生大量的重复结构,难以垂直拆分聚合功能型项目

业务工程项目一般需要 Rails Style 和 Domain Style 结合

multi-repo VS mono-repo

它们是什么?

这是两种代码风格仓库的管理风格

  • multi-repo:把每个项目都分别用 git 托管
  • mono-repo:统一用一个 git 仓库管理所有的项目

multi-repo

js
// multi-repo
root
|── project-a
|   ├── ...
|   └── .git
├── project-b
|   ├── ...
|   └── git
├── project-c
|   ├── ...
|   └── .git
├── project-d
|   ├── ...
|   └── .git
...

从上面可以看出多个项目对应多个仓库,大多数工程,其实都是以 multi-repo 方式管理的

  • 优势:

    可以让各项目团队根据需要定制更适合自己的 workflow

  • 不足:

    1. 难以对所有项目统一进行操作(git checkout / npm publish / npm run build)
    2. 难以追踪依赖关系(a->b->c)

mono-repo

js
// mono-repo

├── .git
├── lerna.json
├── package.json
├── packages
    ├── project-a
    |    ├── README.md
    |    ├── __tests__
    |    ├── lib
    |    └── package.json
    ├── project-b
    |    ├── README.md
    |    ├── __tests__
    |    ├── lib
    |    └── package.json
    ├── project-c
        ├── README.md
        ├── __tests__
        ├── lib
        └── package.json

广泛应用于一些知名开源项目和硅谷巨头(React/Angular/Vuetify/Google)

  • 优势

    1. 方便统一地操作各个项目(git checkout / npm publish / npm run build)
    2. 利用工具,可以方便地追踪项目间的依赖关系
  • 不足:

    1. 代码库随着业务发展会非常巨大
    2. 失去部分的灵活性(workflow 必须统一)
    3. 强依赖于 mono-repo 的管理工具

multi-repo 的管理

  • git submodule
bash
# 初始化git submodules仓库
git submodule init
# 添加一个submodule
git submodule add https://github.com
# 更新所有的submodule
git submodule update
# 查submodule status
git submodule status
# foreach 用于在每个submodule中执行命令
git submodule foreach "git checkout -b featureA"
  • git 提供的一种管理子仓库的方案
  • 可以批量管理多个 git repo
  • 本质上是一个父 repo 维护了一份各个子 repo 的清单
  • 坑还是不少的:git Submodule 的坑
  • 替代方案:git subtree

mono-repo 的管理-lerna

  • 为 js 生态下的 mono-repo 管理提供一站式的解决方案
  • babel/create-react-app/jest/react-router/umi/nestjs

解决方案:

mono-repo 下的依赖管理、版本管理、开发提效、工作流

目录结构

  • 整体作为一个 Git 仓库,也是个 npm 包(私有)
  • lerna.json 是整个 mono-repo 的配置文件
  • 每个真正的项目,平铺在 packages/中
  • 整个项目可以统一管理所有依赖(也可以分别管理)
js
// lerna
├── .git
├── lerna.json
├── package.json
├── packages
    ├── project-a
    |    ├── README.md
    |    ├── __tests__
    |    ├── lib
    |    └── package.json
    ├── project-b
    |    ├── README.md
    |    ├── __tests__
    |    ├── lib
    |    └── package.json
    ├── project-c
        ├── README.md
        ├── __tests__
        ├── lib
        └── package.json

用 lerna 管理项目实战 重要

bash
npm i lerna -g
lerna init
  1. 初始化的目录结构
json
- packages
  - lerna.json
  - package.json
bash
# 创建项目1
lerna create pac-1
# 创建项目2
lerna create pac-2
# 创建项目3
lerna create pac-3

目录中 lerna.json

json
{
  // 配置$schema可以在vscode中,鼠标滑倒每个配置项时候,可以看每个配置项的介绍
  // 可以在https://www.schemastore.org/json/中网站查看知名项目的描述文件
  "$schema": "http://json.schemastore.org/lerna",
  "packages": ["packages/*"],
  "version": "independent" // 可以给各个项目发不同的版本
}

TIP

接下来我们为 pac-1、 pac-2、pac-3 添加依赖关系,我们制造这样一种依赖,pac-1 模块被 pac-2 依赖;pac-2 模块被 pac-3 模块依赖, 这时候就形成了 pac-1、 pac-2、pac-3 间接依赖关系。

  1. 添加项目依赖
bash
# 制造依赖关系,对于内部项目的依赖,lerna会以软链接的形式,给它们相互软链接起来
lerna add pac-1 packages/pac-2
lerna add pac-2 packages/pac-3

如果我们给项目添加外部依赖,可能需要给每个项目 node_moudles 都添加,同一个依赖会安装很多次,这显然不合理,为了解决这个问题,lerna 可以把依赖添加到项目根目录;lerna clean去清除每个项目中 node_modules 相同的依赖;用 lerna bootstrap --hoist重新安装依赖,

  1. 发版

需要注意是的 lerna.json 配置为"version": "independent"时可以为每个包独立发不同的版本,如果"version":'0.0.0'是发同样的版本

bash
leran publish
  1. 其它

可以用lerna -h查看全部指令

bash
lerna  exec [cmd] [args...] # 执行每一个package.json你想指行的命令
lerna run <script> # 执行package.josn 中script配置的命令
lerna diff [pkgName] # 对比 文件变化
lerna changed # 查看文件变化

lerna.json 配置

json
{
  "version": "0.0.0",
  "npmClient": "npm",
  "npmClientArgs": ["--pure-lockfile"],
  "command": {
    "publish": {
      "ignoreChanges": ["ignored-file", "*.md"],
      "message": "chore(replease):publish",
      "registry": "https://npm.pkg.github.com"
    },
    "bootstrap": {
      "ignore": "component-*",
      "npmClientArgs": ["--no-package-lock"]
    }
  },
  "packages": ["packages/*"]
}
  • 其实只有几个主要字段:version、npm*、command、packages

  • command 配置-json schema 查看

    • 配置 lerna 子命令的默认项目
  • version: 决定 fixed mode / independent mode

  • packages:项目路径

  • npm*:解耦包管理工具

一些注意项

  • mono-repo 不可嵌套
  • mono-repo 的主仓库必须是私有的(private:true)
  • 如果你对仓库的私密性要求非常高,甚至可以不用 npm 而使用 git 应用依赖
  • 任何的 json 配置记不住,都可以用 json schema

lerna 外的选择-nx

  • Angular 出品
  • 框架强相关:Angular、React
  • 支持插件机制
  • 远超 mono-repo 管理的强大功能

扩展资料

Git submodule 的坑

git subtree

npm v7 Series - Introduction

NX