ThinkPHP 面试精讲:项目分层设计与 Service 层职责

在 ThinkPHP 面试中,项目分层设计几乎是中高级岗位的必问题。很多候选人能说出“Controller 层、Service 层、Model 层”,但一旦追问“Service 层到底该放什么、不该放什么”,回答就开始模糊。本文从面试实战角度出发,系统梳理 ThinkPHP 项目的分层逻辑与 Service 层的核心职责。

为什么面试官爱问分层设计

分层设计看似是架构问题,实则考察的是候选人对代码可维护性、职责边界和业务复杂度的理解。一个项目如果所有逻辑都堆在 Controller 里,短期能跑通,长期必然失控。面试官想通过这个问题判断:

  • 你是否写过真实的中大型项目
  • 你能否清晰划分各层职责
  • 你是否理解“高内聚、低耦合”在 PHP 工程中的落地方式

在 ThinkPHP 中,常见的分层包括:Controller 层、Service 层、Model 层、Validate 层,有时还会引入 Repository 层或 Logic 层。不同团队叫法不同,但核心思想一致。

各层职责的清晰边界

Controller 层:只做参数接收与响应

Controller 的职责应该非常“薄”,它只负责:

  1. 接收 HTTP 请求参数
  2. 调用 Validate 进行基础校验
  3. 调用 Service 层处理业务
  4. 返回统一格式的响应

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 层职责

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