Introduction: Why a Monorepository in PHP?
\nDeveloping complex PHP applications (CRM, SaaS, frameworks) often faces a dilemma: store all code in one repository (monorepository) or split it into many microservices and packages. A monorepo with PHP is a strategy where several related projects (libraries, modules, applications) reside in a single Git repository.
\nIn practice, this provides:
\n- \n
- Unified change history — easier to track how a change in one package affected another. \n
- Simplified CI/CD setup — a single pipeline for building and testing all components. \n
- Reduced duplication — shared configs, scripts, and dependencies. \n
- Fast iteration — no need to publish packages to Packagist for local testing. \n
However, a monorepository requires discipline. Without proper code organization, it turns into a "spaghetti monolith." In this article, we will cover practical steps for organizing a PHP project in a monorepository.
\n\n1. Monorepository Directory Structure
\nThe key principle is a clear separation into packages and applications. Let's look at a typical architecture example:
\n\nproject-root/\n├── apps/\n│ ├── web/ # Web application (Symfony/Laravel)\n│ │ ├── public/\n│ │ ├── src/\n│ │ └── composer.json\n│ └── api/ # API application (Lumen/Slim)\n│ ├── public/\n│ ├── src/\n│ └── composer.json\n├── packages/\n│ ├── core/ # Core (common interfaces, entities)\n│ │ ├── src/\n│ │ └── composer.json\n│ ├── billing/ # Billing module\n│ │ ├── src/\n│ │ └── composer.json\n│ └── notifications/ # Notifications module\n│ ├── src/\n│ └── composer.json\n├── tools/ # Development scripts (PHP-CS-Fixer, PHPStan)\n├── composer.json # Root manifest\n└── phpstan.neon # Common analysis settings\n\nNote: each package in packages/ is a full-fledged Composer library with its own composer.json. This allows isolating dependencies and testing them separately.
Example composer.json for the core package
\n{\n "name": "myproject/core",\n "type": "library",\n "autoload": {\n "psr-4": {\n "MyProject\\\\Core\\\\": "src/"\n }\n },\n "require": {\n "php": ">=8.1"\n },\n "extra": {\n "branch-alias": {\n "dev-main": "1.x-dev"\n }\n }\n}\n\n2. Managing Dependencies with Composer
\nThe root composer.json is the heart of the monorepository. It contains:
- \n
- A list of all locally connected packages (via
pathrepository). \n - Dev dependencies for the entire project (PHPUnit, PHPStan, GrumPHP). \n
- Automation scripts (static analysis, tests). \n
Example root composer.json
\n{\n "name": "myproject/monorepo",\n "type": "project",\n "repositories": [\n {\n "type": "path",\n "url": "packages/*",\n "options": {\n "symlink": true\n }\n }\n ],\n "require": {\n "myproject/core": "@dev",\n "myproject/billing": "@dev",\n "myproject/notifications": "@dev"\n },\n "require-dev": {\n "phpunit/phpunit": "^10.0",\n "phpstan/phpstan": "^1.10",\n "friendsofphp/php-cs-fixer": "^3.0"\n },\n "autoload": {\n "psr-4": {\n "MyProject\\\\": "packages/*/src/"\n }\n },\n "scripts": {\n "test": "phpunit",\n "analyse": "phpstan analyse",\n "cs-fix": "php-cs-fixer fix"\n }\n}\n\nKey points:
\n- \n
- symlink: true