领域驱动设计3

1.事件溯源

一种将所有的领域事件存储到事件存储中,并通过重放历史事件来还远领域对象状态的模式。该模式可以提供完整的业务追踪能力,并支持聚合根状态回滚。

1.1.方案一 直接保存事件

在这里插入图片描述

领域事件的创建和修改操作如上图所示。要注意的是,此时实体表中的结构是不存储状态的,仅存储实体的id和版本号(用于乐观锁处理)。
实体表字段:id,entity_id,version,…
事件表字段:id,event_id,event_data,event_time,event_type,…

1.2.方案二 系统快照

方案一中实体表中不再存储实体的状态。如果实体发生的时间非常多,则在加载聚合根重放历史事件时可能会消耗大量资源。所以第二种方案,在第一种方案的基础上引入快照的概念。
当事件达到一定数量时,为实体创建快照,持久化存储。每次需要加载聚合根时,则判断其是否存在快照,如果存在,则读取快照之后的事件,并结合快照重建聚合根;如果不存在快照,则读取所有事件,重建聚合根。

1.3.方案三 拉链表

方案二仍存在一定缺点,如很难对领域事件的运营数据进行分析。为解决该问题可使用拉链表。
拉链表是一种数据结构,在拉链表中每一条记录都有一个开始时间和结束时间。当新增一条记录时,设置开始时间为操作时间,结束时间为无限大。当记录被修改时,将记录的结束时间设置为操作时间,并新增一条记录,新纪录的开始时间为操作时间,结束时间为无限大。这种数据结构表示记录在开始时间和结束时间之间生效。
结合拉链表之后,每次聚合根发生修改都产生一个新的快照,并修改上一次快照的结束时间。即将快照作为拉链表的记录。每次需要加载聚合根时,直接从最新的快照恢复即可。

2.一致性

2.1.事务控制

在DDD模型中,将事务控制放在应用层是不合理的,因为在应用层会涉及到通过基础设施层调用外部接口,此时会导致整个事务的事件过长,占用数据库连接时间。
所以事务的处理应放在repository中,因为所有访问数据库的操作都集成在repository中。但是在并发修改时仍旧存在不一致的问题。因为单个repository方法要么是save要么是load,而修改事务中可能同时存在查和改操作,如减库存操作。此时仅在load方法或仅在save方法上加事务并不能保证一致性。此时可以考虑使用乐观锁(版本号或时间戳)结合失败重试机制(Spring Retry组件)来实现。

2.2.跨聚合事务

一个事务只能更新一个聚合。然而在实际应用中可能存在一个业务需要多个聚合根共同实现,且聚合根之间的数据存储位置并不相同(即数据可能保存在不同的数据库中)。
夸聚合事务控制实际上就是处理分布式事务的场景。
在本章节中将介绍几种分布式事务解决方案。

2.2.1.二阶段提交2PC

一种同步的分布式事务协议,包括事务的协调者和参与者两种角色。
协调者:负责协调整个事务的执行过程。
参与者:具体执行事务操作的节点。
在这里插入图片描述

二阶段提交将事务分为两个阶段:准备和提交。
准备:
协调者向参与者发送事务准备请求。
参与者执行事务,并将事务执行结果和是否可以提交的信息反馈给协调者。

提交:
如果所有参与者都反馈可以提交事务,协调者向所有参与者发送提交申请。
参与者收到提交请求后,将正式提交事务并释放相关资源。
如果任何一个参与者反馈无法提交,协调者会发送回滚请求,要求所有参与者撤销事务。

2.2.2.TCC事务

TCC事务分为两个角色:事务协调者和事务参与者。
TCC事务分为三个阶段:
Try:尝试阶段,进行资源预留和锁定
Confirm:如果try阶段所有操作都执行成功,那么此阶段将执行真正的业务操作。
Cancel:如果try阶段有任何业务操作失败,则执行取消操作。

TCC事务三种特殊处理
1)幂等,在TCC事务中,Confirm/Cancel阶段执行失败,需要进行重试。所以Confirm/Cancel需要实现幂等性设计。
2)空回滚,在没有执行try操作的情况下,在全局事务取消时调用了Cancel操作。在一些TCC事务框架中,分支事务会保存全局事务ID,通过全局事务ID来判断是否已经执行。这样可以即可以维持幂等,又可以识别空回滚。为什么会出现空回滚?1 分支事务服务宕机,导致try未执行,但是事务协调者会发起cancel。2 分支事务超时,事务协调者根据超时机制,会回滚整个事务。此时未执行try的分支事务,也会调用其cancel操作。
3)悬挂,某个事务先执行了cancel操作,再执行try操作。此时事务已经执行完了,所以try之后锁定的资源即无法确定又无法取消。可以通过记录事务的状态来避免,如事务已经执行过cancel,则不再执行try。为什么会发生悬挂?分支执行超时,协调者直接发起回滚,回滚之后,try执行成功。

2.2.3.Saga事务

一种基于长事务和补偿机制的解决方案。它将事务划分为多个分支事务,这些分支事务按照一定的顺序执行。当某个分支事务执行成功之后,会通过消息通知下一个分支事务执行;当某个分支事务执行失败时,会按照正常事务执行顺序相反的方向进行一些列的补偿操作,保证事务的全局一致性。
在saga事务方案中有来个角色:协调者和事务。协调者负责管理和协调整个分布式事务的执行流程,跟踪事务执行状态并处理异常情况。事务角色包含正常操作和补偿操作两种类型的子事务。正常操作处理实际的业务逻辑。补偿操作处理的是正常业务相反的逻辑,将系统恢复到事务开始前的状态,用于撤销或修复正常操作的影响,即回滚该分支事务。
在这里插入图片描述

2.2.4.本地消息表

参考前文可靠地发布消息:直接发布+补偿机制和事务日志拖尾机制。

2.2.5.最大努力通知

通知发起者在一定时间内重复发送通知,直到接收者确认。
接收者在接收到消息后,进行业务处理,处理完之后对消息进行确认。
通知发起者收到消息确认后,停止重复发送。

当通知发起者迟迟收不到消息通知或通知发起者已经停止发送消息时。通知发起者需要主动调用通知接收者的查询接口,查询消息的执行状态。所以接受者要提供消息状态查询接口。

2.2.6.跨聚合事务总结

对于实时性要求不高,仅要求最终一致性的场景,可以使用本地消息表或者最大努力通知方案。
对于实时一致性要求比较高的事务场景,可以采用TCC事务方案。
对于长事务场景,设计外部系统、遗留系统,可以考虑Saga事务方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值