简体   繁体   English

了解 Axon 事件溯源

[英]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:据我了解:

  1. 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方法。

  2. 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。

  3. 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表中

  4. Once this CommandHandler is finished executing, the aggregate object doesn't exist anymore.一旦这个CommandHandler完成执行,聚合 object 就不再存在了。

  5. 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很重要的原因)。

  6. 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.然后,此EventSourcingHandlerGiftCard聚合的 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 以便更好地理解。

  • Event Sourcing事件溯源

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.现在进入下一个话题。

  • Axon轴突

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:现在说到你的观点,我想先澄清一些事情:

  1. Correct, you dispatch a 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是业务逻辑和验证的正确位置。
  2. Here the 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:从javadoc:

The event should be applied to the aggregate immediately and scheduled for publication to other event handlers.该事件应立即应用于聚合并安排发布到其他事件处理程序。

  1. 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.但是这里不会发生事件的持久化,它已经被安排在这个过程完成时发生。

  2. Correct.正确的。

  3. Also correct.也正确。 This has to do with Event Sourcing in general and how it reconstructs your Aggregate .这通常与 Event Sourcing 以及它如何重建您的Aggregate有关。

  4. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM