Service层需注册PSR-4命名空间并执行composer dump-autoload,依赖须通过构造函数类型提示注入,禁止new实例或方法内make,事务与数据操作应封装在动词命名的Service方法中,避免响应处理与字段转换。

Service 层不是加个目录就完事,不注册命名空间、不走容器注入、在控制器里 new 实例,等于白建——业务逻辑照样散落各处,复用和测试都成空谈。
Service 类放哪?怎么让 use appserviceUserService 不报错
ThinkPHP 6 默认不识别 app/service 目录,硬建文件会直接 Class not found。
- 在
composer.json的"autoload" → "psr-4"中追加:"app\service\": "app/service/" - 运行
composer dump-autoload(别漏这步,改了配置不重载等于没改) - 别塞进
app/common或app/library:前者语义模糊,后者会被当成工具类自动加载,容易和真正通用的Helper混淆
Service 构造函数怎么写?为什么不能在方法里 app()->make(PaymentService::class)
临时 make 看似灵活,实则绕过容器生命周期,单例失效、事务拦截器挂不上、AOP 全丢。
- 依赖必须声明在构造函数参数里,用类型提示:
public function __construct(private PaymentService $paymentService) - 强依赖(如
RefundService必须用到OrderService)也得提前声明,别等方法里才去取 - 如果依赖含请求上下文对象(比如
Request),要考虑是否该设为非单例——这时应改用工厂或方法内手动构建,而非全局单例
Service 方法命名和职责边界:哪些该放,哪些不该放
Service 不是“万能胶水”,它管数据访问与事务协调;规则堆叠、多条件校验、状态流转,得交给 Logic 层。
立即学习“PHP免费学习笔记(深入)”;
- 方法名用动词开头:
createOrder()、lockStockAndDeduct(),别写getOrderInfo()(那是模型的事) - 禁止在 Service 里调用
$this->redirect()或view():服务层不处理响应,只返回数据或布尔值 - 事务控制必须收口:比如下单含库存扣减+日志+消息推送,全包在
OrderService::createWithStockLock()里,控制器只调一次 - 别在 Service 里查完数据又自己做字段过滤或格式转换——这些该由上层(控制器或 Logic)决定,Service 只负责把原始、完整、结构化的数据交出去
最常被忽略的一点:Service 默认是单例,但一旦构造函数里注入了请求相关对象(比如 Request 或带 session 的 Cache 实例),就可能在并发下共享状态。这种场景下,光靠命名空间注册和构造注入还不够,得配合容器绑定策略或工厂模式来隔离实例生命周期。



















