Aggregate

前面介绍 CommandHandler 时,我们已经看过用来处理 DepositMoneyCommand 命令的代码:

  1. @CommandHandler
  2. public void handle(DepositMoneyCommand command) {
  3. // 从 repository 中装载 BankAccount 的Aggregate
  4. Aggregate<BankAccount> bankAccountAggregate = repository.load(command.getBankAccountId());
  5. // 通过 bankAccountAggregate 来执行 deposit 操作
  6. // deposit 操作的实现是通过 BankAccount 这个 Aggregate 来执行的
  7. bankAccountAggregate.execute(bankAccount -> bankAccount.deposit(command.getAmountOfMoney()));
  8. }

在 DDD 的设计中,这是 Aggregate 的标准使用方式:

  1. Aggregate 只能通过 repository 装载
  2. repository 通常喜欢根据 id 属性来装载 Aggregate
  3. Aggregate 不是简单的VO,而是领域对象,不仅仅保持自身的数据和状态,而且有业务方法来实现业务逻辑

关于第三点,来看 BankAccount 的代码实现:

  1. // @Aggregate 注解表明这是一个 DDD 中的 Aggregate
  2. @Aggregate
  3. public class BankAccount {
  4. // @AggregateIdentifier 注解表明这个字段是 Aggregate 的 id 字段
  5. @AggregateIdentifier
  6. private String id;
  7. // Aggregate 是领域对象,保存有数据和状态
  8. private long overdraftLimit;
  9. private long balanceInCents;
  10. ......
  11. }

除此之外,BankAccount 这个 Aggregate 还定义有业务方法来对应各种业务错误,比如 deposit 和 withdraw:

  1. public void deposit(long amount) {
  2. apply(new MoneyDepositedEvent(id, amount));
  3. }
  4. public void withdraw(long amount) {
  5. if (amount <= balanceInCents + overdraftLimit) {
  6. apply(new MoneyWithdrawnEvent(id, amount));
  7. }
  8. }

和一般业务处理方式不同的是,axon 中的 Aggregate 不能直接在业务方法中简单的操作自身的状态,即不能直接通过 this.balanceInCents=***; 这种方式来修改状态。而是要发送一个 Domain Event 给 Event Bus,然后自己再定义对应的 Event Handler 来处理这个 Domain Event:

注: MoneyAddedEvent 是 MoneyDepositedEvent 和 MoneyWithdrawnEvent 的父类。

  1. @EventHandler
  2. public void on(MoneyAddedEvent event) {
  3. balanceInCents += event.getAmount();
  4. }

在绕了一圈之后,BankAccount 在这里开始真正的修改自身的数据了。但是记住,这里只是修改了内存中当前这个 BankAccount 实例的状态。持久化操作不在这里。