Java 请求与领域校验
校验应放在哪一层:输入边界校验与领域不变量如何分工。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
Java 请求与领域校验
这个概念为什么出现
不可信输入是常态。校验失败应快速、明确;领域不变量应在对象出生与状态迁移时守住,而不是只靠数据库报错。
[!abstract] 一句话理解 边界校验形状与权限;领域校验业务不变量;两者都不可只信客户端。
最小可运行示例
场景:下单命令校验
public record PlaceOrder(String skuId, int qty) {
public PlaceOrder {
if (skuId == null || skuId.isBlank()) throw new IllegalArgumentException("skuId");
if (qty <= 0 || qty > 100) throw new IllegalArgumentException("qty");
}
}
public class PlaceOrderValidationDemo {
public static void main(String[] args) {
try {
new PlaceOrder("", 1);
} catch (IllegalArgumentException ex) {
System.out.println("reject: " + ex.getMessage());
}
System.out.println(new PlaceOrder("S1", 2));
}
}
javac PlaceOrderValidationDemo.java && java PlaceOrderValidationDemo
期望含:
reject: skuId
PlaceOrder[skuId=S1, qty=2]
结合场景再看三个关注点
- 构造期守不变量。
- 错误消息可映射到 API 字段。
- Bean Validation 注解是工程化选项,不取代领域规则。
核心概念与准确模型
1. 分层
| 层 | 校验什么 |
|---|---|
| 控制器 | 类型、必填、格式 |
| 应用服务 | 用例前置条件 |
| 领域 | 深度不变量 |
| 数据库 | 最后防线约束 |
2. Bean Validation
jakarta.validation 注解与校验器(框架集成)。
边界情况与反直觉行为
- 部分更新(PATCH)校验不同。
- 国际化消息。
- 校验与事务顺序。
常见误区
[!warning] 常见误区:只做前端校验 可被绕过。
工程实践
- 统一错误响应结构。
- 复杂规则单测。
- 数据库约束双保险。
- 避免校验逻辑复制三份却不一致。
本节总结
- 边界 + 领域双层
- 失败要明确
- 框架注解是工具
自测题
- 为什么领域层仍要校验?
- 校验失败用 400 还是 422?
参考答案
- 用例可能被多种入口调用,不变量必须在核心守住。
- 团队约定;关键是稳定且文档化(常 400 覆盖校验失败)。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Bean Validation | 规范/社区 | 注解校验 |