service 层是解耦控制器、避免胖 controller 的常用手段,适用于多模型交互、外部 api 调用、事务逻辑或多端复用场景;应以动词命名、依赖注入、明确输入输出,避免权限校验与响应构造。

Service 层不是 Laravel 官方强制结构,但它是解耦控制器、避免胖 Controller 的最常用手段——用不用不重要,怎么用才关键。
什么时候该写 Service 类
当一个操作涉及多个 Model 交互、调用外部 API、含事务逻辑,或同一个业务在多处(如 API + 后台 + 队列)被复用时,就该抽成 Service。比如用户注册要创建 User、发邮件、记录日志、触发 Webhook,这些不该塞进 RegisterController@store 里。
- 常见错误现象:
App\Http\Controllers\Auth\RegisterController超过 200 行,store()方法里混着 DB 查询、HTTP 调用、条件判断和响应构造 - 别为了分层而分层:单查一个
User::find()就包一层UserShowService,反而增加跳转成本 - Service 类名建议动词开头,如
CreateOrderService、SyncUserWithCrmService,而非OrderService(后者易变成“万能工具类”)
Service 类怎么组织依赖和参数
Service 不该自己 new 依赖,也不该接收原始 Request 对象。它只处理业务逻辑,输入是明确的数据,输出是结果或异常。
- 参数尽量用数组或 DTO(如
class CreateOrderData),避免传$request或$user实例——前者耦合 HTTP 层,后者让测试难 mock - 依赖通过构造函数注入,比如需要发邮件就注入
MailerInterface,需要队列就注入Dispatcher,别在方法里直接调Mail::to() - 不要在 Service 里做权限检查(那是 Policy 或 Middleware 的事),也不返回 View 或重定向——它只管“做没做成”,不管“怎么展示”
示例:CreateOrderService 构造函数接收 OrderRepository 和 PaymentGateway,execute(array $data) 接收 ['user_id' => 123, 'items' => [...]],成功返回 Order 模型,失败抛 OrderCreationFailedException。
Service 和 Repository、Action 的边界在哪
Repository 只负责数据存取(getById、save),Service 协调多个 Repository 或外部服务;Action 是 Laravel 社区另一种轻量替代(如 app/Actions/CreateOrder.php),本质是无状态函数式类,适合简单场景。
- 容易踩的坑:把 Repository 当 Service 用——比如在
UserRepository里写发送欢迎邮件的逻辑,这违反单一职责 - 兼容性影响:Service 层本身不影响性能,但若每个方法都 new 一个 HTTP 客户端或未复用 DB 连接,会拖慢响应;建议用 Laravel 的容器自动解析 + 构造注入来保证单例复用
- 命名冲突提醒:
app/Services目录下别放UserService.php,它大概率会和app/Models\User.php或app/Http\Controllers\UserController.php引发语义混淆——改叫AssignUserRoleService或ImportUsersFromCsvService
测试 Service 类要注意什么
Service 是单元测试友好型结构,但前提是它不依赖容器、不硬编码 Facade、不读环境变量。
- 常见错误现象:测试时
Mail::fake()失效,因为 Service 里用了app(Mailer::class)或静态调用Mail::to() - 正确做法:所有外部依赖抽象为接口(如
interface PaymentProcessor),测试时用 Mockery 或 PHPUnit Mock 注入假实现 - 别测“有没有发邮件”,而要测“是否调用了
$this->mailer->sendWelcomeEmail($user)”,关注行为而非副作用
复杂点在于事务嵌套和事件监听——比如 CreateOrderService 中手动开启 DB::transaction(),又在保存后触发 OrderCreated 事件,而事件监听器也操作数据库。这时测试需注意:事务回滚是否影响事件执行、监听器是否被正确 dispatch。这类耦合往往比 Service 本身更难理清。









