Axon參考指南 - 6.命令處理 - State-Stored Aggregates(狀態存儲的聚合)

簡介

在“聚合”主頁中,我們看到了如何創建由“事件來源”支持的聚合。換句話說,事件源聚合的存儲方法是通過重放(事件重放)構成聚合上的更改的事件。

但是,聚合也可以原樣存儲。這樣做時,用於保存和加載聚合的存儲庫就是GenericJpaRepository。(疑問:每一次改變就保存一個聚合狀態?還是更新狀態?)狀態存儲的聚合的結構與事件來源的聚合略有不同:

import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.eventhandling.EventHandler;
import org.axonframework.modelling.command.AggregateIdentifier;
import org.axonframework.modelling.command.AggregateMember;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;

@Entity // 1.
/**
 * 由於聚合存儲在JPA存儲庫中,因此需要使用@Entity註釋該類。
 */
public class GiftCard {

    @Id // 2.
    /**
     * 聚合根必須聲明一個包含聚合標識符的字段。發佈第一個事件時,必須最遲初始化此標識符。
     * 該標識符字段必須由@AggregateIdentifier註釋進行註釋。
     * 使用JPA存儲聚合時,Axon知道使用JPA提供的@Id批註。由於Aggregate是實體,因此@Id註釋是一個硬要求。
     */
    @AggregateIdentifier
    private String id;

    // 3.
    /**
     * 該彙總有幾個“彙總成員”。由於聚合按原樣存儲,因此應考慮實體的正確映射。
     */
    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "giftCardId")
    @AggregateMember
    private List<GiftCardTransaction> transactions = new ArrayList<>();

    private int remainingValue;

    @CommandHandler  // 4.
    /**
     * @CommandHandler批註的構造函數,或以其他方式放置“命令處理構造函數”。
     * 此註釋告訴框架給定的構造函數能夠處理IssueCardCommand。
     */
    public GiftCard(IssueCardCommand cmd) {
        if (cmd.getAmount() <= 0) {
            throw new IllegalArgumentException("amount <= 0");
        }
        id = cmd.getCardId();
        remainingValue = cmd.getAmount();

        // 5.
        /**
         * 靜態AggregateLifecycle#apply(Object ...)可用於發佈事件消息。
         * 調用此函數後,提供的對象將在應用它們的聚合範圍內以EventMessages的形式發佈。
         */
        apply(new CardIssuedEvent(cmd.getCardId(), cmd.getAmount()));
    }

    @CommandHandler
    public void handle(RedeemCardCommand cmd) {
        // 6.
        /**
         * Command Handling方法將首先確定傳入的Command在這一點上是否有效。
         */
        if (cmd.getAmount() <= 0) {
            throw new IllegalArgumentException("amount <= 0");
        }
        if (cmd.getAmount() > remainingValue) {
            throw new IllegalStateException("amount > remaining value");
        }
        if (transactions.stream().map(GiftCardTransaction::getTransactionId).anyMatch(cmd.getTransactionId()::equals)) {
            throw new IllegalStateException("TransactionId must be unique");
        }

        // 7.
        /**
         * 驗證業務邏輯後,可以調整聚合狀態
         */
        remainingValue -= cmd.getAmount();
        transactions.add(new GiftCardTransaction(id, cmd.getTransactionId(), cmd.getAmount()));

        apply(new CardRedeemedEvent(id, cmd.getTransactionId(), cmd.getAmount()));
    }

    @EventHandler  // 8.
    /**
     * 通過定義@EventHandler帶註釋的方法,聚合中的實體可以偵聽聚合發佈的事件。
     * 當事件消息在被任何外部處理程序處理之前發佈時,將調用這些方法。
     */
    protected void on(CardReimbursedEvent event) {
        this.remainingValue += event.getAmount();
    }

    /**
     * JPA必需的無參數構造函數。
     * 如果不提供此構造函數,則會在加載Aggregate時導致異常。
     */
    protected GiftCard() { }  // 9.
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章