Monorepositório com PHP: organização de código e melhores práticas
No desenvolvimento PHP moderno, a questão da organização eficiente do código está cada vez mais presente. A abordagem tradicional com múltiplos repositórios separados (multirrepositório) tem suas desvantagens: complexidade na sincronização de versões, duplicação de configurações e problemas com refatoração. Como alternativa, surge o monorepositório (monorepo) — uma estratégia onde todos os projetos, pacotes e bibliotecas relacionados são armazenados em um único repositório. Neste artigo, vamos explorar como organizar um monorepositório com PHP, quais ferramentas usar e quais armadilhas considerar.
O que é um monorepositório e por que ele é necessário para projetos PHP?
Um monorepositório é um único armazenamento de código que contém vários projetos ou pacotes. No mundo PHP, um exemplo clássico é o Laravel, que inclui o framework, pacotes para autenticação, filas, e-mail e outros componentes em um único repositório.
Principais vantagens para projetos PHP:
- Simplificação da refatoração — alterações em um pacote são imediatamente visíveis para todos os componentes dependentes, sem necessidade de publicar e atualizar versões de cada pacote separadamente.
- Integridade de versões — todos os pacotes usam a mesma versão de dependências, eliminando conflitos de tipos.
- CI/CD unificado — um único pipeline de build para todos os componentes, simplificando o teste de alterações entre pacotes.
- Melhoria na colaboração — desenvolvedores veem todo o código do projeto, facilitando code review e a manutenção de padrões de codificação uniformes.
No entanto, o monorepositório não é adequado para todos. Se seus projetos são fracamente acoplados ou têm ciclos de release diferentes, o multirrepositório pode ser mais conveniente. Para projetos PHP de médio e grande porte com integração estreita de componentes, o monorepositório geralmente se torna a escolha ideal.
Estrutura de um monorepositório em PHP
Uma estrutura adequada é a base de um monorepositório bem-sucedido. Vamos considerar uma organização típica usando um exemplo de projeto com um core, vários módulos e testes.
my-php-monorepo/├── packages/│ ├── core/│ │ ├── src/│ │ │ └── CoreService.php│ │ ├── tests/│ │ └── composer.json│ ├── module-auth/│ │ ├── src/│ │ ├── tests/│ │ └── composer.json│ └── module-payment/│ ├── src/│ ├── tests/│ └── composer.json├── apps/│ ├── api/│ │ ├── public/│ │ ├── src/│ │ └── composer.json│ └── console/│ ├── src/│ └── composer.json├── tests/│ └── IntegrationTest.php├── composer.json (raiz)├── phpunit.xml.dist├── .gitignore└── README.mdPrincípios-chave de organização:
- Separação em pacotes (packages) — cada pacote tem seu próprio
composer.jsoncom nome, autoload e dependências. - Aplicações (apps) — pontos de entrada (API, comandos de console) que reúnem os pacotes.
- composer.json raiz — contém a meta-configuração para trabalhar com o monorepositório.
Configuração do composer.json raiz
Para que o monorepositório funcione, é necessário configurar o composer.json raiz de modo que os pacotes fiquem disponíveis localmente, sem publicação no Packagist.
{ "name": "my-company/my-monorepo", "type": "project", "require": { "php": "^8.1", "my-company/core": "*", "my-company/module-auth": "*", "my-company/module-payment": "*" }, "autoload": { "psr-4": { "MyCompany\\\\": "packages/" } }, "repositories": [ { "type": "path", "url": "packages/*", "options": { "symlink": true } } ], "scripts": { "
```