场景#1
- 情况:我需要对Web服务中的一些数据执行业务操作
- 解决方案:存储库使用Web服务作为存储库,使用Web服务数据构建聚合,并在以后保存更改
- 怀疑:这样可以吗
场景#2
- 情况:我需要对数据库中的一些数据执行业务操作,而这种操作涉及到用于计算值的Web服务
- 解决方案:Web服务被抽象为域服务,因此它作为参数传递给聚合以执行操作
- 怀疑:这有道理吗?我想说,Web服务应该是存储库的一部分,这样数据就可以一起持久化
场景#3
- 情况:业务操作从两个不同的数据库请求/保存数据,一个是直接访问的,另一个是通过Web服务访问的
- 解决方案:数据库连接和Web服务都在存储库中,它们被保存在一起。聚合不知道这个拆分
场景#4(问题开始…)
- 情况:我需要用数据库中的数据执行业务操作,这涉及到Web服务操作,这是业务操作本身所需要的结果
- 问题:Web服务不是事务性的,也不在我的控制之下,尽管它是幂等的。对Web服务的调用取决于聚合必须加载的一些数据,聚合逻辑取决于Web服务返回的结果代码(想象一下在线支付)
- 解决方案:Web服务被用作域服务,如果请求失败,一切都会失败,稍后会重试。如果数据库在保存时失败,也会重试
场景#n[#4的许多变体]。。。
因此,当他们改变自己的状态时,我无法确定如何在DDD中使用网络服务的明确标准。
理想情况下,我会在我的存储中进行更改,然后有某种集成层来复制外部数据库或服务中的这些更改,例如事件。然而,大多数时候都有写后读的一致性要求,如果在操作返回后刷新,屏幕上会显示正确的数据(例如:来自付款所在的同一Web服务的付款收据)。
另一个例子:屏幕允许保存有关书籍的数据,但书籍规范保存在数据库中,描述(几种语言)保存在外部服务中。操作必须一致,如果用户单击"保存"并刷新,屏幕需要同时显示所有内容,而不能只显示带有旧翻译的规范或根本不显示翻译,因为它们正在复制到另一个数据库。
哪个是确定的可靠标准?
答案是阅读Life Beyond Distributed Transactions。
简而言之,试图可靠地协调不同位置的写入是昂贵的。在大多数情况下,最好在设计中承认你不可能同时到达所有地方,并投资于处理这一事实的后果。
我一直采用的规则是,Aggregate应该是纯的,不依赖于进行IO调用的服务(即磁盘或网络)。每次在给定状态下执行命令时,它都应该给出相同的结果。在方法调用中注入服务或将其作为参数传递会破坏此规则。抽象的想法是,聚合永远不应该基于它不拥有的数据做出决定。
但是,每个复杂系统包含的聚合数不止一个。它还包含为业务流程建模的Sagas/流程经理(从现在起只是Saga)。设计一个完美的Saga所需要的只是一个清晰的业务流程和幂等端点。
Saga启动并侦听域中的更改,通常是通过订阅域事件。它通过向相应的端点发送命令来对它们作出反应。请注意,我使用术语Endpoint来指代以幂等方式处理命令的任何接收器。纯粹的集合体就是这样的终点。但Saga端点也可以是一个外部系统,比如支付网关。这样的网关不应使用现有的PaymentID
(您的系统正在发送的不透明字段)启动新的支付。
结论:据我所见,您的所有场景都可以实现为传奇。
你可以在这里阅读更多关于传奇的内容。