Монорепозиторий с JavaScript: организация кода и лучшие практики

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Монорепозиторий с JavaScript: организация кода и лучшие практики

Современная разработка на JavaScript (Node.js) часто сталкивается с проблемой управления кодом в больших проектах. Когда количество микросервисов, библиотек и приложений растет, традиционные многомодульные репозитории (Polyrepo) начинают создавать хаос: дублирование конфигураций, сложности с синхронизацией версий и долгий CI/CD. Решение — монорепозиторий (monorepo). В этой статье мы разберем, как организовать код в монорепозитории на JavaScript, какие инструменты использовать и как избежать типичных ошибок.

Что такое монорепозиторий и зачем он нужен?

Монорепозиторий — это подход, при котором весь код проекта (несколько приложений, библиотек, сервисов) хранится в одном репозитории. В отличие от Polyrepo, где каждый компонент живет в своем Git-репозитории, monorepo объединяет все в единую структуру.

Преимущества монорепозитория для JavaScript:

  • Единая версия зависимостей — все пакеты используют одни и те же версии библиотек, что исключает конфликты.
  • Переиспользование кода — общие утилиты и компоненты легко импортируются между подпроектами без публикации в npm.
  • Атомарные коммиты — изменения в нескольких пакетах можно делать в одном коммите, что упрощает ревью и откат.
  • Упрощенный CI/CD — один пайплайн для всего проекта, кэширование сборок и тестов.

Однако у монорепозитория есть и минусы: он требует строгой дисциплины в организации кода и правильного выбора инструментов. Далее мы рассмотрим, как организовать структуру и какие инструменты использовать.

Инструменты для управления монорепозиторием в Node.js

Экосистема JavaScript предлагает несколько зрелых инструментов для работы с monorepo. Рассмотрим три самых популярных:

1. Lerna

Классический инструмент, который долгое время был стандартом. Lerna управляет публикацией пакетов, версионированием и запуском скриптов во всех пакетах. Пример конфигурации lerna.json:

{
  "version": "independent",
  "packages": ["packages/*"],
  "npmClient": "yarn"
}

Команда lerna bootstrap устанавливает зависимости для всех пакетов и связывает их между собой.

2. Nx (Nrwl)

Более современный и мощный инструмент с расширенной системой кэширования, графами зависимостей и генераторами кода. Nx поддерживает не только JavaScript, но и TypeScript, Angular, React, Next.js. Пример команды для создания библиотеки:

npx nx generate @nrwl/node:library my-lib --directory=shared

Nx автоматически анализирует зависимости и пересобирает только измененные пакеты.

3. Turborepo

Созданный Vercel, Turborepo фокусируется на скорости сборки и простоте. Он использует умное кэширование и параллельное выполнение задач. Пример turbo.json:

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": []
    }
  }
}

Turborepo идеально подходит для проектов с большим количеством пакетов, где важна скорость CI.

Организация структуры проекта в монорепозитории

Правильная структура папок — основа успешного monorepo. Рассмотрим типовую структуру для Node.js проекта:

my-monorepo/
├── packages/
│   ├── shared-utils/       # Общие утилиты
│   │   ├── src/
│   │   │   └── index.js
│   │   └── package.json
│   ├── api-gateway/        # API-шлюз
│   │   ├── src/
│   │   │   └── server.js
│   │   └── package.json
│   └── web-app/            # Веб-приложение (React/Vue)
│       ├── src/
│       │   └── App.js
│       └── package.json
├── tools/                  # Скрипты и конфигурации
│   ├── eslint/
│   └── jest/
├── package.json            # Корневой package.json
├── lerna.json              # или turbo.json / nx.json
└── README.md

Ключевые принципы организации:

  • Изолированные пакеты — каждый пакет имеет свой package.json и собственную точку входа.
  • Единые тулы — ESLint, Prettier, Jest настраиваются один раз в корне и применяются ко всем пакетам.
  • Общие скрипты — в корневом package.json определяются скрипты для запуска всех пакетов (например, "test": "lerna run test").

Пример: создание простого монорепозитория с Lerna

Давайте создадим минимальный монорепозиторий с двумя пакетами: библиотекой утилит и приложением, которое ее использует.

Шаг 1: Инициализация

mkdir my-monorepo
cd my-monorepo
npm init -y
npm install --save-dev lerna
npx lerna init

Шаг 2: Создание пакетов

Создадим пакет shared-utils:

mkdir -p packages/shared-utils/src
cd packages/shared-utils
npm init -y

Содержимое packages/shared-utils/src/index.js:

function greet(name) {
  return `Hello, ${name}!`;
}

module.exports = { greet };

Теперь создадим пакет web-app:

mkdir -p packages/web-app/src
cd packages/web-app
npm init -y
npm install shared-utils@0.0.0

Содержимое packages/web-app/src/index.js:

const { greet } = require('shared-utils');

console.log(greet('World'));

Шаг 3:

Блоги

Рекомендаций по книгам