DDD中Java值对象Size的javax.validation验证实现疑问
解决DDD值对象中JSR-380验证的正确姿势
你不需要在Size值对象内部持有Validator实例——因为ValidatorFactory是重量级资源,应该全局复用,而非每个值对象实例都维护一份。正确的做法是在值对象的工厂方法中触发验证,确保只有符合规则的实例能被创建。
修正后的Size值对象代码
import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import javax.validation.constraints.Positive; import java.util.Set; @Getter @EqualsAndHashCode @RequiredArgsConstructor(access = lombok.AccessLevel.PRIVATE) public class Size { // 全局复用的Validator实例,静态初始化一次 private static final Validator VALIDATOR; static { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); VALIDATOR = factory.getValidator(); } @Positive(message = "Size must be greater than zero") private final long bytes; public static Size ofBytes(long bytes) { Size size = new Size(bytes); validate(size); return size; } public static Size ofKilobytes(long kilobytes) { long bytes = kilobytes * 1024; Size size = new Size(bytes); validate(size); return size; } public static Size ofMegabytes(long megabytes) { long bytes = megabytes * 1024 * 1024; Size size = new Size(bytes); validate(size); return size; } // 封装验证逻辑,复用给所有工厂方法 private static void validate(Size size) { Set<ConstraintViolation<Size>> violations = VALIDATOR.validate(size); if (!violations.isEmpty()) { // 将约束违反转换为业务异常抛出,确保无效实例不会流出 throw new IllegalArgumentException(violations.iterator().next().getMessage()); } } }
关键说明
- 全局Validator实例:通过静态块初始化一次
Validator,避免重复创建ValidatorFactory(该对象创建成本高,不适合频繁实例化)。 - 工厂方法内验证:所有创建
Size的入口都先构建实例,再执行验证,确保只有合法的实例能被返回。 - 异常处理:验证不通过时抛出业务异常,阻止无效值对象进入领域层,符合DDD中值对象"不可变且始终有效"的原则。
对Document实体的影响
你的Document实体无需修改——因为Size的验证已经在创建阶段完成,当Document构建时传入的Size必然是合法的,无需在实体层重复验证。
内容的提问来源于stack exchange,提问作者Jordi
相关产品推荐
相关产品推荐

