https://www.aviator.co/blog/monorepo-a-hands-on-guide-for-managing-repositories-and-microservices/
Не так давно я стикався з проблемою використання однакових компонентів і конфігурацій у кількох проектах одночасно. Спочатку я використовував метод розділення Git Repo для спільних частин, але результат був таким, що інколи спільний код не оновлювався або виникали баги, що вимагало частих оновлень. Це призвело до пошуку рішення, і я натрапив на техніку, яку називають Monorepo. Коли я почав читати про концепцію, я зрозумів, що ця техніка може допомогти вирішити мою проблему. Тому я вирішив написати цей блог, щоб поділитися та зберегти його для себе, а також поділитися з іншими розробниками, щоб вони могли використовувати і розвивати це далі.
Чому Monorepo?
Звісно, все має свої переваги і недоліки, які треба ретельно оцінити перед прийняттям рішення про використання. Тому перед тим, як вирішити, чи варто використовувати Monorepo, важливо ретельно проаналізувати його плюси і мінуси, щоб переконатися, що Monorepo підходить для потреб і розміру проекту, над яким ви працюєте.
Переваги
- Обмін кодом між проектами: Ви стикаєтесь з проблемою розділення модулів, які можна використовувати в кількох проектах, і кількість репозиторіїв зростає? Якщо так, то використання Monorepo допоможе зменшити повторювану роботу і збільшити гнучкість у розвитку. Один репозиторій, не потрібно турбуватись про багатократне отримання кодів з різних репозиторіїв для виконання роботи.
- Відсутність проблем з отриманням кількох репозиторіїв для запуску одного проекту: Коли код спільний, без Monorepo нам доведеться створювати окремі git-репозиторії для кожної спільної папки. У деяких організаціях використовують різні git-токени для кожного репозиторію (що є хорошою практикою). Використання Monorepo вирішує цю проблему.
- Можливість відразу використовувати оновлення в спільному коді: Ви більше не забудете оновити спільний репозиторій, оскільки це буде відбуватись автоматично, коли все знаходиться в одному репозиторії.
Недоліки
- Управління версіями може бути складним: Якщо не продумати git flow, ваш git-репозиторій може швидко стати важким для управління.
- Більше навантаження на машину, що виконує: Якщо у вас мало оперативної пам'яті або слабкий процесор, система може працювати повільніше через більшу кількість ресурсів і простору, що використовуються.
3.
Проблеми інтеграції та розгортання: Коли репозиторій стає складнішим, конфігурація для розгортання також ускладнюється. Потрібно добре спланувати і виділити час для налаштування.
У цьому блозі я налаштовуватиму Monorepo, використовуючи Bun як основу для розробки. Для цього буде створено папку shared для зберігання утиліт і папку myproject, яка буде використовуватися як основний проект.
Попередні вимоги
Для цього блогу буде використовуватися наступний технологічний стек:
- Bun v1.1.2
- Node v20.15
- NextJS v14 ReactJS v18
- Typescript v5.0.0
Коли все готово, почнемо!
Структура папок
Почнемо зі створення робочого простору, створивши нову папку, яку я назвучу themonorepo. Потім використаємо команду bun init -y
, щоб ініціалізувати проект. Після завершення створення структура папок виглядатиме так:
Ініціальна структура папок
Ця команда створить файл index.ts, який в кореневій папці не буде використовуватись. Його можна видалити.
Далі створимо ще одну папку, в якій зберігатимемо спільні утиліти. У цьому блозі вона буде називатися shared
.
mkdir shared
Тепер заходимо в папку shared і створюємо ще 2 папки: components і utils.
cd shared
mkdir components
mkdir utils
Тепер використовуємо команду bun init -y
для кожної з папок:
cd components
bun init -y
cd ../utils
bun init -y
У папці components будуть використовуватися компоненти React, тому може знадобитися змінити tsconfig.json і встановити React, щоб усе працювало коректно (зверніть увагу на версію, адже якщо вона не співпадає з версією в інших проектах, можуть виникнути проблеми).
Якщо у вас є класи утиліт, такі як Tailwind, їх не потрібно буде встановлювати заново, оскільки вони будуть компілюватися під час виконання та повинні вже бути в основному проекті.
bun add react@18
bun add -D @types/react@18
Нарешті, основний проект, в цьому блозі буде тільки один модуль, який я назву myproject. Створимо його за допомогою такої команди:
bunx create-next-app myproject
Ця команда запитає у нас кілька налаштувань для конфігурації проекту. У цьому блозі ми використовуватимемо значення за замовчуванням.
Тепер наша структура папок буде виглядати так:
themonorepo/
├── shared/
│ ├── components/ # Компоненти React
│ └── utils/ # Утиліти
├── myproject/ # Основний проект Next.js
│ ├── pages/
│ ├── public/
│ ├── components/
│ ├── tsconfig.json
│ ├── package.json
│ └── next.config.js
└── package.json # Основний package.json
Налаштування робочого простору
Тепер ми налаштуємо root folder, щоб він знав про всі модулі всередині. Для цього оновимо файл package.json
кореневої папки, ось так:
{
...,
"workspaces": [
"shared/*",
"myproject"
]
}
У цьому випадку ми додали параметр workspaces, який є масивом, що вказує шляхи до підпроектів.
"shared/*"
: Помітимо, що післяshared/
є*
, оскільки ця папка містить підкаталоги, такі як components/ та utils/"myproject"
: Основний проект, який знаходиться в папці myproject
Далі створимо scripts, щоб запустити Next.js. Назву скрипта можна вибрати довільно. Коли ми запустимо, будемо використовувати команду bun run <назва скрипта>
. У цьому блозі називатимемо скрипт "dev" (але праву частину команди змінювати не можна).
{
...,
"workspaces": [
"shared/*",
"myproject"
],
"scripts": {
"dev": "bun run --filter myproject dev"
}
}
У цьому прикладі:
- Параметр
--filter
використовується для вказівки, який модуль ми хочемо запустити. - Після цього додаємо
dev
, що є скриптом у package.json проекту Next.js в myproject.
Тепер спробуємо запустити:
bun run dev
Підключення утилітних папок до основного проекту
Тепер давайте змінимо назви модулів у shared folders, щоб вони відповідали вимогам Monorepo та щоб ми могли звертатися до цих папок в основному проекті з префіксом “@testmonorepo/*”. Змініть файл відповідно.
# ./shared/utils/package.json
{
"name": "@testmonorepo/utils",
...
# ./shared/components/package.json
{
"name": "@testmonorepo/components",
...
}
Тепер ми скажемо TypeScript для Next.js, щоб він знав про наші папки, оновивши файл tsconfig.json
в myproject наступним чином:
# ./myproject/tsconfig.json
{
...,
"paths": {
"@/*": ["./src/*"],
"@testmonorepo/utils": ["../shared/utils/*"],
"@testmonorepo/components": ["../shared/components/*"]
},
...
}
Тепер спробуємо перевірити, чи правильно налаштували модулі, додавши код до utils та components, а потім викликаючи їх
// ./shared/utils/index.ts
export const greetUser = (name: string): string => {
return `Hello, ${name}!`;
};
// ./shared/components/container.tsx
import type { FC, PropsWithChildren } from "react";
const Container: FC = ({ children }) => {
return (
<div className="container">{children}</div>
);
};
export default Container;
Тепер спробуємо додати це в page.tsx
:
// ./myproject/src/app/page.tsx
import Container from "@testmonorepo/components/container";
import { greetUser } from "@testmonorepo/utils";
export default function Home() {
return <Container>{greetUser("Champ")}</Container>;
}
Результат буде таким:
Якщо ви побачите, Tailwind class у components не працює, тому що цей компонент знаходиться поза content у Tailwind config. Це можна виправити двома способами:
- Додати Tailwind у модуль components.
- Або додати content config безпосередньо в myproject.
У цьому блозі ми виберемо другий спосіб — додамо content в Tailwind config для myproject:
# ./myproject/tailwind.config.ts
{
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
"../shared/**/*.{js,ts,jsx,tsx,mdx}"
],
...,
}
Тепер спробуємо оновити сторінку:
Тепер все правильно відображається.
Висновок
При створенні Monorepo є кілька важливих аспектів, які можуть допомогти зробити ваш проект більш ефективним і уникнути проблем у майбутньому.
Основні моменти
- Розділення модулів: Варто чітко проектувати модулі, наприклад, розділяючи код, що використовується повторно (shared utilities) і основний проект, щоб забезпечити їх узгодженість.
- Налаштування імен: Використовуйте префікси, щоб позначити, що модуль є частиною Monorepo. Це допоможе зробити проект більш організованим і зрозумілим, оскільки робота з Monorepo ускладнює структуру проекту, тому організація імен має велике значення.
- Сумісність версій: Звертайте увагу на використання залежностей з різними версіями в різних модулях, оскільки це може спричинити проблеми з компіляцією або взаємодією між модулями.
- Налаштування Tailwind CSS: Якщо використовуєте Tailwind CSS, будьте уважні до неправильних налаштувань у файлі tailwind.config.ts, що може призвести до того, що частини коду не будуть оброблені.
- Управління змінами: Коли відбуваються зміни в одному з модулів, можливо, буде потрібно оновити пов'язані модулі, щоб вони були узгоджені, що робить управління Monorepo більш обережним. Це один з основних недоліків роботи з Monorepo.
6.
Здоров'я коду: У Monorepo, якщо частина коду має помилку, це часто впливає на весь репозиторій, що може позначитися на багатьох проектах у ньому. Якщо виправлення в одному місці спричиняє проблеми в усіх проектах, важливо приділяти увагу перевірці та підтримці якості коду в усіх модулях.
TailwindCSS 4.0
Для тих, хто використовує Tailwind 4.0, а також додає директорії в основний проект, щоб забезпечити роботу Tailwind у спільних модулях, додайте цей рядок у основний CSS файл:
@import 'tailwindcss';
@source '../../../shared/**/*.{js,ts,jsx,tsx,mdx}'; // додайте цей рядок
...
Можете клонувати цей репозиторій для вивчення або тестування.
Посилання
- Зрозуміти Monorepo за 3 хвилини (+ приклад коду)
- Управління репозиторієм впливає на ефективність команди (частина Monorepo)
- Два роки досвіду роботи з Monorepo
Перекладено з: มาลองทำ Monorepo กัน