不能靠 composer require 一键拆模块,因业务模块依赖框架上下文,强行发包会导致类找不到、容器绑定失败、开发效率骤降;应采用 path 仓库+psr-4 实现本地物理隔离,模块间通过接口契约通信,禁止直连 facade 或具体实现。

不能靠 composer require 把单体应用“一键拆模块”。真这么干,不出三天就会遇到 Class not found、IlluminateContractsContainerBindingResolutionException、本地改个逻辑要等 composer dump-autoload + 清缓存 + 重启队列,开发节奏直接崩掉。
为什么直接打包业务逻辑进 Composer 包会出问题
业务模块不是工具库,它活在框架上下文里——依赖 Request、Auth、数据库连接、中间件栈、配置文件、事件总线。强行发布为 Packagist 上可 require 的包,等于把运行时环境也打包了,结果就是:
- 每次改
src/OrderService.php都得composer update app/module-order,再清 Laravel 缓存,本地开发像在踩刹车 -
autoload-dev和主项目psr-4映射容易重叠,composer dump-autoload后类找不到是常态 - 模块里调用
config('app.name')或event(new OrderPlaced),一跑就报容器绑定失败——因为 vendor 里的包根本没注册到 Laravel 容器 - 测试时没法只加载“订单模块”,必须启动整个 Web 应用,CI 跑一次测试要 3 分钟起步
用 path 类型仓库 + PSR-4 命名空间实现物理隔离
真正能落地的模块化,是让模块代码放在 ./modules/order 这种本地路径下,不发包、不上传、不版本号管理,靠 Composer 的 path 仓库类型软链接进来,自动加载走 PSR-4,但生命周期完全由主应用掌控。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 在根目录
composer.json的repositories里加:{ "type": "path", "url": "./modules/order" } - 对应模块的
composer.json必须定义autoload:"autoload": { "psr-4": { "App\Modules\Order\": "src/" } } -
require项写成"app/module-order": "*",执行composer update后,AppModulesOrder就能直接 new 和 use,且修改./modules/order/src/下任意文件,无需 reload 或 dump - 模块内禁止出现
IlluminateSupportFacadesXXX,所有外部依赖(如库存服务)必须通过接口注入,比如InventoryServiceInterface,契约定义在app-contracts这类共享抽象包里
模块间通信必须靠接口契约,不能直连类或 Facade
一个模块调另一个模块的逻辑,如果写成 new AppModulesInventoryStockChecker() 或 InventoryFacade::check(),那模块就废了——耦合死,没法单独测试,也没法换实现。
- 所有跨模块调用,必须先定义 interface,放在独立的
app-contracts包里(它本身只含.php文件,无依赖) - 主应用的
AppServiceProvider中 bind 接口到具体实现:$this->app->bind(InventoryServiceInterface::class, function () { return new AppModulesInventoryStockChecker(); }); - 订单模块里只依赖
InventoryServiceInterface,构造函数注入或方法参数传入,运行时由容器解析——这样模块才能被替换成 Mock 实现做单元测试 - 别图省事在模块里写
app(InventoryServiceInterface::class),Laravel 的app()是全局容器门面,模块一旦脱离 Laravel 就失效
最常被忽略的一点:模块目录下的 composer.json 里不能写 require: {"laravel/framework": "^10.0"} 这类框架依赖。它会让 Composer 认为这是个完整应用,而不是一个可插拔组件。模块的 require 应该为空,或仅含 psr/log、psr/cache 这类标准接口——否则,你只是把巨石应用复制粘贴成了多个小石头。










