DDD:是否存在无法使用最终一致性的情况

发布于 2025-01-10 13:51:58 字数 469 浏览 0 评论 0原文

想象一下您正在开发时间跟踪软件。两位不同的经理在考勤表的不同列中添加员工的工作时间。两位经理分别在不同的两天增加了 8 个小时。但时间表已经是 32 小时,不应超过 40 小时(这是我们的新业务规则)。现在,这两种情况都会从数据库中获取时间表还剩 32 小时的信息。当顶部操作实际完成添加额外的 8 小时工作时,另一个操作已经获取了时间表的状态…32 小时工作。将会发生的事情是两者都会成功。而且,我们的时间表还剩 48 小时!
我可以通过使用方法 addHour(int hours, Enum Day) 将 Fryday 工作时间和周一工作时间移至单个聚合来解决此问题,该方法将检查总时间,或者我可以将 Fryday、周一和 Employee 设为单独的聚合。当星期一经理决定增加工作时间时,员工将收到事件 addHours,该事件将检查总工作时间,并在总工作时间不超过 40 时发回事件 HoursAdded 事件或 HoursNotAdded 事件。然后,周一聚合将处理该事件并将小时数添加到他的总小时数中。

Imagine you were working on time-tracking software. Two different managers are adding time worked for an employee on different columns on a time-sheet. The two managers are each adding 8 hours to two different days. But the time-sheet is already at 32 hours and should not go over 40 hours (that’s our new business rule). Right now, both cases will fetch from the database that the time-sheet has 32 hours left. By the time the top operation finished actually adding the additional 8 hours worked, the other operation has already fetched the state of the time-sheet…32 hours worked. What will happen is that both will succeed. And, we are left with 48 hours on a time-sheet!
I can solve this problem by moving Fryday working hours and Monday working hours into a single aggregate with method addHour(int hours, Enum Day) which will check the total hours or I can make Fryday, Monday, and Employee a separate aggregate. When the Monday manager decides to add hours the employee will receive an event addHours which will check the total hours and send back an event HoursAdded event if the total hours do not exceed 40 or HoursNotAdded event. Then the Monday aggregate will handle the event and add hours to his total hours.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

双马尾 2025-01-17 13:51:58

您所描述的是对记录进行并发更新时出现的持久性问题。

要解决这个问题,您必须检测时间表的并发更新。您通常使用在时间表的持久性表示更新时更新的版本来执行此操作。如果自读取时间表后版本发生变化,则意味着其他人更新了它。在这种情况下,存储库可能会引发异常。

这种使用版本的技术称为乐观锁定

有几种方法可以处理这种乐观锁异常。您可以向用户显示一个对话框并重新加载数据,也可以重试用例并再次执行。如果您再次执行用例,它将从数据库重新加载时间表,从而看到其他人所做的更新。现在,由于用例无法更新时间,因此将引发域异常,因为要添加的时间将超过周限制(您的业务规则)。

由于版本号只是出于持久性原因,因此不应在域实体中对其进行建模。要跟踪版本号,存储库可以使用身份映射,以便它可以将域实体映射到其初始持久性状态。此身份映射的范围仅限于用例调用,以便您不会影响其他用例。有很多不同的方法来实现这个范围。例如,您可以使用 本地线程 或者使存储库有状态,这通常使使用case 有状态,因为它依赖于存储库。这里我无法深入探讨。

当您实现乐观锁定时,您应该确保版本检查和记录更新是原子操作。不然你还没有真正解决这个问题。如何实现乐观锁取决于你使用的数据库(SQL、NoSQL等)。您通常会在更新请求中传递版本和 ID 作为选择标准。

What you describe is a persistence issue that arises when concurrent updates are made to a record.

To solve it you must detect concurrent updates of the time sheet. You usually do this with a version that is updated when the time sheet's persistence representation is updated. If the version changed since the time sheet was read it means that someone else updated it. In this case the repository can raise an exception.

This technique using a version is called optimistic locking.

There are several ways to deal with such an optimistic lock exception. You can either show the user a dialog and reload the data or you can retry the use case and execute it again. If you execute the use case again it will reload the time sheet from the database and thus see the updates made by someone else. Now a domain exception will be raised since the use case can not update the hours, because the hours to add will exceed the week limt (your business rule).

Since the version number is only for persistence reasons it sould not be modeled in the domain entity. To keep track of the version number a repository can use an identity map so that it can map the domain entity to it's initial persistence state. This identity map is scoped to a use case invocation so that you do not affect other use cases. There are a lot of different ways to implement this scope. E.g. you could use a thread local or you make the repository stateful which usually makes the use case stateful since it has a dependency to the repository. I can't go into depth here.

When you implement optimistic locking you should ensure that the version check and the record update is an atomic operation. Otherwise you haven't really solved it. How to implement optimistic locking depends on the database you use (SQL, NoSQL, etc.). You usually pass the version and the id as selection criteria in your update request.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文