【问题标题】:Should a Repository throw an exception if no change is to be stored?如果不存储任何更改,存储库是否应该抛出异常?
【发布时间】:2017-10-24 08:03:16
【问题描述】:

在我的存储库合同规定期间,我开始怀疑任何Repository 的基本合同: 如果 Update 被一个不存储更改的实体调用会发生什么?

  1. 作为Repository 的客户,如果我想存储更改,我只会致电Update。如果没有变化,我会认为我的流程出了问题,所以我想知道这个事实。

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

  1. 但这里有另一种看法:Repository 对改变漠不关心Update 方法只是确保给定的实体像现在一样被持久化。 如果有变化,那不是Repository's 业务。所以没有例外。

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

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


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

【问题讨论】:

  • 您是否正在推出自己的变更跟踪模块?

标签: oop exception repository domain-driven-design design-by-contract


【解决方案1】:

如果使用不存储更改的实体调用 Update 会发生什么?

我认为你希望这是一个无操作。

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

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

此代码位于应用程序组件中;存储库和 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。如果模型已经满足后置条件,则无需进行任何更改。

这种模式是典型的idempotent receivers;例如,HTTP PUT 方法是为承诺 idempotent semantics 的操作保留的。

(更准确地说,您提出了一个precondition——调用者确保两个状态不同——调用该方法必须满足该条件。但在这种情况下,前提条件是一个幻像;方法could满足后置条件,只是选择不满足。)

【讨论】:

  • 同意。在检索数据时,我有时可能同时拥有Get() Find() 方法,其中Find() 可能返回nullGet() 会在找不到项目时抛出异常.另一方面,存储数据略有不同,我 100% 购买幂等点。在上面的示例中,任何 真正的 异常/不变量都可能更好地应用于assignToRoute() 方法。存储库可能不是实施规则的最佳场所。
  • 非常感谢您的回答,它给了我需要的最后提示,尤其是幂等性!另请参阅@guillaume31 的答案,我认为这个问题是多余的。
【解决方案2】:

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

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

在您真正期望更改的情况下,您可以考虑在更新之前单独检查是否有任何未提交的更改。

这很像如果文件在删除之前不存在,文件删除是否应该抛出的问题。在某些用例中,我真的希望该文件存在,所以如果它不存在,那将是例外,但是另一个用例是我想确保该文件不存在,但我不知道(或关心)它当前是否存在。在这种情况下,如果我考虑后置条件“在调用删除后指定的文件不存在”,那么(除非文件存在且不可删除)无论该文件之前是否存在,都已经满足。

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

必须在满足后置条件的非异常情况下捕获异常,这很烦人。如果有一个选项或不同的方法可以区分应该或不应该应用先决条件的情况,则代码的意图会更清楚,这可能比单独检查先决条件更有效/更容易。

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 是的,你是对的。考虑了一段时间后,我真的很感谢这个提醒!我知道存储库是内存中集合的错觉,但现在我真的明白为什么这如此重要了。
    猜你喜欢
    • 2020-09-08
    • 2020-04-15
    • 2023-03-13
    • 2012-02-26
    • 2011-12-17
    • 1970-01-01
    • 2015-01-29
    • 2023-03-08
    • 2019-05-15
    相关资源
    最近更新 更多