一、这一课在整个后端体系中的位置
如果说:
-
第9课解决的是:系统如何稳定地“失败”
-
那第10课解决的是:系统如何“正确地被调用”
👉 第9课是底座(Infrastructure)
👉 第10课是门面(Interfaces)
Controller 所在的层,不是业务层,而是:
协议适配层(Protocol Adapter Layer)
就像 Android 的 Activity / ViewModel:
负责接输入、做校验、转模型、调业务、给输出。
二、Controller 层的唯一定位(一句话定生死)
👉 Controller 只做五件事:
接请求 → 校验 → 转换 → 调用 → 返回
除此之外,一律禁止。
三、工程目录规范(可直接用)
interfaces
└── http
├── controller // 接口入口
├── dto
│ ├── request // 请求模型
│ └── response // 返回模型
└── assembler // DTO ↔ 内部模型转换
配合第9课:
infrastructure/common
├── result
├── exception
├── error
├── web
👉 这两块,就是“接口系统”的全部骨架。
四、Controller 层允许 & 禁止清单(工程红线)
✅ Controller 允许做的
- 接收 HTTP 参数
- 参数校验
- DTO ↔ Command / Query 转换
- 调用 Application Service
- 返回 Result
❌ Controller 严禁做的
- 写业务 if/else
- 控事务
- 捕获业务异常
- 调 Repository
- 拼复杂领域对象
- 处理状态流转
👉 一句话:Controller 脏,系统必烂。
五、参数校验:接口质量第一道防线
1️⃣ 请求 DTO(只为接口存在)
public class RegisterReq {
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\\\d{9}$", message = "手机号格式不正确")
private String phone;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 20, message = "密码长度必须 6-20 位")
private String password;
}
👉 DTO 只服务协议,不参与业务。
2️⃣ Controller 开启校验
@PostMapping("/users")
public Result<UserDTO> register(@Validated @RequestBody RegisterReq req) {
…
}
参数非法 → ValidationException → 第9课异常体系。
六、Assembler:接口边界隔离器
Assembler 的作用只有一个:
👉 隔离“接口模型”和“系统模型”
防止 HTTP 把你的系统结构污染掉。
Request → Command
public class UserAssembler {
public static RegisterUserCommand toCommand(RegisterReq req) {
RegisterUserCommand cmd = new RegisterUserCommand();
cmd.setPhone(req.getPhone());
cmd.setPassword(req.getPassword());
return cmd;
}
}
Domain → Response DTO
public static UserDTO toDTO(User user) {
UserDTO dto = new UserDTO();
dto.setId(user.getId());
dto.setPhone(user.getPhone());
return dto;
}
👉 这一步,相当于 Android 的:
- VO ↔ Entity
- NetworkModel ↔ UIModel
七、标准接口最小闭环(工程模板)
① Request DTO
RegisterReq
② Command(application 层)
RegisterUserCommand
③ Controller
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public Result<UserDTO> register(@Validated @RequestBody RegisterReq req) {
RegisterUserCommand cmd = UserAssembler.toCommand(req);
UserDTO dto = userAppService.register(cmd);
return Result.ok(dto, RequestId.get());
}
}
④ Application Service
public UserDTO register(RegisterUserCommand cmd) {
// 业务编排(事务、规则、领域调用)
}
👉 Controller 到此为止,不往下越界。
八、Controller 与第9课异常体系的协作
Controller 层只处理两种“失败”:
- 参数非法(校验异常)
- 协议错误(404 / 405 / JSON 错误)
其余:
- 业务失败 → BizException
- 系统失败 → Exception
全部由 GlobalExceptionHandler 兜底。
👉 Controller 无 try-catch。
九、第10课完成标准(项目自检表)
- ✅ Controller 中无业务 if/else
- ✅ Controller 不出现 Repository
- ✅ 所有接口参数都有 DTO
- ✅ DTO 不复用 Entity
- ✅ 所有接口都有校验
- ✅ 所有转换在 Assembler
- ✅ 所有异常交给第9课
十、这一课对你意味着什么
从这一课开始,你已经不再是:
❌ 写接口的人
而是在做:
✅ 接口系统设计
✅ 协议边界治理
✅ 工程规范制定
✅ 质量防线构建
这是从“能写功能”迈向“能控系统”的标志。
十一、结尾总结
Controller 不是业务层,
而是系统的“前台大厅”。
大厅越干净,系统越稳定;
边界越清晰,系统越可控。




