【问题标题】:How I can use random with CQRS+Event Sourcing如何在 CQRS+Event Sourcing 中使用随机
【发布时间】:2016-08-25 12:05:25
【问题描述】:

我编写我的项目,这是论坛的游戏 Mafia。我使用 CQRS 事件溯源 + MongoDB。游戏开始时,游戏需要给每个玩家一个随机的角色。我如何实现它,如果何时应用聚合根事件,例如,来自数据库的“角色给定”(不是事件,现在已保存),总是会调用随机函数,这将返回不同的结果?

【问题讨论】:

    标签: c# mongodb cqrs event-sourcing


    【解决方案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。

    【讨论】:

    • 是的,我理解你,但如果我愿意,角色分配是自动的。在我的项目中,当我发送请求时,命令和命令处理程序会更改系统的状态。如果我想,那是自动的,我把它写在事件处理程序中,然后它会被写入 BD。我该怎么办? (对不起我糟糕的英语)
    • 我很困惑。您希望角色只分配一次吗?如果是这样,请按照我的描述在命令处理程序流程中执行此操作。它仍然是自动的 - 无论走到哪里,该位都应该是相同的逻辑。
    • @tomliversidge 或者,您可以生成一个随机的 AssignRole 命令并执行它。诚然,这将是一个商业事务,并且会阻止整个 GameStart 成为一个单一的原子事务。但是根据您的事件溯源风格,您可能无论如何都必须强制执行严格的 1“修改”AR = 1 事务,如果 AR 为 Player,则范围仍然很小。
    【解决方案2】:

    我是如何实现的,如果当聚合根应用事件,例如,“角色给定”,来自数据库(不是事件,现在已经保存),总是会调用随机函数,这将被返回结果不一样?

    创建事件和应用事件是不同的代码路径。应用事件时不需要做“思考”,因为聚合在创建事件时已经做了思考。在 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 时,随机器发送随机角色。我应该如何更改我的代码?
    • 还是(随机分配角色)只能手动制作一次?
    猜你喜欢
    • 2022-01-25
    • 2020-05-09
    • 2017-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-14
    • 2016-12-28
    • 1970-01-01
    相关资源
    最近更新 更多