[英]Understanding of Axon Event Sourcing
I have been learning about axon and event sourcing and I think I have finally understood part of it and it makes sense in my head, but I want to make sure that my understanding of it is correct and that I am not making any mistakes.我一直在学习轴突和事件溯源,我想我终于理解了其中的一部分,这在我的脑海中是有道理的,但我想确保我对它的理解是正确的,并且我没有犯任何错误。 The code works, and I can see the events in my DOMAIN_EVENT_ENTRY table also.
该代码有效,我也可以在我的 DOMAIN_EVENT_ENTRY 表中看到事件。
I will post my code below (simple GiftCard example from the docs) and explain my thought process.我将在下面发布我的代码(文档中的简单礼品卡示例)并解释我的思考过程。 If I have not understood it correctly, please could you help me on understanding that part in the correct way.
如果我没有正确理解它,请你帮助我以正确的方式理解那部分。
I've not included the commands/events as they are very simple with fields for id,amount我没有包含命令/事件,因为它们非常简单,带有 id、amount 字段
First my code:首先我的代码:
TestRunner.java TestRunner.java
package com.example.demoaxon;
import java.util.UUID;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class TestRunner implements CommandLineRunner {
private final CommandGateway commandGateway;
@Autowired
public TestRunner(CommandGateway commandGateway) {
this.commandGateway = commandGateway;
}
@Override
public void run(String... args) throws Exception {
log.info("send command");
String id = UUID.randomUUID().toString();
commandGateway.sendAndWait(new IssueCardCommand(id,100));
commandGateway.sendAndWait(new RedeemCardCommand(id,90));
}
}
GiftCard.java礼品卡.java
package com.example.demoaxon;
import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.eventsourcing.EventSourcingHandler;
import org.axonframework.modelling.command.AggregateIdentifier;
import static org.axonframework.modelling.command.AggregateLifecycle.apply;
import org.axonframework.spring.stereotype.Aggregate;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@NoArgsConstructor
@Aggregate
@Slf4j
public class GiftCard {
@AggregateIdentifier
private String giftCardId;
private Integer amount;
@CommandHandler
public GiftCard(IssueCardCommand cmd) {
log.info("handling {}",cmd);
apply(new CardIssuedEvent(cmd.getCardId(),cmd.getAmount()));
}
@EventSourcingHandler
public void onCardIssuedEvent(CardIssuedEvent evt) {
log.info("applying {}",evt);
this.giftCardId = evt.getCardId();
this.amount = evt.getAmount();
}
@CommandHandler
public void redeemCardCommandHandler(RedeemCardCommand cmd) {
log.info("handling {}",cmd);
this.amount -= cmd.getAmount();
apply(new CardRedeemedEvent(cmd.getCardId(),cmd.getTransactionId(),this.amount));
}
@EventSourcingHandler
public void onCardRedeemedEvent(CardRedeemedEvent evt) {
log.info("applying {}",evt);
this.amount = evt.getAmount();
}
}
From what I can understand:据我了解:
In my TestRunner
class, the Command Gateway dispatches the issueCardCommand
to it's CommandHandler
using the command bus, which is then creating a new instance of the GiftCard
aggregate.在我的
TestRunner
class 中,命令网关使用命令总线将issueCardCommand
分派给它的CommandHandler
,然后创建GiftCard
聚合的新实例。 In this CommandHandler
we can perform any logic, and then we use this apply
method.在这个
CommandHandler
我们可以执行任何逻辑,然后我们使用这个apply
方法。
The apply(event)
method is used to publish the CardIssuedEvent
as an EventMessage
within the scope of the GiftCard
aggregate and it also invokes the EventSourcingHandler
for that particular event, so in this case onCardIssuedEvent
. apply(event)
方法用于在GiftCard
聚合的 scope CardIssuedEvent
作为EventMessage
发布,它还为该特定事件调用EventSourcingHandler
,因此在本例中onCardIssuedEvent
。 It publishes the EventMessage to the EventBus and is sent to EventHandlers.它将 EventMessage 发布到 EventBus 并发送到 EventHandlers。
In the @EventSourcingHandler onCardIssuedEvent
, we can make any state changes to the GiftCard
aggregate and we are also persisting the event to the DOMAIN_EVENT_ENTRY
table using spring Jpa.在
@EventSourcingHandler onCardIssuedEvent
中,我们可以对GiftCard
聚合进行任何 state 更改,我们还使用 spring ZFE034533A8EC0572 将事件持久化到DOMAIN_EVENT_ENTRY
表中
Once this CommandHandler
is finished executing, the aggregate object doesn't exist anymore.一旦这个
CommandHandler
完成执行,聚合 object 就不再存在了。
Now in my TestRunner
class again, the Command Gateway dispatches the RedeemCardCommand
to its CommandHandler
and as the first command no longer exists, the empty no args constructor is used to create the objects.现在再次在我的
TestRunner
class 中,命令网关将RedeemCardCommand
分派给它的CommandHandler
并且由于第一个命令不再存在,所以使用空的无参数构造函数来创建对象。 The axon framework retrieves all events from this DOMAIN_EVENT_ENTRY
table and it replays all the events (EventSourcingHandlers) for the GiftCard
aggregate instance in order to get it's current state (which is why the @AggregateIdentifier
is important). axon 框架从该
DOMAIN_EVENT_ENTRY
表中检索所有事件,并重放GiftCard
聚合实例的所有事件 (EventSourcingHandlers),以获取其当前的 state(这就是@AggregateIdentifier
很重要的原因)。
The RedeemCardCommandHandler
method is then executed, it performs any logic and applies the event, which is published within the aggregate and it invokes it's EventSourcingHandler
.然后执行
RedeemCardCommandHandler
方法,它执行任何逻辑并应用事件,该事件在聚合中发布并调用它的EventSourcingHandler
。 This EventSourcingHandler
then updates the state of the GiftCard
aggregate / persists to the DOMAIN_EVENT_ENTRY
table.然后,此
EventSourcingHandler
将GiftCard
聚合的 state 更新到DOMAIN_EVENT_ENTRY
表。
Is my understanding of how the event sourcing works correct?我对事件溯源如何工作的理解是否正确?
let me try to guide you through this journey!让我试着引导你完成这段旅程!
Your understanding is almost completely correct.你的理解几乎是完全正确的。 I would just like to split it into Event Sourcing and Axon for a better understanding.
我只想将其拆分为 Event Sourcing 和 Axon 以便更好地理解。
In short, Event Sourcing is a way of storing an application's state through the history of events that have happened in the past.简而言之,事件溯源是一种通过过去发生的事件的历史来存储应用程序的 state 的方式。 Keep in mind that you have other patterns as well, for example State Stored Aggregates.
请记住,您还有其他模式,例如 State 存储聚合。 In your example you are using Event Sourcing hence why
@EventSourcingHandler
s are in place.在您的示例中,您使用的是事件溯源,因此为什么要使用
@EventSourcingHandler
。 Now to the next topic.现在进入下一个话题。
I recommend you reading this awesome blog by one of our colleagues, specially the slide decks included on it, and you will see the journey of a message inside Axon Framework!我建议您阅读我们的一位同事撰写的这篇很棒的博客,特别是其中包含的幻灯片,您将看到 Axon Framework 中的消息之旅!
Now to your points, there are things I want to clarify upfront:现在说到你的观点,我想先澄清一些事情:
Command
and since the method annotated is a constructor, it will create an Aggregate for you.Command
,并且由于注释的方法是一个构造函数,它将为您创建一个聚合。 CommandHanlder
s are the right place for business logic and validations. CommandHanlder
是业务逻辑和验证的正确位置。apply
will publish the message internally (to this Aggregate
but also to their Entities
/ AggregateMember
s) and later on to the EventBus
.apply
将在内部发布消息(向这个Aggregate
也向他们的Entities
/ AggregateMember
发布消息),然后EventBus
。 From the javadoc:The event should be applied to the aggregate immediately and scheduled for publication to other event handlers.
该事件应立即应用于聚合并安排发布到其他事件处理程序。
Since we are talking about Event Sourcing, all the EventSourcingHandler
s will be called and the state of the Aggregate
modified/updated.由于我们在谈论事件溯源,所有
EventSourcingHandler
都将被调用,并且Aggregate
的 state 被修改/更新。 This is important because, as stated before, this is how you reconstruct your Aggregate state whenever needed.这很重要,因为如前所述,这是您在需要时重建聚合 state 的方式。 But the persisting of the Event does not occur here, it was already scheduled to happen when this process is done.
但是这里不会发生事件的持久化,它已经被安排在这个过程完成时发生。
Correct.正确的。
Also correct.也正确。 This has to do with Event Sourcing in general and how it reconstructs your
Aggregate
.这通常与 Event Sourcing 以及它如何重建您的
Aggregate
有关。
Also correct with the observations made on point 3, regarding when the Event is published/saved/persisted.关于事件何时发布/保存/持久的第 3 点的观察结果也更正。
Another small side note on your code: you are doing this on your @CommandHandler
关于您的代码的另一个小注释:您在
@CommandHandler
上执行此操作
@CommandHandler
public void redeemCardCommandHandler(RedeemCardCommand cmd) {
log.info("handling {}", cmd);
this.amount -= cmd.getAmount(); // you should not do this here
apply(new CardRedeemedEvent(cmd.getCardId(), cmd.getTransactionId(), this.amount));
}
This state change should be made in an @EventSourcingHandler
.此 state 更改应在
@EventSourcingHandler
中进行。 On the @CommandHandler
you should only do validation if this Aggregate has enough 'money' to be redeemed:)在
@CommandHandler
上,您应该仅在此聚合有足够的“钱”可以兑换时进行验证:)
yes, it looks like you are using the command gateway correctly, and the aggregates have the right methods annotated with the right stuff, You are using the Saga design pattern for Distributed Transactions which is much better than a 2PC (Two-phase commit).是的,看起来您正确使用了命令网关,并且聚合具有正确的方法并用正确的内容进行了注释,您正在使用分布式事务的 Saga 设计模式,这比 2PC(两阶段提交)要好得多。 so each step calling the next step is the right way of doing it.
所以每一步调用下一步都是正确的方法。
As long as the starting saga method is annotated with @StartSaga
, and the end one is annotated with @EndSaga
, then the steps will work in order, as well as rollback should work properly只要开始 saga 方法用
@StartSaga
注释,结束方法用@EndSaga
注释,那么步骤将按顺序进行,并且回滚应该正常工作
The saveAndWait
function actually returns a CompletableFuture
, so if you wanted to, you can step through the threads in debug mode and pause the thread until the whole saga is complete if that's what you'd like to do. saveAndWait
function 实际上返回一个CompletableFuture
,所以如果你愿意,你可以在调试模式下单步执行线程并暂停线程,直到整个 saga 完成,如果这是你想做的。
My only concern with is is - are you using the Axon Server as the Event Sourcer, or another Event Bus or sourcer that Axon Supports?我唯一关心的是 - 您是使用 Axon 服务器作为事件源,还是使用 Axon 支持的另一个事件总线或源? The only problem with the Standard Axon Server is that it's a
Free Tier
product, and they are offering a better, more supported version like Axon Server Enterprise
(which offers more features - better for infrastructure) - so if you are doing this for a company, then they might be willing to pay for the extra support in higher tier...标准 Axon Server 的唯一问题是它是
Free Tier
产品,并且他们提供更好、更受支持的版本,例如Axon Server Enterprise
(它提供更多功能 - 对基础设施更好) - 所以如果你是为公司做这件事,那么他们可能愿意为更高级别的额外支持付费......
Axon Framework does actually support Spring AMQP, which is useful because then, you can have better control over your own infrastructure rather than being tied down to a paid-for Axon Server
- which I don't is actually good or not. Axon Framework 确实支持 Spring AMQP,这很有用,因为这样,您可以更好地控制自己的基础架构,而不是被束缚在付费的
Axon Server
上——我实际上并不好或不好。
If you can get it to work with a custom Event Bus/Sourcer, then your system will be very good - in combination with the ease of developing on Axon.如果您可以让它与自定义事件总线/源程序一起工作,那么您的系统将非常好 - 结合在 Axon 上开发的便利性。
As long as the Aggregates have the right annotations on them, and you command gateway is autowired correctly, then everything should work fine.只要聚合上有正确的注释,并且您命令网关正确自动装配,那么一切都应该正常工作。 My only worry is that there isn't yet enough support for
Axon Framework
yet...我唯一担心的是对
Axon Framework
的支持还不够……
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.