Domain driven design 强制执行跨多个聚合的严格一致性
考虑以下业务需求: 我们有可以玩游戏的玩家。一个玩家一次只能玩一场游戏。一场游戏需要两名玩家。 该系统将容纳数百万玩家,游戏大约需要两分钟。并发问题可能会出现 我们希望遵守以下规则:单个交易涉及单个聚合。此外,最终的一致性不得导致接受的游戏,这些游戏必须在之后(即使是很短的一段时间)由于并发问题而取消。因此,最终的一致性并不真正合适 我们需要如何定义聚合及其边界来实施这些业务规则 我设想了两种方法: 1。基于事件的握手 聚合Domain driven design 强制执行跨多个聚合的严格一致性,domain-driven-design,aggregate,eventual-consistency,Domain Driven Design,Aggregate,Eventual Consistency,考虑以下业务需求: 我们有可以玩游戏的玩家。一个玩家一次只能玩一场游戏。一场游戏需要两名玩家。 该系统将容纳数百万玩家,游戏大约需要两分钟。并发问题可能会出现 我们希望遵守以下规则:单个交易涉及单个聚合。此外,最终的一致性不得导致接受的游戏,这些游戏必须在之后(即使是很短的一段时间)由于并发问题而取消。因此,最终的一致性并不真正合适 我们需要如何定义聚合及其边界来实施这些业务规则 我设想了两种方法: 1。基于事件的握手 聚合玩家,聚合游戏 当游戏被请求时,它会推送一个GameRequested-
玩家
,聚合游戏
当游戏被请求时,它会推送一个GameRequested
-事件。Player
s订阅此事件并响应相应的事件,要么GamePlayerAccepted
,要么GamePlayerRejected
。只有当两个玩家
都接受时,游戏
才会开始(GameStarted
)
优点:
- 聚合
负责管理其自身的可用性,该可用性对应于域模型Player
- 开始一场
游戏的责任分散在多个集合中(看起来像是“假的”-最终的一致性)
- 大量的通信开销
- 需要一致性度量,例如,如果出现问题,释放
s播放器
Player
,聚合GamesManager
(带有一组值对象ActiveGamePlayers
),聚合Game
GameManager
被请求用两个给定的玩家启动一个新的游戏。GameManager
能够确保Player
一次只玩一次,因为它是单个聚合
优点:
- 没有一致性强制事件,例如
GamePlayerAccepted
,GamePlayerRejected
等
缺点:
- 领域模型似乎很模糊
玩家管理可用性的责任转移了
- 我们必须确保只创建一个
GameManager
实例,并引入域机制,让客户不必担心中间聚合
- 独立的
游戏
-开始互相干扰,因为游戏管理器
-聚合自身锁定
- 需要进行性能优化,因为
GameManager
-聚合将收集数千万的所有活动游戏玩家
这些方法似乎都不适合解决这个问题。我不知道如何设置边界,以确保模型和性能的严格一致性和清晰性。我将使用基于事件的握手,这就是我将如何实现的:
据我所知,您需要一个游戏
流程,作为传奇
实现。您还必须定义一个Player
聚合、一个RequestGame
命令、一个GameRequested
事件、一个GameAccepted
事件、一个MarkGameAsAccepted
命令、一个MarkGameAsRejected
命令,GameStarted
事件和GameFailed
事件
因此,当玩家A
想要与玩家B
玩游戏时,玩家A
收到请求游戏
命令。如果此玩家正在播放其他内容,则会引发playerReadyPlaySagame
异常,否则会引发GameRequested
事件,并将其内部状态更新为playing
Game
saga捕获GameRequested
事件,并将RequestGame
命令发送给Player B
聚合(这是Player
聚合,ID
等于a
)。然后:
- 如果
玩家B
正在玩另一个游戏(它通过查询其内部玩
状态知道这一点),则会引发GameRejected
事件;Game
saga捕获此事件并向Player a
发送MarkGameAsRejected
命令;然后Player A
引发GameFailed
事件,并将其内部状态更新为not_playing
- 如果
玩家B
没有玩另一个游戏,则会引发GameAccessed
事件;Game
saga捕获此事件,并将MarkGameAsAccepted
命令发送给Player A
aggregatePlayer A
然后发出GameStarted
事件,并将其内部状态更新为playing
为了理解这一点,您应该尝试对用例进行建模,假设没有计算机存在,玩家将是通过打印邮件进行通信的人
这个解决方案是可扩展的,我知道这是必需的
另一种解决方案似乎对数百万玩家不可行
第三种解决方案是在SQL表或NoSQL集合中使用活动参与者的集合,而不使用聚合战术模式。对于concurency,当将一对播放器设置为活动时,您可以在支持的情况下使用optimistick锁定或事务(低可扩展性)或两阶段提交(有点难看) 我会使用基于事件的握手,这是我将如何实现的: