Монорепозиторий с PHP: организация кода и лучшие практики
В современной PHP-разработке всё чаще встаёт вопрос об эффективной организации кода. Традиционный подход с множеством отдельных репозиториев (мультирепозиторий) имеет свои недостатки: сложность синхронизации версий, дублирование конфигураций и проблемы с рефакторингом. Альтернативой выступает монорепозиторий (monorepo) — стратегия, при которой все связанные проекты, пакеты и библиотеки хранятся в едином репозитории. В этой статье мы разберём, как организовать монорепозиторий с PHP, какие инструменты использовать и какие подводные камни учесть.
Что такое монорепозиторий и зачем он нужен PHP-проектам?
Монорепозиторий — это единое хранилище кода, содержащее несколько проектов или пакетов. В мире PHP классический пример — Laravel, который включает в себя фреймворк, пакеты для аутентификации, очередей, почты и другие компоненты в одном репозитории.
Основные преимущества для PHP-проектов:
- Упрощение рефакторинга — изменения в одном пакете сразу видны всем зависимым компонентам, не нужно публиковать и обновлять версии каждого пакета отдельно.
- Целостность версий — все пакеты используют одну и ту же версию зависимостей, что исключает конфликты типов.
- Единая CI/CD — один конвейер сборки для всех компонентов, упрощение тестирования кросс-пакетных изменений.
- Улучшение совместной работы — разработчики видят весь код проекта, проще проводить code review и поддерживать единые стандарты кодирования.
Однако монорепозиторий подходит не всем. Если ваши проекты слабо связаны или имеют разные циклы релизов, мультирепозиторий может быть удобнее. Для средних и крупных PHP-проектов с тесной интеграцией компонентов монорепозиторий часто становится оптимальным выбором.
Структура монорепозитория на PHP
Правильная структура — основа успешного монорепозитория. Рассмотрим типовую организацию на примере проекта с ядром (core), несколькими модулями и тестами.
my-php-monorepo/
├── packages/
│ ├── core/
│ │ ├── src/
│ │ │ └── CoreService.php
│ │ ├── tests/
│ │ └── composer.json
│ ├── module-auth/
│ │ ├── src/
│ │ ├── tests/
│ │ └── composer.json
│ └── module-payment/
│ ├── src/
│ ├── tests/
│ └── composer.json
├── apps/
│ ├── api/
│ │ ├── public/
│ │ ├── src/
│ │ └── composer.json
│ └── console/
│ ├── src/
│ └── composer.json
├── tests/
│ └── IntegrationTest.php
├── composer.json (корневой)
├── phpunit.xml.dist
├── .gitignore
└── README.md
Ключевые принципы организации:
- Разделение на пакеты (packages) — каждый пакет имеет свой
composer.jsonс именем, автозагрузкой и зависимостями. - Приложения (apps) — точки входа (API, консольные команды), которые собирают пакеты вместе.
- Корневой composer.json — содержит мета-конфигурацию для работы с монорепозиторием.
Настройка корневого composer.json
Для работы монорепозитория необходимо настроить корневой composer.json так, чтобы пакеты были доступны локально, без публикации в Packagist.
{
"name": "my-company/my-monorepo",
"type": "project",
"require": {
"php": "^8.1",
"my-company/core": "*",
"my-company/module-auth": "*",
"my-company/module-payment": "*"
},
"autoload": {
"psr-4": {
"MyCompany\\": "packages/"
}
},
"repositories": [
{
"type": "path",
"url": "packages/*",
"options": {
"symlink": true
}
}
],
"scripts": {
"test": "phpunit",
"cs-fix": "php-cs-fixer fix"
}
}
Параметр "type": "path" указывает Composer, что пакеты находятся локально. Опция "symlink": true создаёт симлинки, что позволяет разработчику видеть изменения в реальном времени без переустановки зависимостей.
Инструменты для работы с монорепозиторием в PHP
Хотя базовую структуру можно настроить вручную, для полноценной работы с монорепозиторием в PHP рекомендуется использовать специализированные инструменты.
1. Monorepo Builder (symplify/monorepo-builder)
Это самый популярный инструмент для PHP. Он автоматизирует задачи, связанные с монорепозиторием: синхронизацию версий, релизы, разделение на отдельные репозитории (split) и многое другое.
Установка:
composer require symplify/monorepo-builder --dev
Пример конфигурации monorepo-builder.php:
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\MonorepoBuilder\ValueObject\Option;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
// Указываем, где находятся пакеты
$parameters->set(Option::PACKAGE_DIRECTORIES, [
__DIR__ . '/packages',
]);
// Пакеты, которые не нужно публиковать отдельно
$parameters->set(Option::DATA_TO_REMOVE, [
'require' => [
'phpunit/phpunit' => '*',
],
]);
};
Основные команды:
vendor/bin/monorepo-builder merge— объединяет composer.json всех пакетов в корневой.vendor/bin/monorepo-builder split— разделяет монорепозиторий на отдельные репозитории для публикации.vendor/bin/monorepo-builder bump-interdependency— синхронизирует версии зависимостей между пак