Skip to content

4.3 后端分层架构 ​

概述

后端采用"三层主干 + 三层支撑"架构:Controller → Logic → Model 构成主干链路,Validate、Service、Middleware 分别提供参数校验、通用能力、请求拦截等支撑。各层职责明确,层间通过方法调用串联,不越层访问。

架构全景 ​

text
┌─────────────────────────────────────────────────────────────────────┐
│                         支撑层                                      │
│                                                                     │
│  Validate(参数校验)   Service(通用能力)   Middleware(请求拦截)  │
│  app/validate/*.php    app/service/*.php     app/middleware/*.php   │
└──────────────────────────────┬──────────────────────────────────────┘
                               │ 被主干层调用
                               ▼
┌─────────────────────────────────────────────────────────────────────┐
│                         主干链路                                     │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ Controller(控制器层)                                        │   │
│  │ 接收请求 → 获取参数 → 调用 Logic → 封装响应                   │   │
│  │ 基类:BaseController | 文件:app/controller/*.php             │   │
│  └──────────────────────────┬──────────────────────────────────┘   │
│                             │ 调用 Logic 方法                       │
│                             ▼                                       │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ Logic(逻辑层)                                               │   │
│  │ 属性配置 → 参数校验 → 生命周期钩子 → 横切处理 → 调用 Model    │   │
│  │ 基类:BaseLogic / BaseTenantLogic | 文件:app/logic/*.php     │   │
│  └──────────────────────────┬──────────────────────────────────┘   │
│                             │ 调用 Model ORM                        │
│                             ▼                                       │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ Model(模型层)                                               │   │
│  │ 表映射 → 软删除 → 审计字段 → ORM → SQL                        │   │
│  │ 基类:BaseModel | 文件:app/model/*.php                       │   │
│  └──────────────────────────┬──────────────────────────────────┘   │
│                             │                                       │
│                             ▼                                       │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ Database(数据库)                                            │   │
│  └─────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘

各层职责详解 ​

Controller 层 ​

文件:app/controller/{Module}Controller.php基类:BaseController职责:HTTP 请求入口,只做"搬运"不做"判断"

职责说明示例
参数获取从请求中提取参数$this->getJsonBody()、$this->getParams()
调用 Logic委托业务逻辑$this->logic->add($data)
响应封装统一 JSON 格式返回$this->success()、$this->fail()
注解标注声明权限和日志#[Permission]、#[Log]

Controller 不应该做: 业务判断、数据库操作、文件处理、参数校验(特殊场景除外)。

php
class ArticleController extends BaseController
{
    protected ArticleLogic $logic;

    protected function initialize(): void
    {
        $this->logic = new ArticleLogic();
    }

    // 标准 CRUD — 一行调用
    #[Permission('sys:article:page', '文章分页')]
    public function page(): Json
    {
        return parent::_index($this->logic);
    }

    #[Permission('sys:article:add', '添加文章')]
    public function add(): Json
    {
        return parent::_add($this->logic, $this->getJsonBody());
    }
}

Logic 层 ​

文件:app/logic/{Module}Logic.php基类:BaseLogic(系统共享)/ BaseTenantLogic(租户隔离) 职责:业务核心,通过"属性配置 + 生命周期钩子"声明式 CRUD

职责方式说明
行为定义属性配置$pageLikeFields、$uniqueFields、$validateClass 等
业务定制生命周期钩子beforeAdd、afterUpdate、beforeDelete 等
自动校验$validateClassadd/update 时自动调用验证器
横切处理属性驱动文件字段、富文本、枚举映射、数据权限、租户隔离

Logic 不应该做: 直接 HTTP 交互、直接返回 JSON 响应。

php
class ArticleLogic extends BaseTenantLogic
{
    /**
     * 关联的模型类
     *
     * @var string
     */
    protected string $modelClass = Article::class;

    /**
     * 参数验证器类
     *
     * @var string
     */
    protected string $validateClass = ArticleValidate::class;

    /**
     * 需要自动处理的文件上传字段
     *
     * @var array
     */
    protected array $fileFields = ['cover'];

    /**
     * LIKE 模糊匹配字段
     *
     * @var array
     */
    protected array $pageLikeFields = ['title'];

    /**
     * 精确匹配字段
     *
     * @var array
     */
    protected array $pageEqFields = ['status', 'category_id'];

    /**
     * 唯一性校验字段
     *
     * @var array
     */
    protected array $uniqueFields = ['title'];

    /**
     * 枚举显示名映射
     *
     * @var array
     */
    protected array $serializeMaps = ['status' => 'article_status'];

    /**
     * 新增前处理:设置默认排序值
     *
     * @param array $data 待新增的数据
     * @return array 处理后的数据
     */
    protected function beforeAdd(array $data): array
    {
        $data['sort'] = $data['sort'] ?? 0;
        return $data;
    }
}

