MonoRepo Setup

Understanding the MonoRepo Structure

A monorepo, or monolithic repository, is a setup where multiple packages (those dependencies that you typically list in a package.json file and install with pnpm install) reside within a single, unified code repository.

This structure is beneficial when managing different components, like connectors, that are used across various projects or applications, which we'll refer to as 'shops' in this context. Each of these shops may also be considered as separate packages within the monorepo.

In a monorepo setup, if we make a change to a particular component, such as a connector, we don't have to update all the shops simultaneously. Instead, we can selectively update only those shops that use or depend on the changed component.

This provides better control over code updates and allows us to maintain different versions of components for different shops, even though all the components and shops reside within the same repository. This structure can simplify code management, especially in large-scale projects where components are shared among multiple applications or services.

Our setup looks like this:

FolderDescription
<root>Contains the root package.json file. Only has prettier
packages/eslint-configContains the shared config for eslint
packages/connectors/contractThis is the contract that the shops use and that the connectors must fulfill
packages/connectors/<connector>Contains the connectors
packages/shops/<shop>Contains the shops

Setting Up Your Development Environment

You should not use any of the npm commands but always pnpm.

Installing a package for a certain package in the repo would be pnpm add --filter kaufland-demo <package>. To get started you could do pnpm install -r, which would install all packages recursively.

Adding a New Package within the Monorepo

To add a new package within the monorepo, you need to create a new directory under the appropriate directory (packages/connectors for connectors, packages/shops for shops). The name of the directory should be the name of your new package.

Inside this new directory, you need to initialize a new package with pnpm init -y. This will create a package.json file with default values. You can then edit this file to specify the details of your package.

Here's an example of how to add a new connector:

cd packages/connectors
mkdir my-new-connector
cd my-new-connector
pnpm init -y

After this, you can start adding your code in the new package directory. Remember to add any new dependencies that your package needs directly to its own package.json file, not the root package.json.

When your package is ready, you can make it available to other packages in the monorepo by running pnpm install -r from the root directory. This will create a symlink in the node_modules directory of the other packages, allowing them to import your new package as if it was installed from the npm registry.

Managing Dependencies

You should not use relative paths import algolia from '../connectors/algolia' when accessing a package from another workspace package but rather use import algoia from '@etribes/algolia'.

Dependencies in package.json to other packages in the repo should be expressed as workspace:<version>, so for example:

"@etribes/connector-contract": "workspace:0.0.1",

This causes pnpm to only use packages from the repo and not download them from the Gitlab repository. If the specified version is not found in the repo, pnpm will throw an error.

Best Practices

  • Always use pnpm instead of npm to ensure that the monorepo structure is respected.
  • If you change code in a connector package (so anything that's not a shop), you will need to bump the version of that package. You also need to make sure that you manually bump any consumers of this package. We tried tools like changesets but their point is to update dependent packages automatically - that's something we don't want. We want certain shops to be pinned at certain versions
  • The CI pipeline will scream at you during Merge Requests if you forgot to bump a package
  • Always use workspace:<version> when adding a dependency to another package in the monorepo.
  • Avoid using relative paths when importing from another package in the monorepo. Use the package name instead.
  • Regularly run pnpm install -r to ensure that all packages are up to date and the symlinks are correctly set up.

Powered by Doctave