A:
你这里的Specification跟聚合根里面的逻辑有啥不一样,为什么不直接在聚合根做这些业务,单独拎出来在 Specification做是为啥了Q:
你提出了非常好的问题!让我详细解释一下 Domain Specification 模式的设计理念和它与聚合根的区别。
1. Specification 与聚合根的职责区别
聚合根的职责
聚合根主要负责:
状态管理:维护自身的数据一致性
行为操作:如 publishPost(), cancelActivity(), registerUser()
业务规则验证:如 canCreateActivity(), canPublishContent()
领域事件发布:状态变更时发布事件
java
// 聚合根内的方法 - 操作和验证
public class UserAggregate {
public boolean canCreateActivity() {
return userEntity != null && userEntity.canCreateActivity();
}
public void banUser() {
if (userEntity != null) {
userEntity.banUser();
}
}
}Specification 的职责
Specification 主要负责:
查询条件封装:将复杂的查询逻辑封装成可复用的规格
组合查询:通过 and(), or(), not() 灵活组合
业务语义表达:用业务语言描述查询条件
解耦查询逻辑:避免在 Repository 或 Service 中硬编码查询条件
java
// Specification - 查询条件
public static ISpecification<UserAggregate> isActiveAndVerified() {
return isActive().and(isRealNameVerified());
}
// 使用场景
List<UserAggregate> users = repository.findBySpecification(
UserSpecifications.isActiveAndVerified()
.and(UserSpecifications.inCity("北京"))
.and(UserSpecifications.ageInRange(25, 35))
);2. 为什么需要 Specification?
场景 1:复杂查询组合
java
// ❌ 没有 Specification - 查询逻辑分散
public List<UserEntity> findRecommendedUsers(String city, int minAge, int maxAge) {
return userRepository.findAll().stream()
.filter(u -> u.getCity().equals(city))
.filter(u -> u.getAge() >= minAge && u.getAge() <= maxAge)
.filter(u -> u.getLastLoginTime().isAfter(LocalDateTime.now().minusDays(30)))
.filter(u -> Boolean.TRUE.equals(u.getIsVerified()))
.collect(Collectors.toList());
}
// ✅ 有 Specification - 查询逻辑可复用
public List<UserEntity> findRecommendedUsers(String city, int minAge, int maxAge) {
return userRepository.findBySpecification(
UserSpecifications.isRecommended() // 可复用
.and(UserSpecifications.inCity(city))
.and(UserSpecifications.ageInRange(minAge, maxAge))
);
}场景 2:业务规则复用
java
// 多个地方都需要"推荐用户"的定义
// ✅ Specification 确保定义一致
public static ISpecification<UserAggregate> isRecommended() {
return isActive().and(isRealNameVerified());
}
// 在不同场景复用
- 首页推荐用户列表
- 活动推荐参与者
- 匹配推荐搭子场景 3:两层过滤策略
java
// Infrastructure 层 - 数据库层面过滤(UserPoSpecs)
Specification<UserPO> dbSpec = UserPoSpecs.isNotDeleted()
.and(UserPoSpecs.isActive());
List<UserPO> userPOs = userRepository.findAll(dbSpec);
// Domain 层 - 业务逻辑过滤(UserSpecifications)
List<UserAggregate> aggregates = userPOs.stream()
.map(converter::poToAggregate)
.filter(UserSpecifications.isRecommended()::isSatisfiedBy) // 复杂业务规则
.collect(Collectors.toList());总结
Specification 不是简化业务,而是封装查询逻辑
聚合根负责操作,Specification 负责查询条件