preface

At work, we may encounter some project management problems. When it comes to managing individual projects, everyone knows how to manage them. When it comes to managing multiple projects, many people may not be able to manage them well.

This article mainly explains the application of MonorePO in our team.

The plight of multi – repo

Under normal circumstances, when we start a new project, we will first create a new repository on Github, and then create the project locally and associate it with the remote repository. Basically, one repository corresponds to one project.

Reusing code and configuration difficulties

Once you have more projects, you run into more complex situations. For example, some independent H5 active pages, these pages are often unrelated, not easy to deploy together, need to be independently deployed to different domain names.

Besides, these pages might have a lot in common — same error handling, same multilingual copywriting, same ESLint and prettier handling, and so on.

If scaffolding falls, it’s fine. Just create a new project. But many teams don’t maintain scaffolding either, and every time they start a new project, they just copy and paste the configuration of the original project and make some changes, which is very inefficient.

Waste of resources

At the same time, creating a project every time a new page comes along can be too diffuse to manage.

They can also be a waste of resources, such as installing packages like React and react-dom, which can be disastrous if you don’t care.


Debugging trouble

If you want to debug a local project that depends on another project, you can only use NPM link to link it to the project that you want to debug.

Once link projects more, manual to manage these link operations will be easy to heart tired, further will develop to drop the keyboard, hit the monitor.

NPM and submodules

You might want to release them to NPM, but as soon as there is a new release change, each dependent project has to follow.

So what’s a good idea? You might also think of Git submodules, where you can integrate those same parts into a Git repository as submodules.


Submodules do solve this problem, but not enough to solve the problem of repeated install dependencies described earlier. And submodules are hard to use.

In fact, on our side, there is a very suitable use scenario for submodules.

Our services are all written in GO and GRPC, but the front end cannot directly call the GRPC interface (there are libraries to support it, but it is too large).

Therefore, a Node service is needed to dynamically load the.proto file to call the GRPC service. This layer of Node service plays the role of converting the GRPC interface to the HTTP interface, which we call the gateway.


In this way, in the service, we must get the back end proto file to dynamically call the service, so we can directly put the back end proto file in the form of submodules embedded in the project.

This way we don’t need to manually copy their files into the project, we just need to update the SubModule every time they change.

monorepo

Monorepo and multirepo are opposite concepts. Monorepo allows us to manage multiple projects in the same repository.


Many open source projects use Monorepo, such as Babel and NuxtJS, which use Lerna to manage projects.

Lerna is also relatively simple to use, just use lerna init to generate packages directories and lerna.json files.

The structural directory of LERNA project is generally as follows:

- packages

  - project1

    - src

      - index.ts

    - package.json

  - project2

    - src

      - index.ts

    - package.json

  - project3

    - src

      - index.ts

    - package.json

- lerna.json

- package.json

- tsconfig.json

Copy the code

Lerna and Yarn workspace are similar in function. Here we mainly talk about the usage of Yarn workspace (lerna I really don’t use much).

yarn workspace

Workspaces is an important advantage of YARN over NPM (another advantage is faster downloads), and it allows us to manage projects using monorepo form.

Setting private to true in package.json and specifying the subprojects in the Workspaces field is also easy.

Take the above lerNA project structure as an example:

{

.

    private: true.

    workspaces: [

      "packages/*"

    ]

}

Copy the code

Of course, Yarn Workspace does not require you to put it under the Packages directory. You can also manually declare each subitem without using wildcards.

{

.

    private: true.

    workspaces: [

      "packages/project1".

      "packages/project2"

    ]

}

Copy the code

Node_modules is not installed into the node_modules of each subproject, but directly into the root directory, so that each subproject can read the node_modules of the root directory.

The entire project only has a yarn.lock file under the root directory. Subprojects are also linked to node_modules, which allows us to import the corresponding project directly.

- node_modules

  - project1

  - project2

  - project3

- packages

  - shared

    - src

      - index.ts

  - project1

    - node_modules

    - src

      - index.ts

    - package.json

  - project2

    - node_modules

    - src

      - index.ts

    - package.json

  - project3

    - node_modules

    - src

      - index.ts

    - package.json

- yarn.lock

- package.json

- tsconfig.json

Copy the code

