天天看點

Axon Framework官方文檔(三)3.Messaging concepts(消息概念)

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官方文檔(三)3.Messaging concepts(消息概念)
當在事件總線上發送事件時,您需要将其包裝在事件消息中。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()通路。如果工作單元是根的話,它将簡單地傳回自己。
           

繼續閱讀