Монорепозиторий с PHP: организация кода для больших проектов

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

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

Начать курс

Монорепозиторий с PHP: организация кода для масштабируемых проектов

В современной PHP-разработке управление кодом становится критически важным по мере роста проекта. Традиционный подход с множеством отдельных репозиториев (полирепозиторий) часто приводит к проблемам синхронизации, дублированию зависимостей и сложностям при рефакторинге. Альтернатива — монорепозиторий (Monorepo), когда весь код, включая библиотеки, утилиты и приложения, хранится в одном Git-репозитории. В этой статье мы разберем, как организовать монорепозиторий на PHP, используя Composer, Git и современные инструменты.

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

Монорепозиторий — это единый репозиторий, содержащий несколько проектов, пакетов или модулей, которые могут быть логически связаны. Для PHP-разработчика это означает, что ядро фреймворка, API-сервис, административная панель и общие библиотеки находятся в одной папке.

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

  • Единая версионность: Все изменения коммитятся атомарно — исправление в библиотеке сразу сопровождается обновлением кода, который её использует.
  • Упрощение рефакторинга: Вы можете изменить интерфейс пакета и сразу обновить все зависимости в рамках одного pull request.
  • Стандартизация инструментов: Один файл composer.json для корня, единые правила линтинга и тестирования.
  • Снижение дублирования: Общий код (например, DTO, хелперы) не нужно публиковать в Packagist — он доступен напрямую.

Однако монорепозиторий требует дисциплины: без чёткой структуры он превращается в «свалку». Ключ к успеху — правильная организация с помощью Composer autoload и Git-сабмодулей или специализированных инструментов вроде monorepo-builder.

Структура монорепозитория на PHP

Рассмотрим типовую структуру для PHP-проекта, где есть несколько библиотек и одно приложение. Корневой каталог выглядит так:

my-monorepo/
├── packages/
│   ├── core/                  # Базовая библиотека (ядро)
│   │   ├── src/
│   │   │   └── CoreClass.php
│   │   └── composer.json
│   ├── logger/                # Логирование
│   │   ├── src/
│   │   └── composer.json
│   └── api/                   # API-пакет (может быть отдельным приложением)
│       ├── src/
│       │   └── Controllers/
│       └── composer.json
├── apps/
│   └── web/                   # Веб-приложение (Laravel/Symfony)
│       ├── public/
│       ├── src/
│       └── composer.json
├── composer.json              # Корневой composer.json (мета-пакет)
├── phpunit.xml.dist
└── .gitignore

Принципы организации:

  • Все пакеты находятся в packages/. Каждый пакет имеет собственный composer.json с уникальным именем (например, mycompany/core).
  • Приложения (фронт, админка, API) лежат в apps/. Они могут использовать пакеты из packages/ как локальные зависимости.
  • Корневой composer.json служит для глобальной конфигурации (autoload, скрипты).

Настройка Composer для работы с монорепозиторием

Чтобы PHP-автозагрузка работала без публикации пакетов на Packagist, используем path-репозитории. Добавьте в корневой composer.json следующий блок:

{
    "name": "mycompany/monorepo",
    "require": {
        "mycompany/core": "*",
        "mycompany/logger": "*"
    },
    "repositories": [
        {
            "type": "path",
            "url": "packages/*"
        }
    ],
    "autoload": {
        "psr-4": {
            "MyCompany\\": "packages/"
        }
    },
    "scripts": {
        "test": "phpunit"
    }
}

Теперь при выполнении composer install Composer создаст симлинки на локальные пакеты. Это значит, что изменения в packages/core/src/CoreClass.php сразу отразятся в приложении без переустановки зависимостей.

Пример пакета packages/core/composer.json:

{
    "name": "mycompany/core",
    "type": "library",
    "autoload": {
        "psr-4": {
            "MyCompany\\Core\\": "src/"
        }
    },
    "require": {
        "php": ">=8.1"
    }
}

Обратите внимание: неймспейсы должны быть уникальными. Для пакета core используем MyCompany\Core, для logger — MyCompany\Logger.

Git-стратегии для монорепозитория

Работа с Git в монорепозитории требует особого подхода, чтобы не «засорять» историю коммитами из разных несвязанных пакетов. Рекомендуемые практики:

  • Feature branches: Создавайте ветки для каждой фичи, даже если она затрагивает несколько пакетов. Коммитьте атомарно: один коммит — одно логическое изменение.
  • Commit messages с префиксами: Используйте соглашение [core], [api] или [web] в начале сообщения, чтобы было понятно, какой пакет изменён. Пример: [logger] Добавлен метод logError().
  • Избегайте сабмодулей: В классическом монорепозитории Git-сабмодули не нужны — все файлы находятся в одном репозитории. Если требуется изолировать историю пакетов, рассмотрите git subtree или инструмент monorepo-builder (symplify/monorepo-builder).

Пример использования monorepo-builder:

Установите пакет:

composer require --dev symplify/monorepo-builder

Создайте

Блоги

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