Skip to content

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 负责查询条件

Powered by VitePress