Laravel 建议将验证(Validation)、授权(Authorization)、业务逻辑(Business Logic) 和响应格式化(Response Formatting) 分散到FormRequest、Policy、Service和Controller中,这正是单一职责原则(Single Responsibility Principle, SRP) 的直接体现。
一、单一职责原则(SRP)的核心
“一个类应该只有一个引起它变化的原因。”
—— Robert C. Martin (Uncle Bob)
- 如果一个类承担多个职责,任何一个职责的变化都可能破坏其他职责。
- 将职责分离 →每个类只因一个原因而修改→系统更稳定、更易维护。
二、Laravel 各组件的职责划分
| 职责 | 组件 | 职责说明 | 变化原因 |
|---|---|---|---|
| 验证输入 | FormRequest | 确保请求数据符合业务规则(如邮箱格式、唯一性) | 表单字段变更、验证规则调整 |
| 授权访问 | Policy | 判断当前用户是否有权操作资源(如“只能编辑自己的文章”) | 权限策略变更(如新增角色) |
| 执行业务逻辑 | Service | 实现核心业务用例(如“创建订单并扣库存”) | 业务规则变更(如新增促销逻辑) |
| 协调 HTTP 流程 | Controller | 接收请求 → 调用 Service → 返回响应 | API 路由变更、响应格式调整 |
✅每个组件只关注一个“变化轴”(Axis of Change)。
三、反例:所有逻辑挤在控制器中(违反 SRP)
// ❌ 违反 SRP 的控制器classPostControllerextendsController{publicfunctionupdate(Request$request,Post$post){// 1. 验证$data=$request->validate(['title'=>'required|unique:posts,title,'.$post->id,'content'=>'required|min:10',]);// 2. 授权if($post->user_id!==auth()->id()){abort(403,'Unauthorized');}// 3. 业务逻辑$post->update($data);if(str_contains($data['content'],'urgent')){event(newUrgentPostUpdated($post));}// 4. 响应格式化returnresponse()->json(['message'=>'Post updated','data'=>$post->fresh()]);}}❌ 问题:
- 四个修改理由:
- 要改验证规则?→ 改控制器
- 要改权限策略?→ 改控制器
- 要改业务逻辑?→ 改控制器
- 要改 API 格式?→ 改控制器
- 难以复用:
- 验证逻辑无法在命令行或队列中复用
- 业务逻辑无法在 API 和 Web 中共享
- 难以测试:
- 需模拟整个 HTTP 环境才能测试业务逻辑
四、正例:按 SRP 拆分职责
1.验证 →FormRequest
// app/Http/Requests/UpdatePostRequest.phpclassUpdatePostRequestextendsFormRequest{publicfunctionauthorize():bool{return$this->user()->can('update',$this->post);// ← 授权委托给 Policy}publicfunctionrules():array{return['title'=>'required|unique:posts,title,'.$this->post->id,'content'=>'required|min:10',];}}2.授权 →Policy
// app/Policies/PostPolicy.phpclassPostPolicy{publicfunctionupdate(User$user,Post$post):bool{return$user->id===$post->user_id;}}3.业务逻辑 →Service
// app/Services/PostService.phpclassPostService{publicfunctionupdate(Post$post,array$data):Post{$post->update($data);if(str_contains($data['content'],'urgent')){event(newUrgentPostUpdated($post));}return$post;}}4.协调 →Controller
// app/Http/Controllers/PostController.phpclassPostControllerextendsController{publicfunctionupdate(UpdatePostRequest$request,Post$post,PostService$service){$post=$service->update($post,$request->validated());returnresponse()->json(['message'=>'Post updated','data'=>$post]);}}五、SRP 带来的核心优势
✅ 1.每个类只有一个修改理由
| 组件 | 修改场景 | 不影响其他组件 |
|---|---|---|
UpdatePostRequest | 表单字段变更 | Policy、Service、Controller 不变 |
PostPolicy | 权限规则调整 | 验证、业务逻辑、响应不变 |
PostService | 业务规则变更 | 验证、授权、API 格式不变 |
PostController | API 响应结构调整 | 验证、授权、业务逻辑不变 |
✅ 2.高内聚、低耦合
- 内聚:每个组件内部逻辑高度相关(如
PostService只处理 Post 业务) - 耦合:组件间通过接口/契约交互(如 Controller 依赖
PostService接口)
✅ 3.可复用性
PostService可被 Web、API、命令行、队列复用PostPolicy可被控制器、Blade 模板、API 同时使用
✅ 4.可测试性
| 组件 | 测试方式 | 依赖 |
|---|---|---|
UpdatePostRequest | 单元测试验证规则 | 无 HTTP 依赖 |
PostPolicy | 单元测试权限逻辑 | 仅需 User/Post 对象 |
PostService | 单元测试业务逻辑 | 可 Mock 事件、仓储 |
PostController | 集成测试 HTTP 流程 | 无需关心内部实现 |
六、Laravel 如何支持 SRP?
| Laravel 特性 | 对 SRP 的支持 |
|---|---|
FormRequest | 自动生成验证/授权类,强制分离验证逻辑 |
Policy | 通过Gate系统集中管理授权逻辑 |
| 服务容器 | 通过依赖注入解耦 Service 与 Controller |
| 事件系统 | 将副作用(如发邮件)从核心逻辑中剥离 |
| API Resource | 将响应格式化从 Controller 中分离 |
💡 Laravel 不是“强制”你遵守 SRP,而是提供工具让遵守 SRP 成为最自然的选择。
七、何时可以违反 SRP?
| 场景 | 说明 |
|---|---|
| CRUD 极其简单 | 如User::create($request->all()),无复杂逻辑 |
| 原型开发 | 快速验证想法,后续再重构 |
| 一次性脚本 | 无长期维护需求 |
💡经验法则:
当一个方法开始包含“和”(如“验证和授权和业务逻辑”)
就应该拆分。
八、总结:SRP 是 Laravel 架构的基石
| 原则 | Laravel 实践 |
|---|---|
| 单一职责 | 每个组件只做一件事 |
| 关注点分离 | 验证、授权、业务、响应各司其职 |
| 可维护性 | 修改一个职责不影响其他 |
| 可演进性 | 业务复杂度增长时,架构自然扩展 |
🔚Laravel 的优雅不仅在于 API 简洁,更在于其对 OOP 原则的深刻践行。
通过将 SRP 内化到框架设计中,
它引导开发者写出结构清晰、易于维护、可长期演进的代码——
正如你所重视的:“通过合理抽象实现知识资产的自我增值”。