当前位置: 首页 > 知识库问答 >
问题:

命令处理程序只是从总线接收命令并发布事件吗?

东门宜
2023-03-14

我实际上是CQRS和事件来源的新手。

当我看到以下代码时,我被命令处理程序弄得一头雾水:

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Aggregate
public class BankAccountAggregate {

    @AggregateIdentifier
    private UUID id;
    private BigDecimal balance;
    private String owner;

    @CommandHandler
    public BankAccountAggregate(CreateAccountCommand command){
        AggregateLifecycle.apply(
                new AccountCreatedEvent(
                        command.getAccountId(),
                        command.getInitialBalance(),
                        command.getOwner()
                )
        );
    }
@Slf4j
@RequiredArgsConstructor
@Component
public class BankAccountProjection {

    private final BankAccountRepository repository;
    private final QueryUpdateEmitter updateEmitter;


    @EventHandler
    public void on(AccountCreatedEvent event) throws Exception {
        log.debug("Handling a Bank Account creation command {}", event.getId());
        BankAccount bankAccount = new BankAccount(
                event.getId(),
                event.getOwner(),
                event.getInitialBalance()
        );
        this.repository.save(bankAccount);
        Boolean isActive =  AggregateLifecycle.isLive();

    }
}
    null

那么我们为什么不把帐户创建的逻辑放在命令处理程序中呢?这样做的目的是什么?

完整的源代码在这个链接。

共有1个答案

滑令
2023-03-14

你在混合一些概念。让我们把它们弄清楚。

如果你实现了事件来源,这意味着你的真相来源是事件本身。您存储的是事件,而不是具体的“状态”(实体)。让我们看看一些伪代码:

创建新帐户:

function createAccount(data) {
  event = new AccountCreatedEvent(data)
  eventStore.save(event)
}
  function withdraw(data) {
    events = eventStore.getEvents(data.accountId)
    account = new Account()
    account.apply(events)
    account.withdraw(data)
    newEvents = account.newEvents
    eventStore.save(newEvents)
  }
class Account {

  amount = 0
  newEvents = []

  function apply(events) {
    events.forEach(event => {
      if event == AccountCreatedEvent {
        this.amount = event.initialAmount
      } else if (event == WithdrawalApplied) {
        this.amount = this.amount - event.amount
      } // more event types
    })
  }

  function withdraw(data) {
    // here is where you ensure aggregate invariants (business rules)
    if this.amount == 0 {
      throw Error("no money")
    }
    this.amount = this.amount - data.amount
    newEvents.add(new WithdrawalApplied(data))
  }

}

 

该命令处理程序只是发布一个事件。正如您所看到的,对象的命名是AccountCreatedEvent,但这并不意味着帐户已经创建,对吗?

答案是您应该将事件存储在命令处理程序中。这恰恰意味着账户被创建了。然后,如果需要,您可以在正在进行的过程中发布事件。继续读。

使用CQRS,您只需分离查询和命令,但这种技术可以在根本不需要事件源的情况下应用。

下图说明了三种技术(CQRS、事件来源和预测)是如何协同工作的。

 类似资料:
  • 主要内容:一、Sql流程,二、源码分析,四、总结一、Sql流程 MySql是数据库,这次就分析一下一条SQL语句的流程,流程可能不会全面展开分析,当后面遇到具体的模块时,再由各个模块深入学习。如果使用过MySql的客户端(任意一种都可以),基本的形式就是在客户端写一条SQL语句,然后点击运行,正常的情况下,就会返回这条SQL执行后的结果。 可能学习Sql源码的人不少,但学习编译器知识的人就少多了。在SQL语句的执行过程中,其实SQL语句就是一门

  • 问题内容: 在Redis 4.0中,有一个新命令UNLINK删除Redis内存中的密钥。 此命令与DEL非常相似:它将删除指定的密钥。与DEL一样,如果密钥不存在,则将其忽略。但是,该 命令在不同的线程中执行实际的内存回收 , 因此当DEL为时 , 它不会阻塞 。这就是命令名称的来源:该命令只是将键与键空间断开链接。实际的删除将在以后异步发生。 因此,可以始终(100%次)使用UNLINK而不是D

  • 在本章中,我们来看一看一些经常使用的批处理命令。如下图中所示 - 编号 命令 描述 1 VER 此批处理命令显示正在使用的MS-DOS的版本。 2 ASSOC 这是将扩展名与文件类型(FTYPE)相关联的批处理命令,显示现有关联或删除关联。 3 CD 此批处理命令用于更改进入不同的目录,或显示当前目录。 4 CLS 这个批处理命令清除屏幕。 5 COPY 此批处理命令用于将文件从一个位置复制到另一

  • #,## # 和 ## 操作符是和#define宏使用的. 使用# 使在#后的首个参数返回为一个带引号的字符串. 例如, 命令 #define to_string( s ) # s 将会使编译器把以下命令 cout << to_string( Hello World! ) << endl; 理解为 cout << "Hello World!" << endl;

  • 预处理命令 #, ## manupilate字符串 #define 定义变量 #error 显示一个错误消息 #if, #ifdef, #ifndef, #else, #elif, #endif 条件操作符 #include 插入其它文件的内容 #line 设置行和文件信息 #pragma 执行特殊命令 #undef 取消定义变量 预定义变量 其它变量

  • 主要内容:实例前面各章中,已经多次使用过 命令。使用库函数之前,应该用 引入对应的头文件。 这种以号开头的命令称为预处理命令。 C语言源文件要经过编译、链接才能生成可执行程序: 1) 编译(Compile)会将源文件( 文件)转换为目标文件。对于 VC/VS,目标文件后缀为 ;对于 GCC,目标文件后缀为 。 编译是针对单个源文件的,一次编译操作只能编译一个源文件,如果程序中有多个源文件,就需要多次编译操作。