【问题标题】:How is the state of the entity being persisted between calls to EventSourcingHandlers?在对 EventSourcingHandlers 的调用之间,实体的状态如何保持?
【发布时间】:2019-03-04 16:58:36
【问题描述】:

Axon Giftcard demo 中,有一个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

在处理第一个命令时,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 类的实例,并将事件从最早到最晚应用于它。这就是事件溯源的定义。

但是,当事件溯源时,在处理 IssuedEvt 之后,remainingValue 中的 123.45 必须在某处持久化,以便 RedeemedEvt 的 ESH 具有正确的值为它的减法运算。

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

【问题讨论】:

    标签: java kotlin persistence axon


    【解决方案1】:

    当您从Repository 检索Aggregate 实例时,框架会在内部实例化AnnotatedAggregate

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

    正如您所说的事件溯源,所使用的Repository 实现是EventSourcingRepository,它在load(String) 上返回一个EventSourcedAggregate 实例(这是AnnotatedAggregate 的实现。

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

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

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

    这个aggregateRootEventSourcingRepository 更新,它通过给EventMessages 流初始化EventSourcedAggregate 的状态。

    顺便问一下,@JonathanM,你为什么对这个问题感兴趣?

    作为参考,这里是课程的 GitHub 链接:

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

    【讨论】:

    • 我仍在努力消化的好答案。 :) 询问是因为,与查询模型不同,命令模型永远似乎在任何地方持久化实例。
    • 感谢夸奖!可以理解的是,您对有关内部运作的一些背景感兴趣;当然,使用黑匣子并不总是令人愉快。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多