首页 > 解决方案 > 在对 EventSourcingHandlers 的调用之间,实体的状态如何保持?

问题描述

Axon Giftcard 演示中,有一个GiftCard类被注释为 @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);
    }
...

命令和事件类在 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

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

在处理第二个命令时,CH forRedeemCmd会将一个RedeemedEvt对象放在事件总线上。该事件将由 ESH 为RedeeemedEvt. 然后我们将有一个GiftCardwithid设置为“ QP34 ”并remainingValue设置为85.35的实例。

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

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

但是,在IssuedEvt处理事件溯源时,必须将123.45 inremainingValue保留在某处,以使 ESH 的RedeemedEvt减法运算具有正确的值。

在对 ESH 的调用之间,对象状态如何以及在何处保持?

标签: javakotlinpersistenceaxon

解决方案


AnnotatedAggregateAggregateRepository.

该类AnnotatedAggregate实现了AggregateRepository接口强制将其作为load(String)操作的返回类型。

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

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

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

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

aggregateRoot是由 更新的,它通过给它一个流来EventSourcingRepository初始化你的状态。EventSourcedAggregateEventMessages

顺便说一句,你为什么对这个确切的位感兴趣,@JonathanM?

作为参考,这里是这些类的 GitHub 链接:

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

推荐阅读