Model 层 ​

文件:app/model/{TableName}.php基类:BaseModel职责:数据持久化,只做表映射和自动维护

职责说明
表映射protected $name = 'article'
软删除全局查询范围自动过滤 is_delete=1
审计字段onBeforeInsert/onBeforeUpdate 自动填充
关联关系hasMany、belongsTo 等

Model 不应该做: 业务判断、复杂查询逻辑(交给 Logic)。

php
class Article extends BaseModel
{
    protected $name = 'article';

    // 关联关系(按需定义)
    public function category()
    {
        return $this->belongsTo(Category::class, 'category_id');
    }
}

Validate 层 ​

文件:app/validate/{Module}Validate.php基类:think\Validate职责:参数格式校验,定义规则 + 场景

php
class ArticleValidate extends Validate
{
    protected $rule = [
        'title' => 'require|max:200',
    ];
    protected $message = [
        'title.require' => '文章标题不能为空',
    ];
    protected $scene = [
        'add'    => ['title'],
        'update' => ['title'],
    ];
}

触发方式: Logic 配置 $validateClass 后,add/update 自动调用。

Service 层 ​

文件:app/service/{Name}Service.php职责:跨模块通用能力,无状态、无副作用

服务能力
DictService字典查询(两级缓存)
PasswordService密码加密(bcrypt 双重加盐)
JwtServiceToken 生成/刷新/解析
ExcelServiceExcel 导入导出
CaptchaService图片验证码

Service 不应该做: 持有状态、直接操作数据库(通过 Model 静态方法例外)。

Middleware 层 ​

文件:app/middleware/{Name}Middleware.php职责:请求拦截,在 Controller 前后执行

text
CorsMiddleware → AuthMiddleware → TenantMiddleware → DemoMiddleware → LogMiddleware → Controller
中间件作用注入数据
AuthMiddlewareJWT 认证 + 权限校验$request->userInfo
TenantMiddleware租户上下文$request->tenantId
DemoMiddleware演示环境拦截—
LogMiddleware操作日志记录—

各层方法对照表 ​

分页查询链路 ​

层方法输入输出
Controller_index($logic)HTTP 请求参数Json 响应
LogicpageList($params, $where, $with)参数数组['records'=>[], 'total'=>int, ...]
ModelModel::page($current, $size)->select()分页条件Collection

新增链路 ​

层方法输入输出
Controller_add($logic, $data) 或 $this->logic->add($data)Json 请求体Json 响应
Logicadd($data)关联数组int(新记录 ID)
ModelModel::create($data)关联数组Model 实例

Logic::add 内部流程:beforeAdd → validateData → camel_to_snake → processTenantId → processFile → processContent → filterTableFields → checkUnique → Model::create → afterAdd

修改链路 ​

层方法输入输出
Controller_edit($logic, $id, $data)id + Json 请求体Json 响应
Logicupdate($id, $data)id + 关联数组bool
ModelModel::find($id)->save($data)模型实例 + 关联数组bool

删除链路 ​

层方法输入输出
Controller_remove($logic, $id)idJson 响应
Logicdelete($id)idbool
Model$model->save(['is_delete' => 1])软删除标记bool

数据流向 ​

请求方向(前端 → 后端 → 数据库) ​

text
前端请求(camelCase JSON)
    │
    ▼
BaseController::getJsonBody()       ← 获取原始参数
    │
    ▼
BaseLogic::beforeAdd($data)         ← 钩子:预处理
    │
    ▼
BaseLogic::validateData($data)      ← 自动参数校验
    │
    ▼
