Introduction: What is a Monorepository and Why Does a PHP Developer Need It?
Developing large PHP projects often faces the problem of code organization. The classic approach with separate repositories for each component (libraries, microservices, frontend) leads to fragmentation, duplication of dependencies, and difficulties during refactoring. An alternative is a monorepository — a single repository containing all related projects and packages.
In this article, we will practically examine how to organize a monorepository for a PHP project. You will learn how to set up Composer, structure folders, manage dependencies, and organize CI/CD. The article is aimed at intermediate-level PHP developers familiar with the basics of Git and Composer.
Main advantages of a monorepository:
- Unified change history and atomic commits
- Ease of refactoring shared dependencies
- Unified tools and configurations
- Simplified CI/CD through a single pipeline
Monorepository Structure: Choosing the Right Architecture
The key element of a successful monorepository is a well-thought-out folder structure. For PHP projects, we recommend the following template based on separation into packages and applications:
monorepo/├── apps/ # Ready-made applications (websites, APIs, console scripts)│ ├── api/ # REST API on Slim or Laravel│ │ ├── public/│ │ ├── src/│ │ └── composer.json│ └── admin/ # Admin panel│ ├── public/│ ├── src/│ └── composer.json├── packages/ # Reusable PHP packages (libraries)│ ├── core/ # System core: base classes, exceptions│ │ ├── src/│ │ └── composer.json│ ├── users/ # Package for user management│ │ ├── src/│ │ └── composer.json│ └── payments/ # Payment package│ ├── src/│ └── composer.json├── tools/ # Build and auxiliary scripts├── tests/ # Integration and E2E tests for the entire project├── composer.json # Root manifest (usually "type": "project")├── composer.lock├── phpstan.neon├── phpunit.xml.dist└── .gitignoreEach package in packages/ is an independent Composer library with its own composer.json. Applications in apps/ include these packages as local dependencies. This structure allows you to:
- Isolate logic and test packages independently
- Use strict typing and PSR-4 autoloading
- Reuse code between different applications without copying
Setting Up Composer for Working with a Monorepository
Composer is the main dependency management tool in PHP. To work with a monorepository, we use two key mechanisms: local paths (path) and root manifest.
Step 1: Root composer.json
In the project root, create a minimal manifest that defines this as a monorepository:
{ "name": "mycompany/monorepo", "type": "project", "require": { "php": "^8.1" }, "autoload": { "psr-4": { "MyCompany\\\\": "packages/" } }, "scripts": { "test": "phpunit", "phpstan": "phpstan analyse" }}Step 2: Local Dependencies in Packages
Each package in packages/users/ must have its own composer.json. For example:
{ "name": "mycompany/users", "type": "library", "require": { "php": "^8.1", "mycompany/core": "*" }, "autoload": { "psr-4": { "MyCompany\\\\Users\\\\": "src/" } }}