从一次删除需求,理解 DDD 中的 Repository 职责

起因:一次“看起来很简单”的删除需求

最近在实习中负责一个设备模板相关的模块,有一个接口需求很简单:

删除一个模板属性。

属性表里有个字段叫 draft,表示是否是草稿状态,只有草稿属性才能被删除

听起来非常合理,对吧?


我的第一反应:那就 deleteById 吧?

一开始我写的代码大概是这样:

repository.deleteById(propertyId);

但很快我意识到不对——
如果属性不是草稿状态呢?

于是我改成了:

TemplateProperty property = repository.findById(propertyId)
        .orElseThrow(() -> new IllegalArgumentException("属性不存在"));

if (!property.isDraft()) {
    throw new IllegalStateException("只有草稿属性可以删除");
}

repository.deleteById(propertyId);

功能是没问题的,但我心里开始犯嘀咕:

🤔 JPA 的 deleteById 本身不是就会检查 ID 是否存在吗?
那我前面这次 findById 会不会多此一举?


疑惑出现:那是不是 Repository 就该支持“按条件删除”?

顺着这个思路,我甚至开始想:

要不我直接写个
deleteByIdAndDraftTrue(id)

这样不就一条 SQL 解决了吗?

但我越想越觉得哪里不对,却又说不上来。


我去问了 mentor

我把我的疑惑原封不动地问了 mentor,大概意思是:

Repository 不是应该“更智能一点”吗?
既然删除要判断条件,为什么不直接写进 Repository?

mentor 没有直接回答我,而是反问了我一句。


mentor 的反问,让我愣住了

他说:

“如果半年后,删除规则从
‘只能删草稿’
变成
‘草稿 + 创建人是自己 + 未被引用’,
你打算改哪一层代码?”

我当时愣了一下。


我才意识到:我把业务逻辑写错层了

如果我一开始写的是:

deleteByIdAndDraftTrue(id);

那后续规则变化就意味着:

  • Repository 方法签名要改
  • 所有调用点要改
  • 甚至 SQL 也要改

而这些规则,本质上是:

“一个属性,当前是否允许被删除”

这不是持久化问题,而是领域规则


Repository 到底该不该“聪明”?

mentor 给我的总结是:

Repository 要尽量“笨”一点。
它只负责数据存取,不负责业务决策。

于是正确的代码应该是:

TemplateProperty property = repository.findById(id)
        .orElseThrow(...);

property.assertDeletable();

repository.delete(property);

而不是:

repository.deleteByIdAndDraftTrue(id);

我总结的一条判断标准

后来我给自己总结了一个非常好用的判断标准:

“这个条件如果未来会变,它就不该出现在 Repository。”

  • 会变的 → 放领域 / 业务层
  • 不会变的(如 tenantId、软删除)→ 可以放 Repository

回头再看那段代码

现在再回头看最初那段代码,我反而觉得:

  • 多一次 findById 并不是浪费
  • 它是在明确表达业务语义
  • JPA 的一级缓存也保证了不会产生额外 SQL

写在最后

这次看似很小的删除逻辑,其实让我第一次真正理解了:

为什么“分层”不是为了好看,
而是为了让变化发生时,
你知道该改哪一层。

posted @ 2025-12-18 17:28  jiangyang1556  阅读(1)  评论(0)    收藏  举报