Laravel控制器应仅处理请求接收、服务调用和响应返回;业务逻辑须移至按域划分的Service类,命名需动宾明确,超3个public方法须拆分;事务与异常由Service管理并抛出自定义异常,Controller仅转换HTTP状态码。

控制器只管“接请求、转调用、发响应”,其余逻辑全交给服务层——这是 Laravel 项目能长期维护下去的底线。
Controller 里只能出现这三类代码
真正该留在 Controller 中的,只有三件事:
- 接收
Request对象(或已验证的FormRequest) - 调用服务类方法,比如
$this->orderService->create($data) - 包装返回值,比如
response()->json(...)或view(...)
任何带 if 判断业务规则、手动拼 SQL、调用多个模型、触发事件、发通知、处理文件上传逻辑的代码,都算越界。哪怕只有一行,也建议立刻拆走。
Service 类命名和位置要直白可查
别叫 OrderHandleService 或 BusinessLogicV2,Laravel 不靠名字炫技,靠结构清晰:
- 目录统一放
app/Services/,按业务域再分,如app/Services/Order/、app/Services/User/ - 类名用动宾结构,比如
OrderCreationService、UserDeletionService,一眼知道它干啥 - 方法名必须是动作+名词,如
createWithInventoryCheck(),而不是doSomething()或process()
如果一个 Service 类里有超过 3 个 public 方法,大概率职责已经超载,该拆了。
什么时候该用接口 + 实现,而不是直接写类?
不是所有 Service 都需要接口,但以下情况必须加 interface:
- 这个逻辑未来可能被不同实现替换(比如支付网关:Stripe vs Alipay)
- 你正在写测试,需要 mock 行为(例如在单元测试中避免真实调用第三方 API)
- 服务会跨项目复用,且底层依赖可能变化(如缓存驱动从 Redis 换成 DynamoDB)
接口定义在 app/Contracts/Services/,实现类放在 app/Services/,然后在 AppServiceProvider 里绑定:$this->app->bind(UserServiceInterface::class, DatabaseUserService::class)。
最容易被忽略的边界:事务和错误恢复
很多人把事务写在 Controller 里,或者让 Service 把所有异常吞掉再返回 false —— 这两种做法都会让错误不可追溯。
正确姿势是:
- 事务控制权交由 Service 主导,比如
OrderCreationService::create()内部用DB::transaction() - Service 不捕获业务异常(如库存不足、余额不够),而是抛出明确的自定义异常,如
InsufficientStockException - Controller 只负责 catch 并转成对应 HTTP 状态码,比如
400 Bad Request或422 Unprocessable Entity
这个边界一旦模糊,日志里就只剩 “500 Internal Server Error”,没人知道到底是库存扣错了,还是用户没登录。


















