Axon Framework官方文档(三)

3.Messaging concepts(消息概念)

Axon的核心概念之一是消息传递。组件之间的所有通信都是使用消息对象完成的。这种模式为这些组件提供了位置透明性,以便在必要时能够伸缩和分发这些组件。
尽管所有这些消息都实现了Message接口,但不同类型的消息和如何对待它们之间存在明显的区别。
所有消息都包含有效负载(payload)、元数据(META-DATA)和惟一标识符。消息的有效负载是这条消息的含义的功能描述。这个对象的类名和它所承载的数据的组合,描述了应用程序中该消息的含义。元数据允许您描述正在发送的这条消息的上下文。例如,您可以存储跟踪信息,以允许跟踪消息的起源或原因。您还可以存储信息来描述正在执行的命令的安全上下文。
Note:
请注意,所有消息都是不可变的。在消息中存储数据实际上意味着基于前一个消息创建新的消息,并添加额外的信息。这保证了消息在多线程和分布式环境中是安全的。

3.1Commands(命令)

命令描述了更改应用程序状态的意图。它们被实现为(最好实现为只读的)pojo,这些pojo使用一个CommandMessage的实现类来包装。
命令总是只有一个目的地。虽然发送方不关心哪个组件处理命令以及这个组件驻留在何处,但是它可能会对命令执行的结果感兴趣。这就是为什么在命令总线上发送的命令消息允许返回结果的原因。

3.2 Event(事件)

Event(事件)是描述应用程序中已经发生了的事情的对象。Event(事件)的典型来源是Aggregate(聚合)。当在Aggregate(聚合)上发生了一些很重要的事情的时候,它就会引发一个Event(事件)。在Axon框架中,Event(事件)可以是任何对象。高度鼓励您确保所有事件都是可序列化的。
当Event(事件)被分派时,Axon将它们包裹在EventMessage中。实际使用的Message类型取决于Event(事件)的来源。当一个事件是由于Aggregate(聚合)产生的时候,它被包裹在一个DomainEventMessage中(它继承自EventMessage)。所有其他Event(事件)都包在EventMessage中。除了通用的消息属性(比如惟一标识符)之外,EventMessage还包含一个时间戳属性。此外,DomainEventMessage还包含了引发事件的聚合的类型和标识符。它还包含了事件在聚合的event strem(事件流)中的序列号,这个序列号可以用作事件的重现(如果读过IDDD的话,应该知道作者所一直强调的Event Source源的优点之一,便是便于事件的重现)。
Note:
即使DomainEventMessage包含对聚合标识符的引用,也应该始终在实际的事件本身中包含标识符。DomainEventMessage中的标识符被EventStore用于存储事件,并可能不总是为其他目的提供可靠的值。

原始的Event(事件)对象存储为EventMessage的payload(有效负载)。在有效负载的旁边,您可以将信息存储在EventMessage的META-DATA(元数据)中。META-DATA(元数据)的意图是存储关于一个不以业务信息为主的事件的附加信息。审计信息就是一个典型的例子。它允许您查看在哪些情况下引发了事件,例如触发处理的用户帐户,或者处理事件的机器的名称。
Note:
一般来说,您不应该基于事件消息的元数据进行业务决策。如果是这样的话,你可能会附上一些信息,这些信息实际上应该是事件本身的一部分。元数据通常用于报告、审计和跟踪。

虽然没有强制的要求,但是很好的做法是使领域事件是不可变的,最好是将所有字段都设置为final,并在构造函数内初始化事件。如果事件构造过于繁琐,请考虑使用Builder(构建器,设计模式之一)模式。
Note:
尽管从技术上讲,领域事件表示状态更改,但您也应该尝试捕获事件状态的意图。一个好的实践是使用领域事件的抽象实现来捕获某个状态已经更改的事实,并使用该抽象类的具体子实现来表示更改的意图。例如,您可以有一个抽象的AddressChangedEvent(地址改变事件),以及两个实现ContactMovedEvent和AddressCorrectedEvent,以捕获状态更改的意图。有些侦听器并不关心意图(例如数据库更新),它们只监听抽象类型。然而其他的则关心更改的意图,于是它们监听在抽象类型的子类型上(例如,向客户发送地址更改确认邮件,那么它需要关心的则是AddressCorrectedEvent事件,而不仅仅是抽象类型)。

Axon Framework官方文档(三)_第1张图片

当在事件总线上发送事件时,您需要将其包装在事件消息中。GenericEventMessage是一个EventMessage实现,允许您在消息中包装事件。可以使用构造函数或静态asEventMessage()方法。后者检查给定参数是否已经实现Message接口。如果是这样,它要么直接返回(如果它实现EventMessage情况下),要么使用给定消息的有效负载和元数据返回一个新的GenericEventMessage。如果一个事件被聚合应用(发布),Axon则会自动将事件包装在一个包含聚合标识符、类型和序列号的DomainEventMessage中。

3.3 Unit of Work(工作单元)

