如果不存储更改,存储库是否应该抛出异常?

Should a Repository throw an exception if no change is to be stored?

在制定我的存储库合同的过程中,我开始怀疑任何 Repository 的基本合同: 如果使用不存储更改的实体调用 Update 会怎样?

  1. 作为 Repository 的客户,如果我想存储更改,我只会调用 Update。如果没有变化,我会假设我的过程中出了问题,所以我想知道这个事实。

所以我会说:未提交的更改是 Update 方法的先决条件。 如果不满足此先决条件,则需要 抛出 Exception

  1. 但这里有另一种观点:Repository无所谓改变Update 方法只是确保给定的实体像现在一样持久化。 如果有变化根本不是 Repository's 业务。 所以没有例外。

就个人而言,我倾向于查看 1,因为术语 Update 本身就表明发生了变化。

你对这个话题有什么看法?


PS:让我们假设(为了示例)不涉及并发,这当然会导致其他可能的结果。

如果您完全确定未提交的更改是先决条件,那么如果该先决条件未得到满足,您就有理由抛出异常。 如果这是先决条件,则在可能有更改或可能没有更改的情况下排除非异常调用更新。如果这就是你想要的,那好吧。

但是,正如您在第 2 点中暗示的那样,它并不是真正做了任何异常的存储库。

在确实需要更改的情况下,您可以考虑在更新之前单独检查是否有任何未提交的更改。

这很像删除前文件不存在是否抛出文件删除的问题。在某些用例中,我真的希望该文件在那里,所以如果它不存在,那将是例外,但是另一个用例是我想确保该文件不存在,但我不知道(或关心)它当前是否存在。在那种情况下,如果我考虑 post 条件 "the specified file does not exist after the call to delete" 那么(除非文件存在并且不可删除)无论文件之前是否存在,都已经满足。

更新post条件是调用后没有未提交的更改,更新成功和无更新情况都满足。

在满足 post 条件的这些非异常情况下必须捕获异常是很烦人的。如果有一个选项或不同的方法可以区分前提条件应该或不应该应用的情况,代码的意图会更清楚,这可能比单独检查前提条件更多efficient/easier。

What happens if Update is called with a an entity on which no change is to be stored?

我想你希望那是一个空操作。

使用持久性支持存储库的常用模式looks something like

final Cargo cargo = cargoRepository.find(trackingId);
cargo.assignToRoute(itinerary);
cargoRepository.store(cargo);

此代码存在于应用程序组件中;存储库和货物聚合根是允许域模型了解的抽象。实施细节在其他地方

谜语:如果 cargo.assignToRoute 对于当前状态是空操作,会发生什么情况? 应用程序 无法知道这一点,因为它无权访问基础状态。它调用了模型,模型决定不更改任何内容,因此存储库找到存储中可用的相同状态。

这可能很重要,因为如果您的消息是通过不可靠的传输方式(如网络)发送给您的,您可能会收到 同一消息的两份副本。如果领域模型识别出新行程与旧行程相同,则不需要更改任何内容。

final Cargo cargo = cargoRepository.find(trackingId);
cargo.assignToRoute(itinerary);
cargoRepository.store(cargo);
final Cargo cargo = cargoRepository.find(trackingId);
cargo.assignToRoute(itinerary);
cargoRepository.store(cargo);

在此流程中引入异常是否真的提供了业务价值?还是我们只是在为自己发明额外的工作?

我们真的要放置一堆额外的脚手架来检测以下无害的代码在运行时吗?

final Cargo cargo = cargoRepository.find(trackingId);
cargoRepository.store(cargo);

一种思考此处机制的方法:我们不调用模型使其任何事情。我们称领域模型是因为我们希望满足特定的 post condition。如果模型 已经 满足 post 条件,则无需进行任何更改。

这种模式是 idempotent receivers; for example, the HTTP PUT method is reserved for operations that promise idempotent semantics 的典型模式。

(更准确地说,您提出了一个 precondition —— 调用者确保两个状态不同 —— 必须满足才能调用该方法。但在这种情况下,前提条件是幻影; 方法 可以 满足 post 条件,它只是选择不太多。)

不要在您的存储库界面中包含 Update

存储库是内存中集合的幻觉(此处引用内存中的蓝皮书)。 Update(element) 在集合语义中不存在。

相反,在您的工作单元实施中处理更改跟踪。大多数 ORM 都有优化的内置更改跟踪,您可以方便地用于此目的。