在调用 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 实现 Aggregate
,Repository
接口在 load(String)
操作上强制成为 return 类型.
当您谈论事件溯源时,正在使用的 Repository
实现是 EventSourcingRepository
,它在 load(String)
return 上是 EventSourcedAggregate
实例(这是 AnnotatedAggregate
.
的一个实现
Aggregate
接口,AnnotatedAggregate
该接口的实现和 EventSourcedAggregate
再次实现它,定义一个泛型。
这个泛型是你的聚合实现。
当您通过 EventSourcingRepository
对聚合进行事件溯源时,您的 聚合实例保存在内存中 AnnotatedAggregate
在 private T aggregateRoot
全局字段下。
这个 aggregateRoot
由 EventSourcingRepository
更新,它通过给它一个 EventMessages
.
的流来初始化你的 EventSourcedAggregate
的状态
顺便问一下,@JonathanM,你为什么对这个确切的部分感兴趣?
作为参考,这里是 GitHub 指向 class 的链接:
在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 实现 Aggregate
,Repository
接口在 load(String)
操作上强制成为 return 类型.
当您谈论事件溯源时,正在使用的 Repository
实现是 EventSourcingRepository
,它在 load(String)
return 上是 EventSourcedAggregate
实例(这是 AnnotatedAggregate
.
Aggregate
接口,AnnotatedAggregate
该接口的实现和 EventSourcedAggregate
再次实现它,定义一个泛型。
这个泛型是你的聚合实现。
当您通过 EventSourcingRepository
对聚合进行事件溯源时,您的 聚合实例保存在内存中 AnnotatedAggregate
在 private T aggregateRoot
全局字段下。
这个 aggregateRoot
由 EventSourcingRepository
更新,它通过给它一个 EventMessages
.
EventSourcedAggregate
的状态
顺便问一下,@JonathanM,你为什么对这个确切的部分感兴趣?
作为参考,这里是 GitHub 指向 class 的链接: