Service需严格遵循分层规范:必须通过容器注入、封装完整事务、动词命名、不处理响应、不依赖请求上下文,且注意单例生命周期问题。

适合,但必须满足几个硬性条件:Service 不是“写业务逻辑的地方”,而是“只写符合分层规范的业务逻辑”的地方。直接在 PurchaseController::store() 里写扣库存、发消息、记日志,再挪到 PurchaseService::confirmInbound() 里,不改结构、不抽接口、不控事务——这种“搬代码式 Service”毫无价值。
Service 类必须能被容器正确加载和注入
常见错误是建了 app/service/PurchaseService.php,却在 Controller 里写 new PurchaseService() 或 app()->make('PurchaseService'),结果类找不到、依赖没注入、事务拦截器失效。
- 在
composer.json的"autoload": {"psr-4": {...}}中显式注册命名空间:"app\service\": "app/service/" - 执行
composer dump-autoload(改完配置不运行这句等于白改) - Controller 构造函数中用类型提示注入:
public function __construct(private PurchaseService $purchaseService),禁止方法内手动app()->make()
Service 方法必须封装完整事务边界
比如采购入库涉及校验供应商、扣预付款、更新库存、生成财务凭证四个动作,它们要么全成功,要么全回滚。如果只把“更新库存”塞进 Service,其余还在 Controller 或 Model 里,事务就形同虚设。
将 .eth 名称解析为以太坊地址,反之亦然。适用于用户提供 .eth 名称(如“发送给 vitalik.eth”)、显示地址时展示 ENS 名称、查询 ENS 档案,或帮助注册、续期、管理 .eth 名称。
- 动词命名:用
confirmInbound($orderId),不用getInboundData()或inboundHandle() - 一个方法对应一个完整业务场景,跨模型操作、第三方调用、Db 写入都包进去
- 事务控制收口在 Service 方法内部:
Db::transaction(fn () => { ... }),别让 Controller 去套
Service 不能处理响应、不依赖请求上下文
看到 $this->redirect()、view()、Request::instance() 出现在 Service 文件里,基本可以判定分层已破。这类代码会导致单元测试无法运行、复用时抛出异常、并发下状态错乱。
- 禁止返回 HTML/JSON/Response 对象,只返回数组、对象或布尔值
- 禁止直接使用
input()、session()、cookie()等全局函数或 Facade - 若逻辑真需请求数据(如 IP、User-Agent),应由 Controller 提取后以参数传入,而非 Service 自行获取
最容易被忽略的是生命周期问题:Service 默认单例,但一旦构造函数里注入了带会话态的 Cache 或 Request 实例,就会在并发请求间共享状态。这时候光靠 PSR-4 注册和构造注入不够,得配合容器绑定策略或工厂模式做实例隔离。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










