我有一个非常混乱的问题,因为我不确定这是否更多涉及全球设计问题。
我提供的服务定义了一些汇总,这些汇总发布了事件。让我们从服务中选择一个集合,并将其称为A。 现在,我定义了另一个具有应该与A一起使用的聚合的服务,我们将第二个聚合称为B。
当A发布某个事件时,我想向B发送命令(最好是通过传奇),但是我很难计算B的适当ID(A不知道B存在,因此它发布的事件没有关于如何计算ID的任何提示)
我可以想到几种可能的情况:
第一个方法是根据A的ID静态计算B的ID,例如在轴突中,我可以做类似some-id-${suffix}
的事情,因此当我用some-id
从A接收事件时,我可以立即知道应该将其发送到some-id-B
第二个会使用读取端吗? (我不确定该如何正确调用)并查询该事物,然后尝试根据A id查找B id,但这似乎有些过分。
有什么我能读懂的内容,可以引导我了解各种可能的情况,并提示我如何处理它们?谢谢!
答案 0 :(得分:2)
据我了解,您具有从集合B到集合A的关系。这种关系是正常的,并且始终存在。
注意:由于这个问题很笼统,没有上下文,因此我可能会遗漏一些东西。如果有比上述情况更特殊的情况,请通知我。
This is a great read for aggregate design
注意:请阅读Martin Fowler的this video,然后再阅读本答案的其余部分,我强烈建议这样做,因为它详细解释了与事件和命令有关的概念。
注意:由于实体一词也很重要,因此从现在开始,我将不再使用聚合,因此假设每个实体(玩家,用户 ,游戏)是其自身集合的根,并且是一致性边界,因此在这种情况下,将使用域事件的最终一致性。此刻我也将忽略CQRS,以避免不得不谈论读取侧和写入侧。我们将讨论并实现模型
让我们以游戏为例。您有一个播放器,该播放器应代表游戏中的用户。 玩家实体应以某种方式引用游戏和用户。它可以通过直接引用或通过ID。在分布式系统的情况下,将通过ID。对于我们的示例,让我们为ID使用UUID(例如 8d670541-aa51-470b-9e72-1d9227669d0c ),以便我们无需定义架构即可随机生成它们,自动生成序列号(例如在SQL数据库中) )或特殊算法。 假设一个用户具有 UserStatistics 。因此,当玩家得分(例如通过杀死射击游戏中的其他玩家)时,应创建 UserStatistics 实体(如果不存在)并进行更新。 UserStatistics 还应通过ID引用 User ,因此我们具有从 UserStatistics 到 User 的依赖性。
UserStatistics 如下所示:
UserStatistics {
UUID UserID,
uint KillsCount,
uint GamesPlayedCount
}
由于没有用户,播放器将不存在,因此应先创建用户。因为 Player 是 Game 的一部分,所以意味着 Game 应该首先创建。让我们用无所不在的语言定义一些术语。 用户 “加入” ,通过成为其中的 Player 来游戏。可以说,游戏将由其他人而不是用户创建,以避免讨论用户创建游戏并同时加入游戏时的情况,等等。 。如果在同一笔交易中发生这种情况,则... 游戏将类似于MMO,由某人创建,并且普通用户可以加入。
当用户 加入一个游戏时,将使用创建玩家实体userID 和 gameID 。创建没有用户ID 和游戏ID 的玩家是无效的。
让我们与命令和事件讨论问题。 命令可以通过事件进行触发。让我们使用观察者模式。一个实体必须观察另一个实体的事件。在我们的示例中,这意味着依赖性从 UserStatistics (观察者)到 User 和 Player (主题/消息生产者)。在 UserStatistics 上执行特定 Command 的事实是对 Player 中引发的 Event 的反应。 用户不应以任何方式影响播放器或播放器。通过使用 Event 故意以被动积极的方式触发特殊的 Command 并不是一个很好的策略。 命令可以由事件触发,但是触发不仅只能是一个特定的命令。可以触发许多不同的命令,只有相关的实体,服务或系统应该关心发生的情况。 播放器和用户仅提供事件。
当用户加入游戏并创建玩家时,它将通过ID引用两个实体,因此看起来像这样:
Player {
UUID GameID,
UUID UserID
}
还将 UserJoinedGameEvent 事件从 User 实体引发(它可能是从 Game 引发的,但我们将选择 User 强>)。看起来像这样:
UserJoinedGameEvent {
UUID GameID,
UUID UserID,
UUID PlayerID
}
UserStatisticsService 可以订阅事件并更新统计信息。
当用户加入游戏时,将开始收集统计信息的过程,我们将更新(或创建不存在的)他的 UserStatistics 他玩过多少场比赛。在玩家被杀的同时,我们将不得不再次更新统计信息。
StartGatheringUserStatisticsCommand 将由 UserJoinedGameEvent 事件触发。
让我们添加一个如下所示的事件 PlayerMadeKillEvent :
PlayerMadeKillEvent {
UUID UserID,
UUID PlayerID,
UUID GameID
}
UserStatisticsService 将订阅 PlayerMadeKillEvents ,并通过使用 PlayerMadeKillEvent.UserID 查找 UserStatistics 特定的用户。
当用户退出游戏时,可以引发 UserQuitsGameEvent ,并且统计信息收集可以停止。
在此示例中,我们没有用于生成特殊ID的特定架构,我们可以引用将首先创建的其他聚合,然后使用其ID。