Make your projet as a monoRepo with PNPM
PNPM is a fast, disk space efficient package manager that helps you to manage your project dependencies.
MonoRepo is a method that manages multiple projects in a single repository, which helps to simplify the complexity of code sharing, version control, building and deployment, and provides better reusability and collaboration. Monorepo advocates an open, transparent and shared organizational culture. This method has been widely used by many large companies, such as Google, Facebook and Microsoft.
MonoRepo Evolution
- Phase 1: Single-repository monolith application. A Git repository maintains the project code. As the complexity of iterative business increases, the project code will become more and more numerous and complex. The efficiency of building a large amount of code will also decrease, eventually leading to a single monolith application. This code management method is called Monolith.
- Phase 2: Multi-repository and multi-module application. The project is then broken down into multiple business modules and managed in multiple Git repositories. Module decoupling reduces the complexity of the monolithic application. Each module can be independently coded, tested, and released. Code management is simplified and build efficiency is improved. This code management method is called MultiRepo.
- Phase 3: Single warehouse multi-module application. With the increase of business complexity, there are more and more module warehouses. Although MultiRepo is decoupled from the business, it increases the difficulty of project engineering management. As the module warehouse reaches a certain level, there will be several problems: cross-warehouse code is difficult to share; module dependency management scattered in a single warehouse is complicated (after the underlying module is upgraded, other upper-level dependencies need to be updated in time, otherwise there will be problems); and the build time is increased. Therefore, it has become a trend to integrate multiple projects into one warehouse, share project configuration, and quickly share module code. This code management method is called MonoRepo.
MonoRepo advantages and disadvantages
en.wikipedia.org/wiki/Monorepo has a good explanation with the advantages and disadvantages.
Scenes | MultiRepo | MonoRepo |
---|---|---|
Code Visibility | ✅ Code isolation, developers only need to focus on the warehouse they are responsible for ❌ Package management is divided according to their respective owners. When problems occur, they need to judge and solve them in the dependent packages. | ✅ Multiple related projects in one repository make it easy to see the change trend of the entire code base and better team collaboration. ❌ Increased risk of non-owners modifying the code |
Dependency Management | ❌ Multiple repositories have their own node_modules, which may lead to duplicate installation of dependencies and occupy a lot of disk memory. | ✅ Multiple project codes are in one repository, and the same version of dependencies are promoted to the top level and installed only once, saving disk memory. |
Code permissions | ✅ Each project has its own separate repository, so the code will not be modified by mistake, and problems with a single project will not affect other projects. | ❌ Multiple project codes are in one repository, and there is no project-level permission control. If a problem occurs in one project, all projects may be affected. |
Development Iteration | ✅ Small warehouse size, clear module division, strong maintainability. ❌ Switching between multiple warehouses (editor and command line) is very inefficient if there are many projects. When multiple warehouses have dependencies, manual npm linkoperation is required, which is cumbersome. ❌ Dependency management is inconvenient. Multiple dependencies may have different versions in multiple warehouses, and repeated installation will cause conflicts between dependencies of different projects when npm linking. | ✅ Multiple projects are in one warehouse, you can see the whole picture of related projects, and coding is very convenient. ✅ High code reuse, convenient for code refactoring. ❌ Multiple projects are in one warehouse, the code volume is several GB, and git cloneit takes a long time. ✅ Convenient dependency debugging. In the scenario of dependency package iteration, with the help of tools, the latest version of dependency is directly used with automatic npm link, which simplifies the operation process. |
Project Configuration | ❌ Each project builds, packages, and verifies its own code. Inconsistencies can lead to code or build differences. | ✅ Multiple projects are in one repository, the project configuration is consistent, and the code quality standards and style are also easy to be consistent. |
Build and deploy | ❌ There are dependencies between multiple projects. When deploying, you need to manually go to different repositories to modify the version and deploy them in order, which is cumbersome and inefficient. | ✅ The buildability Monorepo tool can configure the build priority of dependent projects and complete all deployments with one command. |
Monorepo with PNPM workspace
PNPM only need
NOTE:
pnpm-workspace.yaml
on your root repository alongside packages.json
, with array path to directory in packages
property. 1
2
3
4
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
1
2
3
4
5
6
# Directory structure
project-example/
├─ apps/
├─ packages/
├─ package.json
└─ pnpm-workspace.yaml
NOTE:
apps/*
and packages/*
is very popular monorepo structure, where apps/*
is projects directory and packages/*
is reusable code. All you need just create directory inside packages
list, change directory to it, and do pnpm init
. 1
2
3
4
5
6
7
8
9
#Commands
mkdir -p packages/math-lib
cd packages/math-lib
pnpm init
cd ../..
mkdir -p apps/calculator
cd apps/calculator
pnpm init
cd ../..
List all workspaces
1
2
#List all workspaces in JSON format.
pnpm m ls --depth -1 --json
Command specific workspace
1
2
3
4
5
6
7
8
9
# Format
pnpm --filter <workspace> <command>
pnpm -F <workspace> <command>
# Example
pnpm --filter math-lib add lodash
pnpm --filter math-lib add -D typescript @types/lodash
pnpm --filter calculator run test
# Support glob pattern
pnpm --filter pkg* run test
NOTE: PNPM use
name
property in package.json
to filter workspace, so make sure ever name in package.json
is unique.Add local dependency
1
2
# Example
pnpm --filter calculator add math-lib --workspace
Project example
1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir -p monorepo-example
cd monorepo-example
pnpm init
mkdir -p packages/math-lib
cd packages/math-lib
pnpm init
cd ../..
mkdir -p apps/calculator
cd apps/calculator
pnpm init
cd ../..
touch packages/math-lib/index.js
touch apps/calculator/index.js
1
2
3
4
5
6
7
8
//packages/math-lib/index.js
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
module.exports = {
add,
subtract,
};
1
2
3
4
5
//apps/calculator/index.js
const { add, subtract } = require("math-lib");
console.log('9 + 10 = ', add(9, 10));
console.log('26 - 9 = ', subtract(26, 9));
1
pnpm --filter calculator add math-lib --workspace
1
2
3
4
5
6
#apps/calculator/package.json
{
"scripts": {
"start": "node index.js"
}
}
1
2
3
4
5
6
#package.json
{
"scripts": {
"calculator:start": "pnpm -F calculator run start"
}
}
1
pnpm run calculator:start
How i use this
I worked with good craftsmen on the migration of a set of services called Webhooks that shared the same dependencies from a multirepo architecture to a monorepo. Today, the DX (Developer Experience) has been significantly enhanced with ease of maintenance, 100% redundant code reduction and almost simple handling thanks to structured documentation ranging from project initialization in a local environment to the scheme of infrastructure architecture in staging and production.
I pushed this project further by also integrating a Continuous Deployment of Releases in the different environments (staging and production) with semantically versioned docker images.