使用CQRS是否可以直接从Axon聚合的命令处理程序运行查询?

乔治

这更多是与CQRS和Axon有关的理论问题。下面是一个不言自明的设置,该代码是伪代码,并不意味着可以编译。

假设为了处理来自集合“ Foo”的命令,我们首先需要查询另一个集合“ Bar”的状态以进行验证(从另一个有界上下文中进行,因此这不是简单地查找“ Foo”的问题”)的成员集合)。

如伪代码所示,我们有两个选择。选择(1),我们仅使用查询网关从“ Foo”集合中的命令处理程序中运行查询。

选择(2),我们应用一个事件,要求一个专用服务来处理查询,而不是“ Foo”。在查询系统的“ Bar”状态后,服务将向“ Foo”发送命令。

请问首选(直接查询从命令处理程序)似乎违背命令查询分离的整体思路-毕竟,这样一来我们命令处理过程中执行的查询,对不对?

第二种选择似乎更符合CQRS的精神:该命令仅导致一个事件(稍后将导致另一个命令,依此类推)。但这显然是有代价的:涉及更多的步骤2a,2b,2c,2d ...

我想听听社会人士对此的看法。对我而言,如果我们严格限制不将命令与查询处理混合使用,而是允许在命令处理中执行查询,这似乎很奇怪。还是我错过了什么?

@Aggregate
class AggregateFoo {

    private QueryGateway queryGateway;

    @CommandHandler
    public void on(UpdateFooCommand command){

        /*
            Assume that in order to validate this command we first need
            to query the state of another aggregate, "Bar".
        */

        // 1. We can just issue the query directly from the command handler.

        queryGateway
            .query(new AskForStateOfBarQuery(command.getBarId()))
            .then(queryResponse -> {
                // Proceed with original command execution depending
                // the result of the query response.    
            });

        // 2a. Or we can issue an intermediate EVENT offloading the query handling
        //     to a dedicated service ("FooBarService", see below).

        AggregateLifecycle.apply(new FooUpdateValidationRequestedEvent(command.getBarId()));

    }

    // 2d. "Foo" aggregate will react to the validation command from the
    //     dedicated service effectively executing the original command.

    @CommandHandler
    public void on(ProceedWithFooUpdateCommand command){

        // Do other validations, issue events here. At this
        // point we know that UpdateFooCommand was validated.
    } 

}

@Service
class FooBarService {

    private QueryGateway queryGateway;
    private CommandGateway commandGateway;

    @EventHandler
    public void on(FooUpdateValidationRequestedEvent event){
        
        // 2b. The dedicated service will run the corresponding query,
        //     asking for the state of "Bar".
        
        queryGateway
            .query(new AskForStateOfBarQuery(command.getBarId()))
            .then(queryResponse -> {

                // 2c. And will issue a COMMAND to the "Foo" aggregate
                //     indicating that it shoud proceed with the original 
                //     command's (UpdateFooCommand) execution.

                commandGateway.send(new ProceedWithFooUpdateCommand(command.getFooId()));

            });

    }

}

更新:

这是在讨论91stefan给出的有益答案之后进行的更新(请参阅下文)。

class AggregateFoo {
    int f = 9;

    // reference to the related Bar aggregate
    UUID bar;

    on(UpdateFooCommand){
        // Assume we must execute ONLY IF f < 10 AND bar.b > 10.
        // So we apply event to Saga (with f = 9), 
        // Saga will ask Bar: (b = 15), so the condition holds
        // and Saga issues ConfirmValidBarStateCommand
    }

    // Meanwhile, when Saga is busy validating, we process another
    // command CHANGING the state of Foo
    on(AnotherCommand) { f++; }

    // or "ConfirmValidBarStateCommand" as in 91stefan's example
    on(ProceedWithFooUpdateCommand){
        // So when we get back here (from Saga), there is no guarantee
        // that the actual state of Foo (f < 10) still holds,
        // and that we can proceed with the execution of the
        // original UpdateFooCommand
    }

}

class AggregateBar {
    int b = 15;
}

因此看起来问题似乎仍然存在:如果其验证取决于来自另一个有界上下文的Bar状态,那么如何在Foo中一致地验证和执行命令?看起来我们可能在这里有几种选择:

  • 从Foo的命令处理程序直接查询Bar的投影状态
  • (无状态)服务的事件,该服务向Bar查询投影状态
  • (91stefan的建议)事件传递给Saga,该事件将验证命令发送给Bar
91斯蒂芬

您可能会遇到问题,从命令处理程序中进行查询并不被认为是一种好习惯,因为由于最终的一致性,您的投影可能不是最新的。同样,来自相同聚合的命令处理程序将顺序/同步执行。您只想快速做些事情,调用外部服务将阻止并阻止其他命令的执行,直到当前命令处理程序完成执行为止。

在这种情况下,您需要的是Saga。https://docs.axoniq.io/reference-guide/v/3.3/part-ii-domain-logic/sagas

简化流程为:

  • commandHandler(UpdateFooCommand)-> applyEvent(AskedForStateOfBarEvent)
  • AskedForStateOfBarEvent开始传奇
  • 在传奇中,将命令发送到Bar聚合-> ConfirmYourStateBar(barAggregateId)
  • commandHandler(ConfirmYourStateBar)->验证状态-> applyEvent(StateIsValidatedEvent)
  • Saga对StateIsValidatedEvent做出反应,将命令发送到AggregateFoo->(ConfirmValidBarStateCommand(aggreageFooId)&saga已关闭(结束事件为StateIsValidatedEvent)
  • 在commandHandler(ConfirmValidBarStateCommand)中,因为状态BAR状态有效,所以您可以继续执行

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

无法直接使用“ java”命令运行Java程序图

是否可以使用mgo驱动程序运行mongo copysetset命令?

处理排队的命令处理程序响应。CQRS

CQRS命令和查询-它们是否属于域?

在CQRS模式中,应该在域服务或命令处理程序中运行

为什么CQRS命令处理程序排除保存UnitOfWork?

是否可以直接在类tbl_sql(或tbl_dbi)上运行SQL查询?

C#MongoDb直接从JSON运行聚合查询

Axon:用于单个聚合的多命令

DDD / CQRS-请求处理程序(或控制器)是否可以引发在域级别定义的异常?

是否可以使用mediatR处理一个查询但使用不同的处理程序?

是否可以在Firestore中运行聚合查询?

是否可以使用TSQL查询Azure SQL服务器在哪个程序包/层上运行?

是否可以在“运行”命令(即F5)中运行批处理文件?

bash中是否有一个命令可以检查程序是否可以运行

是否可以从命令行运行rascal程序?

在CQRS中为命令使用多个处理程序是否可以?

LINQ查询是否使用运行程序的计算机或托管数据库的服务器的处理能力?

是否可以使用日期匹配的MongoDB聚合查询?

是否可以在Android View上查询正在运行/待处理的动画?

是否可以直接在移动设备上运行 angular js 应用程序

处理器是否可以直接使用RAM?

Axon 事件处理程序和查询处理程序在 kotlin 中不能一起工作

交叉聚合引用:命令处理程序可以在聚合 id 集合之间循环,还是事件处理程序可以调度命令?

在 CQRS 模式中的命令处理程序中查询数据库

MongoDB 是否可以在 key 上使用 substr 运行不同的命令

是否可以在 QT 应用程序中使用 C++ 运行 cmd 的命令?

命令使用 CQRS 和 ES 进行多个聚合

CQRS 中是否有登录/注册命令或查询