Of course, if your subproject relies on a different version of the package, you will also install the corresponding version of the package in the node_modules of the subproject.

For example, if there is a 2.5 vue in package.json and a 2.6 vue in project1, then the 2.5 version will be installed in node_modules in the root directory. Node_modules under project is installed with version 2.6.

- node_modules

  - [email protected]

- packages

  - shared

    - src

      - index.ts

  - project1

    - node_modules

      - [email protected]

    - src

      - index.ts

    - package.json

  - project2

    - node_modules

    - src

      - index.ts

    - package.json

  - project3

    - node_modules

    - src

      - index.ts

    - package.json

- yarn.lock

- package.json

- tsconfig.json

Copy the code

If multiple subprojects depend on different versions of the same package, the one installed in the root directory is the one with the highest version number.

Yarn workspace command

Yarn Workspace provides some common commands.

Generally speaking, the yarn workspace project run XXX command is used to execute a command under a project.

To execute a command under all projects, use YARN workspaces run XXX.

Install dependencies

Install the entire project with the same dependencies as normal YARN. Yarn install is done.

If you want to install a dependency, there are three scenarios:

  1. Yarn Workspaces add Package: Install dependencies for all applications
  2. Yarn Workspace project add Package: Install dependencies for an application
  3. Yarn add -w -d package: installs dependencies for the root application

Clean up the node_modules

To remove all node_modules, use lerna clean, or install Rimraf and write in package.json for each subproject:

clean: rimraf node_modules

Copy the code

This allows you to delete node_modules for all projects by executing YARN Workspaces run clean directly under the root directory.

If you just want to reload node_modules, you can use YARN install –force to retrieve all node_modules.

Applicable scenario

Scattered pages

React, React-dom, React-router, etc. All H5 active pages depend on React, React-dom, etc., but they need to be deployed under different domains, so it is not easy to manage the React-router.

So here we can put them in the same warehouse and manage the warehouse in the form of monorepo.

Because they use the same technology stack, ESLint, Prettier, and even WebPack configurations can be pulled out of the way and not maintained in every project.

Take the configuration after create-react-app eject as an example:

- node_modules

  - react

  - react-dom

  - redux

  - lodash

  

- packages

  - project1

    - package.json

  - project2

    - package.json

    

- config

  - webpack.config.js

  - webpack.dev.config.js

  

- scripts

  - create.js

  - bin.js

  - build.js

  - start.js

  

- .eslintrc.js

- .prettierrc

- commitlint.config.js

- jest.config.js

- tsconfig.js

- package.json

- yarn.lock

Copy the code

As we can see, the generic configuration has been extracted to the outermost layer.

If you run or build a subproject, you only need to do so in the package.json of the subproject.

 "start""node .. /.. /scripts/start.js".

 "build""node .. /.. /scripts/build.js".

 "test""node .. /.. /scripts/test.js"

Copy the code

Front end item

Sometimes we need to use NodeJS to write simple interfaces for our own front-end projects. Often we need to create a server project, but this project is very simple and only used by the front-end project.

Then we don’t have to manage them in two warehouses, we can manage them in one warehouse.

- website

  - package.json

- server

  - package.json

- package.json

Copy the code

At build time, you can directly use server to render the index.html from the website, which only needs to configure a copy of nginx.

A chestnut

Recently, I was working with the team next door to develop a Google login function. I provided the login page and authentication interface.

Although this feature is intended for them, it is possible to access other teams’ applications later, so I want this interface to be universal.

In the early stage, for convenience, the projects on both sides were deployed together. But my project might migrate out later, so my service exports only one route, and their service dynamically loads my route.

So the project structure looks like this:

- login

- website

- loginServer

- server

- package.json

Copy the code

Add loginServer directly to server:

import fastify from 'fastify'

import loginServer from 'loginServer';



fastify.register(loginServer, {

  prefix'/login'

})

Copy the code

In SRC /index.ts of loginServer, a route is directly exported.

import routes from './routes'



export default routes

Copy the code

This organizational structure is also a flexible use of Monorepo.

conclusion

Although Monorepo is not a new technology, it became so popular that many projects in the team are now managed in the form of Monorepo.

With the team’s Github Actions syntax, which is based on Jenkins Groovy, there was no difficulty in building and deploying.

Ps: The above pictures are from the network, deleted