在 ThinkPHP 面试中,项目分层设计几乎是中高级岗位的必问题。很多候选人能说出“Controller 层、Service 层、Model 层”,但一旦追问“Service 层到底该放什么、不该放什么”,回答就开始模糊。本文从面试实战角度出发,系统梳理 ThinkPHP 项目的分层逻辑与 Service 层的核心职责。
为什么面试官爱问分层设计
分层设计看似是架构问题,实则考察的是候选人对代码可维护性、职责边界和业务复杂度的理解。一个项目如果所有逻辑都堆在 Controller 里,短期能跑通,长期必然失控。面试官想通过这个问题判断:
- 你是否写过真实的中大型项目
- 你能否清晰划分各层职责
- 你是否理解“高内聚、低耦合”在 PHP 工程中的落地方式
在 ThinkPHP 中,常见的分层包括:Controller 层、Service 层、Model 层、Validate 层,有时还会引入 Repository 层或 Logic 层。不同团队叫法不同,但核心思想一致。
各层职责的清晰边界
Controller 层:只做参数接收与响应
Controller 的职责应该非常“薄”,它只负责:
- 接收 HTTP 请求参数
- 调用 Validate 进行基础校验
- 调用 Service 层处理业务
- 返回统一格式的响应
Controller 中不应该出现复杂业务判断、数据库查询、事务控制。例如下面这种写法就是典型的反例:
public function createOrder()
{
$data = input('post.');
if (empty($data['user_id'])) {
return json(['code' => 1, 'msg' => '用户ID不能为空']);
}
$user = Db::name('user')->where('id', $data['user_id'])->find();
if (!$user) {
return json(['code' => 1, 'msg' => '用户不存在']);
}
// ... 大量业务逻辑
}
这段代码把校验、查询、业务判断全塞进了 Controller,后续无法复用,也难以测试。
Model 层:数据访问与关联定义
Model 层应聚焦于数据本身:表结构映射、关联关系、查询作用域、获取器与修改器。它不应该承载跨表的业务编排。例如“下单后扣库存、发优惠券、写日志”这类流程,不属于 Model 的职责。
Service 层:业务逻辑的核心载体
Service 层是分层的重心。它承接 Controller 传来的参数,编排 Model 完成业务,并对外返回结果。Service 层应该:
- 封装完整的业务用例
- 控制事务边界
- 协调多个 Model 或外部服务
- 保持与 HTTP 无关,可被命令行、队列、API 复用
Service 层的核心职责详解
1. 业务逻辑的封装与复用
Service 层最直接的价值是复用。同一个“创建订单”逻辑,可能被 Web 接口、后台管理、定时任务同时调用。如果写在 Controller 里,就无法复用;写在 Service 里,则天然支持多入口调用。
namespace app\service;
class OrderService
{
public function createOrder(array $data)
{
// 业务校验
// 库存扣减
// 订单写入
// 优惠券核销
}
}
2. 事务边界的控制
事务应该放在 Service 层,而不是 Controller 或 Model。因为一个业务用例往往涉及多张表,事务边界应与业务用例边界一致。
public function createOrder(array $data)
{
Db::startTrans();
try {
$order = OrderModel::create($data);
InventoryModel::where('id', $data['goods_id'])->dec('stock')->update();
Db::commit();
return $order;
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
}
面试中如果能主动提到“事务放在 Service 层”,往往是加分项。
3. 协调多个 Model 与外部服务
复杂业务通常需要多个数据源协作。Service 层负责编排这些调用顺序,并处理异常。例如下单需要:查用户、查商品、扣库存、生成订单、发消息。这些步骤的先后与失败回滚策略,都属于 Service 的职责。
4. 保持与框架解耦
优秀的 Service 层应尽量少依赖 ThinkPHP 的请求上下文(如 input()、request())。参数通过方法传入,返回值是纯数据。这样 Service 可以被单元测试,也能被其他入口调用。
常见面试追问与回答思路
追问一:Service 层和 Model 层如何划分?
回答思路:Model 管“数据怎么存怎么取”,Service 管“业务怎么做”。如果一段逻辑只涉及单表 CRUD,可以放 Model;一旦涉及多表协作、事务、外部调用,就应上移到 Service。
追问二:Service 层要不要返回统一格式?
建议 Service 返回业务数据或抛出业务异常,由 Controller 统一包装成 JSON。这样 Service 不关心响应格式,职责更纯粹。
追问三:简单项目有必要分 Service 吗?
可以坦诚回答:极简项目可以不分,但一旦业务增长,重构成本很高。分层的意义在于应对变化,而不是为了分层而分层。
总结
在 ThinkPHP 面试中,分层设计考察的是工程思维。记住三个要点:
- Controller 薄,只做参数与响应
- Model 纯,只做数据访问
- Service 厚,承载业务、事务与编排
能把这三点讲清楚,并结合实际项目举例,就能在面试中展现出扎实的架构能力。
未经允许不得转载:任鹏个人博客 » ThinkPHP 面试精讲:项目分层设计与 Service 层职责

