You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用CQRS时过滤标记删除的聚合体该由哪一层承担职责

这个问题的核心判断标准是你的业务对「查询已停用聚合」的需求频率,没有绝对的通用答案,两种方案分别适配不同场景:


如果绝大多数业务场景仅需要返回启用状态的聚合

优先把软删除过滤逻辑放在仓储(持久化)层:

  • 符合仓储的封装原则:软删除是聚合的基础状态规则,作为聚合的持久化访问入口,仓储天然应该对上层屏蔽软删除的实现细节,避免上层每次调用查询都重复写过滤逻辑,大幅降低漏过滤的bug概率
  • 扩展性也可以得到保障:如果有少数管理端场景需要查询已停用聚合,只需要在仓储方法上加一个可选参数即可,比如:
public List<Article> allArticlesByUserId(long userId, boolean includeDeactivated) {
    String query = "select article from Article article where article.userId = :userId " 
        + (includeDeactivated ? "" : "and article.activated = true");
    // 其余查询逻辑
}

默认不传的情况下只查启用数据,特殊场景按需传参即可,完全不需要改动上层逻辑。


如果多业务端频繁需要灵活切换是否查询停用聚合

可以把过滤能力向上透出到应用层甚至API层:

  • 比如前台C端默认只返回启用内容,后台运营端需要支持查看全部/仅启用/仅停用内容,这种需求频繁出现的话,直接在API层透出onlyActivated参数会更灵活
  • 但要注意:过滤的具体实现逻辑依然要收口在仓储层,不要在应用层、Controller层做全量查询后再内存过滤,不仅性能差,还会导致逻辑分散难以维护。

示例优化建议

你给出的两个示例其实都有可优化的点:

  • 第一个示例在Controller层做分支调用两个不同的应用服务方法,完全没有必要,直接把onlyActivated参数透传给同一个应用服务方法,再下传给仓储即可,后续新增过滤参数也不会出现大量分支代码
  • 第二个示例把过滤条件写死为只查停用数据,相当于完全屏蔽了查询启用数据的可能性,后续有需求改动必须调整仓储方法,扩展性很差。

通用最佳实践

  1. 所有软删除相关的过滤逻辑统一收口到仓储层,上层仅按需传递参数
  2. 仓储查询方法默认仅返回启用状态的聚合,可选参数支持包含已停用聚合
  3. 应用层根据用例的受众决定是否透出参数到API:面向普通用户的用例隐藏参数,面向运营的管理端用例按需透出参数

内容的提问来源于stack exchange,提问作者chytonpide

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 21:06:04