# 4.3 后端分层架构 :::tip 概述 后端按职责分为五层:契约(dto)、接口(handler)、业务(service)、数据(repository)、横切(middleware + hooks)。每层只做一件事,依赖单向向下。 ::: ## 分层与依赖方向 ```text middleware 横切处理(认证/租户/权限/日志/限流) │ 注入身份与上下文到 ctx ▼ handler 极薄委托(签名固定,反射注册) │ ▼ service 业务规则(ModuleSpec 声明 + 手写逻辑 + 事务) │ ▼ repository 数据访问(Ctx 入口注入软删/租户 Scope) │ ▼ gorm + db 连接(多方言驱动注册表 + 全局钩子盖章) ``` dto 被所有层引用(契约);utility 被任意层引用(无业务语义)。 ## 各层职责边界 | 层 | 做什么 | 不做什么 | |---|---|---| | dto | 请求/响应结构、binding/msg/form/json 标签、UnmarshalBody | 业务判断 | | handler | 委托调用、调用转换 | 参数解析、权限、规则 | | service | 业务规则、事务、错误上抛、ModuleSpec 声明 | HTTP 响应构造、裸 SQL 拼接 | | repository | 条件查询、软删除、存在性判断 | 业务条件决策 | | middleware | 认证、租户、鉴权、日志、限流、信封 | 业务逻辑 | ## 单例与调用约定 ```text service:var Xxx *sXxx(init 注册,全进程单例,方法必须并发安全) 跨模块:同包直调 —— User.Page(...)、repository.NewDeptRepo()(无需 import) 仓储:无状态(仅持表名),NewXxxRepo() 按需构造、可并发使用 ``` ## 事务边界 事务是 service 的职责: ```go db.WithTrans(ctx, func(ctx context.Context) error { // 回调内使用入参 ctx——它携带事务连接 // 内部所有 repository.Ctx(ctx) 自动加入事务 // 返回非 nil 错误整体回滚 }) ``` repository 无感知事务——它永远从 ctx 取连接,这是 ctx 贯穿设计的直接收益。 ## 错误传播 ```text repository 返回 gorm 错误 ↓ service 映射为业务错误(consts 哨兵 / apperr.Newf)或原样上抛 ↓ handler 不接触,透传 ↓ response 中间件统一映射为信封(apperr 按码输出,其余通用失败) ``` ## 横切的两个执行点 | 执行点 | 时机 | 承担什么 | |---|---|---| | 中间件链 | HTTP 请求级 | 身份、租户、权限、日志、限流、信封 | | GORM 全局钩子 | SQL 语句级 | 审计盖章、物理删除拦截 | | repository Scope | SQL 构建前 | del_flag=0、租户过滤 | 同一个红线(软删/租户)在读写两侧分别由 Scope 与钩子执行,业务代码零参与。 ## 模块文件规约回顾 一个模块固定四文件,写法见 [第 3 章 模块开发实战](/zh/guide/overview.html) 与 [12.4 模块开发规约](/zh/standards/module-code.html)。 ## 下一步 → [4.4 反射元数据驱动机制](/zh/architecture/bind-mechanism.html)