我写了我的项目,这是论坛的游戏黑手党。我使用CQRS Event Sourcing + MongoDB。当游戏开始时,游戏需要给每个玩家一个随机角色。如何实现它,如果聚合根将被应用事件,例如,#DB;给出的角色",来自DB(不是事件,现在已经保存),总是会调用随机函数,这将是返回不同的结果?
答案 0 :(得分:1)
通常你会有一个触发某些域行为的命令(即分配随机角色)然后该角色将被保存在数据库中的一个事件中,即。 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。
答案 1 :(得分:0)
我如何实现它,如果聚合根将被应用事件,例如,“给出的角色”,来自DB(不是事件,现在已经保存),总是会调用随机函数,这将是返回不同的结果?
创建事件和应用事件是不同的代码路径。应用事件时没有“思考”,因为聚合在创建事件时已经考虑过了。您在申请时需要做的就是忠实地更新您的州。
也就是说,在应用事件时强制执行规则并不是聚合的工作。
所以你的历史看起来像是
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));
}
}
}
事件处理程序就像经理,他们什么都不做,他们只是将工作委托给其他人