引言:为什么在PHP中使用单一代码库?
\n开发复杂的PHP应用程序(CRM、SaaS、框架)常常面临一个难题:是将所有代码存储在一个仓库(单一代码库)中,还是拆分成多个微服务和包。使用PHP的单一代码库(monorepo)是一种策略,它将多个相关项目(库、模块、应用程序)放在同一个Git仓库中。
\n在实践中,这带来了以下好处:
\n- \n
- 统一的变更历史 — 更容易追踪一个包中的变更如何影响另一个包。 \n
- 简化的CI/CD配置 — 一个流水线即可构建和测试所有组件。 \n
- 减少重复 — 共享配置、脚本和依赖项。 \n
- 快速迭代 — 无需将包发布到Packagist即可进行本地测试。 \n
然而,单一代码库需要纪律。如果没有正确的代码组织,它会变成“意大利面条式单体”。在本文中,我们将探讨在单一代码库中组织PHP项目的实际步骤。
\n\n1. 单一代码库的目录结构
\n关键原则是清晰地区分包和应用程序。我们以典型架构为例:
\n\nproject-root/\n├── apps/\n│ ├── web/ # Web应用程序 (Symfony/Laravel)\n│ │ ├── public/\n│ │ ├── src/\n│ │ └── composer.json\n│ └── api/ # API应用程序 (Lumen/Slim)\n│ ├── public/\n│ ├── src/\n│ └── composer.json\n├── packages/\n│ ├── core/ # 核心(通用接口、实体)\n│ │ ├── src/\n│ │ └── composer.json\n│ ├── billing/ # 支付模块\n│ │ ├── src/\n│ │ └── composer.json\n│ └── notifications/ # 通知模块\n│ ├── src/\n│ └── composer.json\n├── tools/ # 开发脚本 (PHP-CS-Fixer, PHPStan)\n├── composer.json # 根清单文件\n└── phpstan.neon # 通用分析配置\n\n请注意:packages/中的每个包都是一个完整的Composer库,拥有自己的composer.json。这允许隔离依赖项并单独测试它们。
core包的composer.json示例
\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. 通过Composer管理依赖项
\n根composer.json是单一代码库的核心。它包含:
- \n
- 所有本地连接的包列表(通过
path仓库)。 \n - 整个项目的开发依赖项(PHPUnit、PHPStan、GrumPHP)。 \n
- 自动化脚本(静态分析、测试)。 \n
根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\n关键点:
\n- \n
- symlink: true