【发布时间】:2016-08-25 12:05:25
【问题描述】:
我编写我的项目,这是论坛的游戏 Mafia。我使用 CQRS 事件溯源 + MongoDB。游戏开始时,游戏需要给每个玩家一个随机的角色。我如何实现它,如果何时应用聚合根事件,例如,来自数据库的“角色给定”(不是事件,现在已保存),总是会调用随机函数,这将返回不同的结果?
【问题讨论】:
标签: c# mongodb cqrs event-sourcing
我编写我的项目,这是论坛的游戏 Mafia。我使用 CQRS 事件溯源 + MongoDB。游戏开始时,游戏需要给每个玩家一个随机的角色。我如何实现它,如果何时应用聚合根事件,例如,来自数据库的“角色给定”(不是事件,现在已保存),总是会调用随机函数,这将返回不同的结果?
【问题讨论】:
标签: c# mongodb cqrs event-sourcing
通常你会有一个触发某些域行为的命令(即分配随机角色),然后该角色将保存在数据库中的一个事件中,即RoleAssigned。这将在玩家下一次通过重播事件恢复游戏时保留角色。您不会在处理事件的代码中分配随机角色,它将在命令处理程序中完成,不会重播。
public void Handle<StartGameCommand>()
{
var player = someEventSourcedPlayerRepository.GetOrCreateOrWhatever();
player.assignRole(); // randomly assigns the role and creates event RoleAssigned
someEventSourcedPlayerRepository.Save(); // saves events to db
}
在上面的代码中,玩家将拥有一个包含其角色的事件。当您下次加载玩家时,他们的角色将从事件中获取。您不再调用 player.assignRole。
【讨论】:
Player,则范围仍然很小。
我是如何实现的,如果当聚合根应用事件,例如,“角色给定”,来自数据库(不是事件,现在已经保存),总是会调用随机函数,这将被返回结果不一样?
创建事件和应用事件是不同的代码路径。应用事件时不需要做“思考”,因为聚合在创建事件时已经做了思考。在 apply 中您需要做的就是忠实地更新您的状态。
也就是说,在应用事件时执行规则不是聚合的工作。
所以你的历史看起来像
GameStarted()
RoleAssigned(Alice, Innocent)
RoleAssigned(Bob, Innocent)
RoleAssigned(Charlie, Mafia)
...
请注意,没有什么可以随意做的;您可以补充游戏状态,而不必担心这些事件最初是否描述了随机分配 - 因为分配发生在过去,所以分配完全确定现在。
当聚合做出决定时,分配的随机位属于写入路径
void assignRole(Player p) {
Role role = getRandomRole();
this.apply( RoleAssigned(p, role) );
}
跟进
我应该如何更改我的代码?
class GameEventHandler {
public void Handle(GameStartedEvent message) {
var randomRoles =_randomizer.GetRandomRoles();
foreach(user in userAggregates) {
user.RoleAssign(new AssignRoleCommand(randomRoles[i] ));
}
}
}
事件处理程序中的决策代码是“代码气味”;这是某个地方出了问题的线索。当聚合处理命令时,应在模型中做出选择。
对于像 Mafia 这样的游戏,您希望在其中获得适当的角色平衡,并且需要担心在分配角色时使用哪些变体规则,我希望 Game 聚合拥有角色分配决策.因此,随机分配将发生在 StartGameCommand 处理程序中,可能看起来像这样
class Game {
public void startGame () {
this.apply(GameStarted());
var randomRoles =_randomizer.GetRandomRoles();
foreach(userId in this.players) {
this.apply(RoleAssigned(userId, randomRoles[i]);
}
}
}
我们之前看到的事件列表都在这里,是作为运行单个命令的结果写入的(这意味着所有事件都在单个事务中写入您的事件存储)。
另一方面,如果您确实需要每个用户聚合负责分配其角色,那么这里的逻辑将像这样分解
class Game {
public void startGame () {
this.apply(GameStarted(this.players));
}
}
class User {
public void assignRole () {
this.apply(RoleAssigned(this.id, _randomizer.getRandomRole());
}
}
您的事件处理程序将连接这两者,而无需了解随机角色。
class GameEventHandler {
public void Handle(GameStartedEvent message) {
foreach(userId in message.players) {
dispatch(AssignRoleCommand(userId));
}
}
}
事件处理程序就像经理一样,他们不做任何事情,他们只是将工作委派给其他人
【讨论】:
class GameEventHandler { public void Handle(GameStartedEvent message) { var randomRoles =_randomizer.GetRandomRoles(); foreach(user in userAggregates) { user.RoleAssign(new AssignRoleCommand(randomRoles[i] )) } } },这意味着,总是当 gameAggregate 将应用事件 GameStarted 时,随机器发送随机角色。我应该如何更改我的代码?