BaseLogic::convertQueryParams()     ← camelCase → snake_case
    │
    ▼
BaseLogic::processFileFieldsOnSave  ← 文件字段处理
    │
    ▼
BaseLogic::filterTableFields()      ← 过滤非数据库字段
    │
    ▼
BaseLogic::checkUnique()            ← 唯一性校验
    │
    ▼
BaseModel::create()                 ← 审计字段自动填充
    │
    ▼
Database(snake_case)

响应方向(数据库 → 后端 → 前端) ​

text
Database(snake_case 数据)
    │
    ▼
BaseModel                           ← ORM 查询
    │
    ▼
BaseLogic 处理
    │ processFileFieldsOnRead()     ← 文件字段补全域名
    │ processContentFieldsOnRead()  ← 富文本占位符替换
    │ processSerializeMaps()        ← 枚举显示名补全
    │ afterDetail()                 ← 钩子:补充关联数据
    ▼
Result::convertData()               ← snake_case → camelCase
    │
    ▼
JSON 响应 → 前端

异常处理传播 ​

text
Model 层异常(如数据库连接失败)
    │
    ▼ 向上抛出
Logic 层异常(如 beforeDelete 抛出业务异常、validateData 抛出 ValidateException)
    │
    ▼ 向上抛出
Controller 层
    │ 方式一:try-catch 捕获,返回 fail()
    │ 方式二:不捕获,由 ExceptionHandle 统一处理
    ▼
ExceptionHandle::render()
    │ ValidateException   → {"code":1, "msg":"参数验证失败", "data":"..."}
    │ ModelNotFoundException → {"code":1, "msg":"数据不存在"}
    │ RuntimeException(401)  → {"code":1, "msg":"未授权"}
    │ 其他异常              → {"code":1, "msg":"操作失败"}
    ▼
JSON 响应

推荐做法:

  • Controller 通过 _add/_edit/_remove 调用时,不捕获异常,交给 ExceptionHandle
  • Controller 直接调用 $this->logic->add() 时,try-catch 捕获并返回 $this->fail()
  • Logic 层的业务异常(如 beforeDelete 校验)直接 throw new \Exception

完整请求示例 ​

以"新增文章"为例,展示一个请求从进入到返回的完整链路:

text
POST /api/article/add
Content-Type: application/json
Authorization: Bearer <token>

{"title": "新文章", "categoryId": 1, "status": 0}
text
1. CorsMiddleware        → 放行(设置跨域头)
2. AuthMiddleware        → 校验 Token → 注入 $request->userInfo
3. TenantMiddleware      → 解析租户 → 注入 $request->tenantId
4. LogMiddleware         → 记录日志(#[Log] 注解)

5. ArticleController::add()
   │ getJsonBody() → ["title"=>"新文章", "categoryId"=>1, "status"=>0]
   │
   ▼
6. ArticleLogic::add($data)
   │ beforeAdd($data)           → 设置默认 sort=0
   │ validateData($data, 'add') → 校验 title 必填 ✓
   │ camel_to_snake($data)      → ["title"=>"新文章", "category_id"=>1, "status"=>0]
   │ processTenantId($data)     → 加入 tenant_id
   │ processFileFieldsOnSave    → cover 无值,跳过
   │ filterTableFields($data)   → 过滤非表字段
   │ checkUnique($data)         → title 唯一 ✓
   │
   ▼
7. Article::create($data)
   │ onBeforeInsert            → 自动填充 create_user, create_time
   │
   ▼
8. Database INSERT

9. ArticleLogic::afterAdd($id, $data)  → 无额外操作

10. 返回 {"code":0, "ok":true, "msg":"添加成功", "data":{"id":42}}

层间通信规范 ​

规则说明
Controller → Logic✅ 允许,标准调用
Controller → Model❌ 禁止,越层
Logic → Model✅ 允许,标准调用
Logic → Service✅ 允许,调用通用能力
Logic → Controller❌ 禁止,反向依赖
Model → Logic❌ 禁止,反向依赖
Model → Service⚠️ 慎用,仅静态工具方法
Middleware → Controller✅ 通过 $request 注入数据
Validate → Logic❌ 禁止,只做格式校验

小蚂蚁云团队 · 提供技术支持