在 ddd 中,值对象应通过构造确保内在有效性;javax.validation 适用于外部输入校验阶段,而非嵌入值对象内部——验证应在创建时由外部协调器(如解析器、应用服务)执行,而非由值对象自身承担。
在 ddd 中,值对象应通过构造确保内在有效性;javax.validation 适用于外部输入校验阶段,而非嵌入值对象内部——验证应在创建时由外部协调器(如解析器、应用服务)执行,而非由值对象自身承担。
值对象(Value Object)的核心设计原则是不变性与自我验证:一旦创建,其状态必须合法且不可更改。因此,验证逻辑不应“延迟”到运行时通过反射校验字段,而应前置到构造过程中——即在 ofBytes() 等静态工厂方法内主动检查并抛出语义明确的异常。
以下是对原始 Size 类的重构建议(移除 @Min/@Positive 等声明式注解,改用防御性编程):
@Getter
@EqualsAndHashCode
@RequiredArgsConstructor(access = lombok.AccessLevel.PRIVATE)
public final class Size {
private final long bytes;
public static Size ofBytes(long bytes) {
if (bytes <p>✅ <strong>优势</strong>: </p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2870" title="Gradle 9.8.0"><img
src="https://img.php.cn/upload/manual/001/431/639/6ad9ce71d8cb6379.png" alt="Gradle 9.8.0" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2870" title="Gradle 9.8.0" class="overflowclass">Gradle 9.8.0</a>
<p class="overflowclass"></p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2870" title="Gradle 9.8.0" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 构造即校验,杜绝非法实例存在;
- 无运行时反射开销,类型安全;
- 异常语义清晰(IllegalArgumentException 表达业务约束违反);
- 符合 DDD “值对象必须始终有效”的建模契约。
⚠️ 关于 javax.validation 的正确定位:
它并非值对象的组成部分,而是面向外部输入的校验工具链一环。例如,在 Web 层接收 JSON 请求时:
@PostMapping("/documents")
public ResponseEntity> createDocument(@Valid @RequestBody DocumentRequest request) {
// 此处 @Valid 触发 javax.validation,校验 request 中的 size 字段(若为 DTO)
Size size = Size.ofBytes(request.getSizeInBytes()); // ✅ 安全校验后构造值对象
Document doc = Document.builder()
.name(Name.of(request.getName()))
.checksum(Checksum.of(request.getChecksum()))
.size(size)
.build();
return ResponseEntity.ok(repository.save(doc));
}
? 关键注意事项:
- 不要将 Validator 注入值对象:这违反了值对象的纯数据+行为内聚原则,也破坏了不可变性;
-
警惕数值溢出:ofKilobytes(1_000_000_000) 可能导致 long 溢出,建议在工厂方法中加入 Math.multiplyExact() 防护:
public static Size ofKilobytes(long kilobytes) { long bytes; try { bytes = Math.multiplyExact(kilobytes, 1024L); } catch (ArithmeticException e) { throw new IllegalArgumentException("Size overflow: " + kilobytes + " KB"); } return ofBytes(bytes); } - 区分信任边界:来自数据库或内部服务的 Size 实例默认可信(已通过领域规则保障),无需重复校验;仅对用户输入、API 请求、文件解析等外部边界输入启用 javax.validation 或自定义解析校验。
总结:值对象的验证是构造时的强制契约,而非运行时的可选检查。把校验逻辑下沉到工厂方法中,既符合 DDD 语义,又提升系统健壮性与可维护性。javax.validation 应作为基础设施层的辅助工具,服务于输入网关,而非污染领域模型。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