Unit of Work(工作单元)是Axon框架中的一个重要概念,不过在大多数情况下,您不太可能直接与它交互。消息的处理被视为单个单元。Unit of Work(工作单元)的目的是协调在消息处理过程中执行的操作(命令或事件)。组件可以Unit of Work(工作单元)的每个阶段上注册执行的操作,比如onPrepareCommit或onCleanup。
你不太可能需要直接访问Unit of Work(工作单元)。它主要用于Axon提供的构建块。如果你确实需要访问它,不管出于什么原因,有一些方法可以获得它。处理器Handler通过处理方法的参数接收Unit of Work(工作单元)。如果你开启了注解驱动,那么你可以向注解标注的方法中添加一个UnitOfWork类型的参数。在其他位置,您可以通过调用CurrentUnitOfWork.get()来检索绑定到当前线程的工作单元。注意,如果没有绑定到当前线程的工作单元,此方法将抛出异常。你可以使用CurrentUnitOfWork.isStarted()来检查当前是否有可用的工作单元。
需要访问当前工作单元的一个原因是在消息处理过程中需要多次使用需要重用的资源,或者在工作单元完成时需要清理创建的资源。在这样的情况下,unitOfWork.getOrComputeResource()和生命周期回调方法,如onRollback(),afterCommit()和onCleanup()允许您在此工作单元的处理过程中,声明采取的行动。
Note:
注意,工作单元仅仅是更改的缓冲区,而不是事务的代替者。尽管所有的状态更改都只在提交工作单元时提交,但它的提交不是原子的。这意味着当一个提交失败时,一些更改可能被持久化,而另一些则没有。最佳实践规定,命令不应该包含多个动作。如果你坚持这种做法,一个工作单元将会只包含一个单独的操作,使它可以安全地使用。如果你在工作单位有更多的动作,你可以考虑将一个Transaction(事务)附加到Unit of Work的提交中。使用unitofwork.oncommit(. .)来注册在提交工作单元时需要采取的操作。

处理消息时,处理程序可能会抛出异常。默认情况下,未经检查的异常将导致UnitOfWork回滚所有更改。因此,调度的副作用被取消了。
Axon提供了一些开箱即用的回滚策略:
>RollbackConfigurationType.NEVER,工作的单位总是会提交
>RollbackConfigurationType.ANY_THROWABLE:将总是在异常发生时回滚
>RollbackConfigurationType.UNCHECKED_EXCEPTIONS:将在Error或者运行时异常时回滚
>RollbackConfigurationType.RUNTIME_EXCEPTION:仅仅在运行时异常时候回滚(不包括Error)
当使用Axon组件处理消息时,将自动为您管理工作单元的生命周期。如果您选择不使用这些组件,而是自己实现处理,您将需要以编程方式启动和提交(或回滚)一个工作单元。
在大多数情况下,DefaultUnitOfWork将为您提供所需的功能。它期望处理在一个线程内发生。在工作单元的上下文中执行任务,只需要简单地在新建的DefaultUnitOfWork上调用UnitOfWork.execute(Runnable)或UnitOfWork.executeWithResult。当任务完成时,工作单元将启动并提交,或者在任务失败时回滚。如果需要更多的控制,您还可以选择手动启动、提交或回滚工作单元。
典型用法如下:
UnitOfWork uow = DefaultUnitOfWork.startAndGet(message);
// then, either use the autocommit approach:
uow.executeWithResult(() -> ... logic here);

// or manually commit or rollback:
try {
    // business logic comes here
    uow.commit();
} catch (Exception e) {
    uow.rollback(e);
    // maybe rethrow...
}
一个工作单元了解各个阶段。每当它进展到不同的阶段时,就会通知UnitOfWork监听器。
>活动阶段:这是开始工作单元的地方。工作单元通常在当前线程中的这个阶段被注册(通过CurrentUnitOfWork.set(UnitOfWork))。随后,消息通常在此阶段由消息处理器处理。
>提交阶段:在处理消息后,在工作单元提交之前,将调用onPrepareCommit侦听器。如果一个工作单元绑定到一个事务,则调用onCommit侦听器来提交任何支持事务。当commit成功时,将调用afterCommit侦听器。如果提交失败,则调用onRollback侦听器。如果可用的话,消息处理程序的结果包含在工作单元的执行结果中。
>清理阶段:这是该工作单元(如锁)所持有的任何资源都将被释放的阶段。如果多个工作单元被嵌套,清理阶段将被推迟,直到工作的外部单元准备好清理为止。

信息处理过程可以看作是一个原子过程;它要么完全被处理,要么根本不被处理。Axon框架使用工作单元来跟踪消息处理程序执行的操作。处理程序完成后,Axon将尝试提交以工作单元注册的操作。
将事务绑定到工作单元是可能的。许多组件,例如CommandBus的实现和所有异步处理事件处理器,都允许你配置事务管理器(Transaction Manager)。然后,该事务管理器将被用于创建事务,以绑定到用于管理消息处理的工作单元。
当应用程序组件在消息处理的不同阶段需要资源时,例如数据库连接或实体管理器(EntityManager),这些资源可以附加到工作单元。getresources()方法允许您访问连接到当前工作单元的资源。有几个辅助方法可以直接在工作单元上使用,以便更容易地使用资源。
当嵌套的工作单元需要能够访问资源时,建议在工作的根单元上注册它,它可以使用unitofwork.root()访问。如果工作单元是根的话,它将简单地返回自己。

你可能感兴趣的:(axon)