Введение: зачем монорепозиторий в PHP?
Разработка сложных PHP-приложений (CRM, SaaS, фреймворки) часто сталкивается с дилеммой: хранить весь код в одном репозитории (монорепозиторий) или разбить на множество микросервисов и пакетов. Монорепозиторий (monorepo) с PHP — это стратегия, при которой несколько связанных проектов (библиотек, модулей, приложений) находятся в едином Git-репозитории.
На практике это даёт:
- Единую историю изменений — легче отследить, как изменение в одном пакете повлияло на другой.
- Упрощённую настройку CI/CD — один конвейер для сборки и тестирования всех компонентов.
- Снижение дублирования — общие конфиги, скрипты и зависимости.
- Быструю итерацию — не нужно публиковать пакеты в Packagist для локального тестирования.
Однако монорепозиторий требует дисциплины. Без правильной организации кода он превращается в «спагетти-монолит». В этой статье мы разберём практические шаги по организации PHP-проекта в монорепозитории.
1. Структура каталогов монорепозитория
Ключевой принцип — чёткое разделение на пакеты и приложения. Рассмотрим на примере типовой архитектуры:
project-root/
├── apps/
│ ├── web/ # Веб-приложение (Symfony/Laravel)
│ │ ├── public/
│ │ ├── src/
│ │ └── composer.json
│ └── api/ # API-приложение (Lumen/Slim)
│ ├── public/
│ ├── src/
│ └── composer.json
├── packages/
│ ├── core/ # Ядро (общие интерфейсы, сущности)
│ │ ├── src/
│ │ └── composer.json
│ ├── billing/ # Платёжный модуль
│ │ ├── src/
│ │ └── composer.json
│ └── notifications/ # Модуль уведомлений
│ ├── src/
│ └── composer.json
├── tools/ # Скрипты разработки (PHP-CS-Fixer, PHPStan)
├── composer.json # Корневой манифест
└── phpstan.neon # Общие настройки анализа
Обратите внимание: каждый пакет в packages/ — это полноценная Composer-библиотека со своим composer.json. Это позволяет изолировать зависимости и тестировать их отдельно.
Пример composer.json для пакета core
{
"name": "myproject/core",
"type": "library",
"autoload": {
"psr-4": {
"MyProject\\Core\\": "src/"
}
},
"require": {
"php": ">=8.1"
},
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
}
}
}
2. Управление зависимостями через Composer
Корневой composer.json — сердце монорепозитория. Он содержит:
- Список всех пакетов, подключённых локально (через
pathрепозиторий). - Dev-зависимости для всего проекта (PHPUnit, PHPStan, GrumPHP).
- Скрипты для автоматизации (статический анализ, тесты).
Пример корневого composer.json
{
"name": "myproject/monorepo",
"type": "project",
"repositories": [
{
"type": "path",
"url": "packages/*",
"options": {
"symlink": true
}
}
],
"require": {
"myproject/core": "@dev",
"myproject/billing": "@dev",
"myproject/notifications": "@dev"
},
"require-dev": {
"phpunit/phpunit": "^10.0",
"phpstan/phpstan": "^1.10",
"friendsofphp/php-cs-fixer": "^3.0"
},
"autoload": {
"psr-4": {
"MyProject\\": "packages/*/src/"
}
},
"scripts": {
"test": "phpunit",
"analyse": "phpstan analyse",
"cs-fix": "php-cs-fixer fix"
}
}
Ключевые моменты:
- symlink: true — изменения в пакетах сразу видны в корневом проекте (не нужно выполнять
composer update). - @dev — позволяет подключать нестабильные версии пакетов (для локальной разработки).
- Скрипты — запускаются из корня и применяются ко всем пакетам.
3. Настройка автозагрузки и тестирования
Чтобы PHPUnit и статический анализатор работали со всеми пакетами одновременно, нужно настроить конфигурацию на уровне монорепозитория.
phpunit.xml.dist (корневой)
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="all">
<directory>packages/*/tests</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory>packages/*/src</directory>
</include>
</coverage>
</phpunit>
phpstan.neon (корневой)
parameters:
level: 8
paths:
- packages/*/src
- apps/*/src
excludePaths:
- vendor
- */tests
Теперь для запуска всех тестов достаточно выполнить в корне:
composer test
А для статического анализа:
composer analyse
4. CI/CD и автоматизация релизов
Монорепозиторий упрощает настройку CI. Вот пример пайплайна для GitHub Actions:
name: Monorepo CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions