簡介
在“聚合”主頁中,我們看到了如何創建由“事件來源”支持的聚合。換句話說,事件源聚合的存儲方法是通過重放(事件重放)構成聚合上的更改的事件。
但是,聚合也可以原樣存儲。這樣做時,用於保存和加載聚合的存儲庫就是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.
}