Monorepo Management is the practice of hosting multiple different projects or applications (e.g., a web application, a mobile application, a shared UI library, an API, etc.) within a single version control repository (usually Git). Traditionally, each project has its own separate repository (polyrepo or multi-repository).
Why Use a Monorepo?
1. Easy Management of Shared Code: This allows you to easily share and synchronize code snippets, such as common UI components, utility functions, or types, across all projects. When a change is made, all projects affected by that change are instantly updated.
2. Atomic Commits: You can make logically related changes across multiple projects or packages in a single commit. For example, it's possible to commit a change to a shared library and its adaptation in an application that uses that change simultaneously.
3. Simplified Dependency Management: All projects and packages can share the same `node_modules` directory or a virtual `node_modules` structure. This reduces dependency conflicts and saves disk space.
4. Consistent Toolset and Configuration: The same build tools, linting rules, testing frameworks, and coding standards can be applied across all projects, unifying the development experience.
5. Easier Refactoring: When refactoring a component or service, it's easier to see and apply changes to all affected projects in one place.
6. Centralized CI/CD: It's simpler to establish a centralized CI/CD (Continuous Integration/Continuous Deployment) pipeline for all projects in a single repository.
Challenges of a Monorepo:
1. Repository Size and Performance: The repository size can grow over time, which can slow down operations like git clones.
2. Build and Test Times: Compiling and testing all projects or dependencies each time can take a long time. Tools capable of smart compilation (compiling only the changed parts) mitigate this issue.
3. Access Control: Ensuring that a specific team only has access to their own project can be more complex, as all the code is in a single repository.
4. Tool Complexity: To effectively manage monorepos, specialized tools such as Lerna, Nx, Turborepo, Yarn Workspaces, or pnpm Workspaces are needed.
Monorepo Management Tools:
* Yarn Workspaces / pnpm Workspaces / npm Workspaces: Provides basic dependency management and package hoisting. Ideal for projects with multiple packages.
* Lerna: A popular tool for managing package dependencies, releasing versions, and running commands on packages in a monorepo.
* Nx (Nrwl Extensible Dev Tools): Provides advanced build optimizations, smart recompilation, code generation, and a robust framework for a range of technologies (React, Angular, Node.js, etc.). It excels in large and complex monorepos. * Turborepo: A next-generation monorepo tool featuring high-performance build caching and parallel task execution capabilities.
These tools help manage the complexities of a monorepo, optimize build times, and improve the developer experience.
Example Code
// This example demonstrates a monorepo structure and how a React application uses a shared UI component.
// Let's assume we have a root directory called "my-monorepo".
// It's configured using Yarn Workspaces.
// 1. my-monorepo/package.json (Root Directory)
// The root package.json file defines all subdirectories under the "packages" folder in the monorepo with the "workspaces" attribute.
// The "nohoist" setting ensures that some dependencies (which can be mandatory in React-based projects) are not pushed to the root level.
```json
{
"name": "my-monorepo",
"version": "1.0.0",
"private": true,
"workspaces": [
"packages/*"
],
"scripts": {
"start:web": "yarn workspace web-app start",
"build:web": "yarn workspace web-app build",
"build:shared-ui": "yarn workspace shared-ui build"
},
"devDependencies": {
"lerna": "^6.0.0" // Additional tools like Lerna can also be used
}
}
```
// 2. my-monorepo/packages/shared-ui/package.json
// This file defines the package of a shared UI library (for example, the Button component) within the monorepo.
```json
{
"name": "shared-ui",
"version": "1.0.0",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"files":[
"dist"
],
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w"
},
"devDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@rollup/plugin-babel": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
"rollup": "^3.0.0",
"@babel/core": "^7.20.0",
"@babel/preset-react": "^7.18.0"
}
}
```
// 3. my-monorepo/packages/shared-ui/src/Button.js
// A simple React component in the shared UI library.
```javascript
// my-monorepo/packages/shared-ui/src/Button.js
import React from 'react';
function Button({ children, onClick, variant = 'primary' }) {
const buttonStyle = {
padding: '10px 20px',
borderRadius: '5px',
border: 'none',
cursor: 'pointer',
backgroundColor: variant === 'primary' ? '#007bff' : '#6c757d',
colour: 'white',
fontSize: '16px'
};
return (
<button style={buttonStyle} onClick={onClick}>
{children}
</button>
);
}
export default Button;
```
// 4. my-monorepo/packages/shared-ui/src/index.js
// Main export file of the shared UI library.
```javascript
// my-monorepo/packages/shared-ui/src/index.js
export { default as Button } from './Button';
```
// 5. my-monorepo/packages/web-app/package.json
// This file defines the package for a React web application within the monorepo.
// Note how the "shared-ui" package is referenced as a dependency.
// The "workspace:^" syntax is the standard way to reference a local package in Yarn Workspaces.
```json
{
"name": "web-app",
"version": "0.1.0",
"private":true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"shared-ui": "workspace:^1.0.0" // Reference to shared UI package in Monorepo
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/gesture"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
```
// 6. my-monorepo/packages/web-app/src/App.js
// Demonstrates how a React web application imports and uses the 'Button' component from the shared UI library.
````javascript
// my-monorepo/packages/web-app/src/App.js
import React from 'react';
import { Button } from 'shared-ui'; // Import from shared package in Monorepo
import './App.css'; // Default Create React App CSS
function App() {
const handleClick = () => {
alert('Button clicked!');
};
return(
<div className="App">
<header className="App-header">
<h1>Monorepo Demo with Shared UI</h1>
<Button onClick={handleClick} variant="primary">
ClickMe!
</button>
<br />
<Button onClick={() => alert('Secondary button clicked!')} variant="secondary">
Secondary Button
</Button>
</header>
</div>
);
}
export defaultApp;
```
Installation and Run Steps:
1. Go to the Monorepo Root Directory: `cd my-monorepo`
2. Install Dependencies: `yarn install` (Yarn will detect the `workspaces` feature and manage dependencies for all packages).
3. Create the Shared UI Package: `yarn workspace shared-ui build` (This creates the `dist` folder of the `shared-ui` package so `web-app` can import it).
4. Start the Web Application: `yarn workspace web-app start` (This command runs the `start` script of the `web-app` package and typically starts the application at `http://localhost:3000`). `web-app` directly installs `shared-ui` in `node_modules`








Monorepo Yƶnetimi