在调用 EventSourcingHandlers 之间如何保持实体的状态?

How is the state of the entity being persisted between calls to EventSourcingHandlers?

Axon Giftcard demo中有一个GiftCard class注解为@Aggregate:

@Aggregate
@Profile("command")
public class GiftCard {

    private final static Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    @AggregateIdentifier
    private String id;
    private int remainingValue;

    @CommandHandler
    public GiftCard(IssueCmd cmd) {
        log.debug("handling {}", cmd);
        if(cmd.getAmount() <= 0) throw new IllegalArgumentException("amount <= 0");
        apply(new IssuedEvt(cmd.getId(), cmd.getAmount(), cmd.getCurrency()));
    }

    @CommandHandler
    public void handle(RedeemCmd cmd) {
        log.debug("handling {}", cmd);
        if(cmd.getAmount() <= 0) throw new IllegalArgumentException("amount <= 0");
        if(cmd.getAmount() > remainingValue) throw new IllegalStateException("amount > remaining value");
        apply(new RedeemedEvt(id, cmd.getAmount()));
    }

...
    @EventSourcingHandler
    public void on(IssuedEvt evt) {
        log.debug("applying {}", evt);
        id = evt.getId();
        remainingValue = evt.getAmount();
        currency = evt.getCurrency();
        log.debug("new remaining value: {}", remainingValue);
        log.debug("new currency: {}", currency);
    }

   @EventSourcingHandler
    public void on(RedeemedEvt evt) {
        log.debug("applying {}", evt);
        remainingValue -= evt.getAmount();
        log.debug("new remaining value: {}", remainingValue);
    }
...

命令和事件class在Kotlin代码中定义:

data class IssueCmd(@TargetAggregateIdentifier val id: String, val amount: Int)
data class IssuedEvt(val id: String, val amount: Int)
data class RedeemCmd(@TargetAggregateIdentifier val id: String, val amount: Int)
data class RedeemedEvt(val id: String, val amount: Int)

假设将以下两个命令放在命令总线上:

Command #     Command Class   id          amount
---------     -------------   -------     -------------
1             IssueCmd        QP34        123.45
2             RedeemCmd       QP34        38.10

在处理第一个命令时,IssueCmd 的 CommandHandler (CH) 会将一个 IssuedEvt 对象放在事件总线上。该事件将由 IssuedEvt 的 EventSourcingHandler (ESH) 处理。然后我们将有一个 GiftCard 的实例,其中 id 设置为“QP34”,remainingValue 设置为 123.45.

当处理第二个命令时,RedeemCmd 的 CH 将把一个 RedeemedEvt 对象放到事件总线上。该事件将由 RedeeemedEvt 的 ESH 处理。然后我们将有一个 GiftCard 的实例,其中 id 设置为“QP34”,remainingValue 设置为 85.35.

问题:每个事件都由其指定的 ESH 处理后,生成的对象实例如何以及在何处保留?

以前,我听到的答案是:确实没有。所有持久存在的都是事件对象,它们保存在 Axon 的事件存储中。当需要对象的当前状态时,Axon 告诉命令模型启动 GiftCard class 的实例,并且事件从最早到最新应用于它。这就是事件溯源的定义。

但是,当 Event Sourcing 处理完 IssuedEvt 后,remainingValue 中的 123.45 必须保留在某处,以便 ESH for RedeemedEvt 为其减法运算提供正确的值。

对象状态在 ESH 调用之间如何保存以及保存在哪里?

当您从 Repository.

检索 Aggregate 实例时,框架会在内部实例化一个 AnnotatedAggregate

AnnotatedAggregate class 实现 AggregateRepository 接口在 load(String) 操作上强制成为 return 类型.

当您谈论事件溯源时,正在使用的 Repository 实现是 EventSourcingRepository,它在 load(String) return 上是 EventSourcedAggregate实例(这是 AnnotatedAggregate.

的一个实现

Aggregate 接口,AnnotatedAggregate 该接口的实现和 EventSourcedAggregate 再次实现它,定义一个泛型。

这个泛型是你的聚合实现。

当您通过 EventSourcingRepository 对聚合进行事件溯源时,您的 聚合实例保存在内存中 AnnotatedAggregateprivate T aggregateRoot 全局字段下。

这个 aggregateRootEventSourcingRepository 更新,它通过给它一个 EventMessages.

的流来初始化你的 EventSourcedAggregate 的状态

顺便问一下,@JonathanM,你为什么对这个确切的部分感兴趣?

作为参考,这里是 GitHub 指向 class 的链接:

  1. Aggregate
  2. AnnotatedAggregate
  3. EventSourcedAggregate
  4. Repository
  5. EventSourcingRepository