service方法必须用动词命名,如createorder();禁止名词方法如getuserinfo();构造函数只注入强依赖,禁注request/session;禁用new、db::table()、手动拼响应;方法须面向业务动作而非http动作。

Service 方法名别用名词,必须是动词开头
看到 UserOrderService 里一堆 getUserInfo()、getOrderList(),基本就偏了——这已经不是 Service,是带业务前缀的 Model 封装。Service 的职责是“做事情”,不是“取数据”。动词命名能天然划清边界:createOrder()、refundAndNotify()、syncUserToCrm(),每个方法都对应一个明确的业务动作。
- 名词方法(如
getUserById())该交给 Model 或 Repository 层,Service 只在需要组合多个查询结果时才调用它们 - 如果一个方法只查一次数据库、不改状态、不调第三方、不涉及多模型协作,那它大概率不该出现在 Service 中
- 动词命名还能倒逼你拆分逻辑:把 “下单” 拆成
checkStock()+deductBalance()+recordLog(),比塞进一个placeOrder()更易测、可复用
构造函数里只注入强依赖,别塞 Request/Session/Cache 实例
Service 默认是容器单例,一旦你在构造函数里写 public function __construct(private Request $request),这个实例就会在并发请求间共享 $request 状态,轻则参数错乱,重则 session 混淆。这不是 bug,是设计误用。
- Request、Session、Cache(带上下文的)、Validate 实例都不该作为构造参数注入 Service
- 真正需要的依赖,比如
OrderService依赖StockService或PaymentService,才走构造注入 - 若某方法确实要读请求参数,让它通过方法参数传入数组或 DTO(如
createOrder(CreateOrderData $data)),保持 Service 无 HTTP 上下文
禁止在 Service 里手动 new 类、调用 Db::table()、拼接响应结构
这是判断 Service 是否退化为工具类的三道红线。一旦出现 new ExcelExporter()、Db::table('log')->insert()、return ['code'=>0, 'data'=>$result],说明它正在接管本该由其他层负责的事。
-
new实例破坏容器管理,导致事务拦截器失效、AOP 失效、单例失效 -
Db::table()是数据访问快捷方式,不是业务逻辑——应封装进 Model 或 Repository,Service 只调用$this->orderRepo->findById($id) - 响应结构(code/msg/data)是 Controller 层统一处理的事,Service 返回原始数据或抛异常即可
别让 Service 方法变成 Controller 的镜像副本
最隐蔽也最普遍的问题:把 Controller 里的 index()、save()、delete() 剪切粘贴到 Service,参数列表一模一样,只是加了个 use app\service\UserService。这没解耦,只是把面条代码从锅里捞进碗里。
- Controller 方法面向 HTTP 动作(GET /users、POST /users),Service 方法面向业务动作(activateUser()、migrateUserData())
- 一个 Controller 方法可能调用多个 Service 方法,也可能一个 Service 方法被多个 Controller(API/后台/命令行)共用
- 如果某个 Service 方法只被一个 Controller 的一个方法调用,且逻辑简单,不如直接删掉 Service,把逻辑上推到 Controller 或下沉到 Model
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











