确保交易与DDD的一致性


9

我从DDD开始,并了解使用聚合根来确保跨国一致性。我们不应在一项应用程序服务中修改多个聚合。

但是,我想知道如何处理以下情况。

我有一个称为“产品”的聚合根。

还有一个称为“组”的聚合根。

两者都有ID,并且可以独立编辑。

多个产品可以指向同一组。

我有一个可以更改产品组的应用程序服务:

ProductService.ChangeProductGroup(string productId, string groupId)

  1. 检查组存在
  2. 从仓库获取产品
  3. 设置组
  4. 将产品写回到存储库

我还有一个可以删除组的应用程序服务:

GroupService.DeleteGroup(string groupId) 1.从其groupId设置为提供的groupId的存储库中获取产品,确保计数为0或中止2.从组存储库中删除组3.保存更改

我的问题是以下情况,如果发生以下情况:

在ProductService.ChangeProductGroup中,我们检查该组是否存在(确实存在),然后在此检查之后,一个单独的用户(通过另一个GroupService.DeleteGroup)删除了productGroup。在这种情况下,我们为刚刚删除的产品设置了参考?

这是我设计中的缺陷吗?我应该使用其他域设计(如有必要,添加其他元素),还是必须使用事务处理?

Answers:


2

我们有同样的问题。

而且,除了在最坏的情况下在数据库中进行事务处理或进行一致性检查以获取异常之外,我无法解决这个问题。

您也可以使用悲观锁,为其他客户端阻止聚合根或仅阻止其部分,直到业务交易完成,这在某种程度上等效于可序列化交易。

大量使用的方式取决于您的系统和业务逻辑。
并发根本不是一件容易的事。
即使可以检测到问题,也将如何解决?只是取消操作还是允许用户“合并”更改?

我们默认使用Entity Framework,而EF6默认使用read_commited_snapshot,因此从存储库连续两次读取可能会给我们带来不一致的数据。当业务流程将被更清晰地概述并且我们可以做出明智的决策时,我们只是牢记这一点。是的,我们仍然像您一样在模型级别检查一致性。这至少允许与DB分开测试BL。

我还建议您考虑存储库的一致性,以防您进行“长期”业务交易。事实证明,这非常棘手。


1

我认为这不是一个特定于域驱动设计的问题,而是一个资源管理问题。

获取资源时必须简单地锁定它们。

当应用程序服务预先知道所获取的资源(Group在您的示例中为集合)将被修改时,这是正确的。在域驱动设计中,资源锁定是属于存储库的一个方面。


不,锁定不是必须的。实际上,对于长时间运行的事务而言,这是一件非常可怕的事情。最好使用乐观并发,每个现代ORM都支持开箱即用。乐观并发的伟大之处在于,只要它们支持原子更新,它也可以与非事务性数据库一起使用。
Aaronaught 2014年

1
我同意锁定长时间运行的事务的缺点,但是@ g18c所描述的方案确实暗示了相反的情况,即对单个聚合进行简短的操作。
rucamzu 2014年

0

您为什么不将产品ID存储在组实体中?这样,您只需要处理一个汇总即可,这使事情变得更加容易。

然后,您将需要实现某种类型的并发模式,例如,如果选择开放式并发,则只需将版本属性添加到Group实体,并在更新时版本不匹配时抛出异常,例如

将产品添加到组

  1. 检查产品是否存在
  2. 从存储库获取组(包括其版本ID属性)
  3. Group.Add(ProductId)
  4. 使用存储库保存组(使用新的版本ID更新并添加where子句以确保版本未更改。)

许多ORM都使用版本ID或时间戳内置了乐观并发性,但是很容易自己滚动。这是一篇有关如何在Nhibernate中完成操作的好文章,网址为http://ayende.com/blog/3946/nhibernate-mapping-concurrency


0

尽管事务可能会有所帮助,但是还有另一种方法,尤其是在您的情况仅限于少数几种情况下。

您可以在进行突变的查询中包括检查,从而有效地使每个检查与突变对成为原子操作。

产品更新查询内部加入(新引用的)组。如果该组不存在,这将导致它不进行任何更新。

删除组查询将连接指向它的所有产品,并具有附加条件,即连接结果(的任何一列)必须为null。如果有任何产品指向它,这将导致它不删除任何内容。

查询返回匹配或已更新的行数。如果结果为0,则说明您失去了竞争条件,但未执行任何操作。它可以引发异常,应用程序层可以根据需要进行处理,例如从头开始重试。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.