如何在CQRS中创建新的聚合根?


10

我们应该如何在cqrs体系结构中创建新的聚合根?在此示例中,我想创建新的聚合根AR2,其中包含对第一个AR1的引用。

我正在使用AR1方法作为起点来创建AR2。到目前为止,我看到的选择很少:

  1. 在AR1的内部方法中,createAr2RootOpt1我可以new AR2()使用具有访问存储库权限的域服务立即将该对象调用并将其保存到db。
  2. 我可以在第一个聚合根发出事件,例如。SholdCreateAR2Event然后有无状态的传奇对此做出反应并发出命令CreateAR2Command,该命令随后被处理并实际创建AR2并发出AR2CreatedEvent。如果使用事件源SholdCreateAR2Event,则不会将其保留在事件存储中,因为它不会影响第一个聚合根的状态。(或者我们还是应该将其保存在事件存储中?)

    class AR1{
        Integer id;
        DomainService ds;
    
        //OPTION 1
        void createAr2RootOpt1(){
            AR2 ar2 = new AR2();
            ds.saveToRepo(ar2);
        }
    
        //OPTION 2
        void createAr2RootOpt2(){
            publishEvent(new SholdCreateAR2Event());    //we don't need this event. Shoud it still be preserved in event store?
        }
    }
    
    class AR2{
        Integer id;
        Integer ar1Id;
    
        void handle(CreateAR2Command command){
            //init this AR with values and save
            publishEvent(AR2CreatedEvent());    //used for projections afterwards and saved inside AR2 event store
        }
    }
    
    class Saga{
        void handle(SholdCreateAR2Event ev){
            emitCommand(new CreateAR2Command());
        }
    }
    

哪种方法更合适?

Answers:


2

我认为没有选择。2是解决方案,但进行了少量但重要的修改:AR1不应发出旨在创建的事件AR2,而应发出AR1WasCreated事件。此事件应保留在事件存储中,因为它是标记诞生的重要事件AR1。然后,Sagawhould侦听AR1WasCreated事件并生成命令以创建AR2CreateAR2Command

选项1是非常错误的。您绝对不应将这种域服务注入AggregateAggregates应该是纯净的,除了产生事件外没有其他副作用。

PS我永远不会从的构造函数发出事件,Aggregate因为在创建对象的实例(就编程语言而言)与创建(如果需要的话)之间有区别Aggregate。我仅从handle方法发出事件(处理时command)。


你是什么意思AR1WasCreated?应该是AR2WasCreated吗?另外,如果我使用您的逻辑,AR2WasCreated则在实际创建事件之前发出事件吗?将这个事件保存在AR1的事件日志中似乎是有问题的,因为我实际上不需要在AR1内使用此数据(它不会修改AR1内的任何内容)。
Bojan Vukasovic

好,三年后 它去AR1WasCreated-> SAGA(如果创建了A1,则有规则,然后创建A2)-> CreateAR2Command-> AR2WasCreated
Bojan Vukasovic

@BojanVukasovic我很高兴它在我写的时候起作用了:)
Constantin Galbenu

2

我们应该如何在cqrs体系结构中创建新的聚合根?

创作模式很奇怪

乌迪·达汉(Udi Dahan)对于一般问题有一些有用的话要说:不要创建聚合根。基本要点是聚合不仅会突然出现,而且存在描述它们显示方式的域语言,应在域模型中捕获该域语言。

可能会发生扭曲的地方是,域模型中正在处理命令的实体不是被事务修改的实体。没错 这很奇怪(与您要求实体进行自我修改的情况相比)。

您的第二种方法也可以。“我们未实际保存到数据库而引发的事件”有时称为“域事件”

基本思想是,在同一事务中,命令处理程序引发事件,该事件沿着总线传播到事件处理程序,该事件处理程序允许第二个聚合自身创建。也许您会获得更好的代码凝聚力。

注意:在事件源系统中,通常不以这种方式使用事件。

在使用事件源的情况下,ShouldCreateAR2Event将不会保留在事件存储中,因为它不会影响第一个聚合根的状态。

注意:事件名称通常是过去时态-ShouldCrateAR2的拼写错误。

是的,如果您只是将一个事件扔到同步总线上以运行远程代码,那么您不应该将该事件保存在记录簿中。这只是这种规模的实现细节。

还是应该仍将其保存在事件存储中?

避免在同一事务中修改两个不同的事件流。如果此创建也表示对AR1的更改,则通常的答案是在此事务中修改AR1,并由负责触发创建AR2的命令的事件的异步订户。

等幂命令处理在这里有很大帮助。


感谢您的回答。还有一件事不是100%清楚的-稍后,如果我必须使用AR2作为AR1的参数,我应该如何传递它-因为CQRS指出AR应该仅用于写入而不是查询。但是我除了使用之外别无选择,AR1.doSmthn(AR2 param)因为我创建的任何读取投影都没有我需要的完整数据(只有AR2具有完整数据)。
Bojan Vukasovic

>“是的,如果您只是将一个事件抛出到同步总线上以运行远程代码,那么您不应该将该事件保存在记录簿中。” 我认为保存它具有真正的价值,因为您知道该过程已经开始进行,现在您还可以跟踪该过程是否实际完成。但是我想这取决于用例
Chaosekie
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.