Монорепозиторий с PHP: организация кода на практике

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

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

Начать курс

Введение: что такое монорепозиторий и зачем он нужен PHP-разработчику?

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

В этой статье мы на практике разберем, как организовать монорепозиторий для PHP-проекта. Вы узнаете, как настроить Composer, структурировать папки, управлять зависимостями и организовать CI/CD. Статья ориентирована на PHP-разработчиков среднего уровня, знакомых с основами Git и Composer.

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

  • Единая история изменений и атомарные коммиты
  • Простота рефакторинга общих зависимостей
  • Унифицированные инструменты и конфигурации
  • Упрощение CI/CD через единый пайплайн

Структура монорепозитория: выбираем правильную архитектуру

Ключевой элемент успешного монорепозитория — продуманная структура папок. Для PHP-проектов мы рекомендуем следующий шаблон, основанный на разделении на пакеты и приложения:

monorepo/
├── apps/                   # Готовые приложения (веб-сайты, API, консольные скрипты)
│   ├── api/                # REST API на Slim или Laravel
│   │   ├── public/
│   │   ├── src/
│   │   └── composer.json
│   └── admin/              # Панель администратора
│       ├── public/
│       ├── src/
│       └── composer.json
├── packages/               # Многоразовые PHP-пакеты (библиотеки)
│   ├── core/               # Ядро системы: базовые классы, исключения
│   │   ├── src/
│   │   └── composer.json
│   ├── users/              # Пакет для управления пользователями
│   │   ├── src/
│   │   └── composer.json
│   └── payments/           # Платежный пакет
│       ├── src/
│       └── composer.json
├── tools/                  # Сборочные и вспомогательные скрипты
├── tests/                  # Интеграционные и E2E-тесты для всего проекта
├── composer.json           # Корневой манифест (обычно "type": "project")
├── composer.lock
├── phpstan.neon
├── phpunit.xml.dist
└── .gitignore

Каждый пакет в packages/ — это самостоятельная Composer-библиотека со своим composer.json. Приложения в apps/ подключают эти пакеты как локальные зависимости. Такая структура позволяет:

  • Изолировать логику и тестировать пакеты независимо
  • Использовать строгую типизацию и автозагрузку PSR-4
  • Переиспользовать код между разными приложениями без копирования

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

Composer — основной инструмент управления зависимостями в PHP. Для работы с монорепозиторием мы используем два ключевых механизма: локальные пути (path) и корневой манифест.

Шаг 1: Корневой composer.json

В корне проекта создаем минимальный манифест, который определяет, что это монорепозиторий:

{
    "name": "mycompany/monorepo",
    "type": "project",
    "require": {
        "php": "^8.1"
    },
    "autoload": {
        "psr-4": {
            "MyCompany\\": "packages/"
        }
    },
    "scripts": {
        "test": "phpunit",
        "phpstan": "phpstan analyse"
    }
}

Шаг 2: Локальные зависимости в пакетах

Каждый пакет в packages/users/ должен иметь свой composer.json. Например:

{
    "name": "mycompany/users",
    "type": "library",
    "require": {
        "php": "^8.1",
        "mycompany/core": "*"
    },
    "autoload": {
        "psr-4": {
            "MyCompany\\Users\\": "src/"
        }
    }
}

Обратите внимание: пакет mycompany/core указан как зависимость. Чтобы Composer понимал, что это локальный пакет, мы добавляем в корневой composer.json секцию repositories:

{
    "repositories": [
        {
            "type": "path",
            "url": "packages/*"
        }
    ]
}

После этого выполняем composer update — Composer создаст символические ссылки на локальные пакеты. Это значит, что изменения в packages/core/src/ сразу отразятся во всех приложениях.

Шаг 3: Подключение пакетов в приложениях

Теперь в приложении apps/api/composer.json мы можем подключать локальные пакеты как обычные зависимости:

{
    "name": "mycompany/api",
    "require": {
        "mycompany/users": "*",
        "mycompany/payments": "*"
    }
}

При запуске composer install в корне проекта все зависимости будут разрешены через локальные пути.

Работа с Git и CI/CD в монорепозитории

Монорепозиторий требует особого подхода к управлению версиями и автоматизации. Рассмотрим практические рекомендации.

Стратегия веток и коммитов

Используйте Trunk-based development или Git Flow с префиксами для указания области изменений:

  • feat/core-add-validator — новая функциональность в пакете core
  • fix/api-login-error — исправление ошибки в приложении api
  • refactor/payments-structure — рефакторинг пакета payments

Атомарные коммиты должны затрагивать только один пакет или приложение. Это упрощает код-ревью и откат изменений.

CI/CD пайплайн с фильтра

Блоги

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