銀行賬戶轉賬案例是一個經典的領域驅動設計(DDD)應用場景。接下來我們通過一個簡單的銀行賬戶轉賬案例,來了解如何使用 Wow 進行領域驅動設計以及服務開發。
執行之後,存取 Swagger-UI : http://localhost:8080/swagger-ui.html 。
該 RESTful API 端點是由 Wow 自動生成的,無需手動編寫。
模組 | 說明 |
---|---|
example-transfer-api | API 層,定義聚合命令(Command)、領域事件(Domain Event)以及查詢檢視模型(Query View Model),這個模組充當了各個模組之間通訊的「釋出語言」。 |
example-transfer-domain | 領域層,包含聚合根和業務約束的實現。聚合根:領域模型的入口點,負責協調領域物件的操作。業務約束:包括驗證規則、領域事件的處理等。 |
example-transfer-server | 宿主服務,應用程式的啟動點。負責整合其他模組,並提供應用程式的入口。涉及設定依賴項、連線資料庫、啟動 API 服務 |
狀態聚合根(AccountState
)與命令聚合根(Account
)分離設計保證了在執行命令過程中,不會修改狀態聚合根的狀態。
AccountState
)建模public class AccountState implements Identifier {
private final String id;
private String name;
/**
* 餘額
*/
private long balanceAmount = 0L;
/**
* 已鎖定金額
*/
private long lockedAmount = 0L;
/**
* 賬號已凍結標記
*/
private boolean frozen = false;
@JsonCreator
public AccountState(@JsonProperty("id") String id) {
this.id = id;
}
@NotNull
@Override
public String getId() {
return id;
}
public String getName() {
return name;
}
public long getBalanceAmount() {
return balanceAmount;
}
public long getLockedAmount() {
return lockedAmount;
}
public boolean isFrozen() {
return frozen;
}
void onSourcing(AccountCreated accountCreated) {
this.name = accountCreated.name();
this.balanceAmount = accountCreated.balance();
}
void onSourcing(AmountLocked amountLocked) {
balanceAmount = balanceAmount - amountLocked.amount();
lockedAmount = lockedAmount + amountLocked.amount();
}
void onSourcing(AmountEntered amountEntered) {
balanceAmount = balanceAmount + amountEntered.amount();
}
void onSourcing(Confirmed confirmed) {
lockedAmount = lockedAmount - confirmed.amount();
}
void onSourcing(AmountUnlocked amountUnlocked) {
lockedAmount = lockedAmount - amountUnlocked.amount();
balanceAmount = balanceAmount + amountUnlocked.amount();
}
void onSourcing(AccountFrozen accountFrozen) {
this.frozen = true;
}
}
Account
)建模
@StaticTenantId
@AggregateRoot
public class Account {
private final AccountState state;
public Account(AccountState state) {
this.state = state;
}
AccountCreated onCommand(CreateAccount createAccount) {
return new AccountCreated(createAccount.name(), createAccount.balance());
}
@OnCommand(returns = {AmountLocked.class, Prepared.class})
List<?> onCommand(Prepare prepare) {
checkBalance(prepare.amount());
return List.of(new AmountLocked(prepare.amount()), new Prepared(prepare.to(), prepare.amount()));
}
private void checkBalance(long amount) {
if (state.isFrozen()) {
throw new IllegalStateException("賬號已凍結無法轉賬.");
}
if (state.getBalanceAmount() < amount) {
throw new IllegalStateException("賬號餘額不足.");
}
}
Object onCommand(Entry entry) {
if (state.isFrozen()) {
return new EntryFailed(entry.sourceId(), entry.amount());
}
return new AmountEntered(entry.sourceId(), entry.amount());
}
Confirmed onCommand(Confirm confirm) {
return new Confirmed(confirm.amount());
}
AmountUnlocked onCommand(UnlockAmount unlockAmount) {
return new AmountUnlocked(unlockAmount.amount());
}
AccountFrozen onCommand(FreezeAccount freezeAccount) {
return new AccountFrozen(freezeAccount.reason());
}
}
TransferSaga
)轉賬流程管理器(TransferSaga
)負責協調處理轉賬的事件,並生成相應的命令。
onEvent(Prepared)
: 訂閱轉賬已準備就緒事件(Prepared
),並生成入賬命令(Entry
)。onEvent(AmountEntered)
: 訂閱轉賬已入賬事件(AmountEntered
),並生成確認轉賬命令(Confirm
)。onEvent(EntryFailed)
: 訂閱轉賬入賬失敗事件(EntryFailed
),並生成解鎖金額命令(UnlockAmount
)。
@StatelessSaga
public class TransferSaga {
Entry onEvent(Prepared prepared, AggregateId aggregateId) {
return new Entry(prepared.to(), aggregateId.getId(), prepared.amount());
}
Confirm onEvent(AmountEntered amountEntered) {
return new Confirm(amountEntered.sourceId(), amountEntered.amount());
}
UnlockAmount onEvent(EntryFailed entryFailed) {
return new UnlockAmount(entryFailed.sourceId(), entryFailed.amount());
}
}
internal class AccountKTest {
@Test
fun createAccount() {
aggregateVerifier<Account, AccountState>()
.given()
.`when`(CreateAccount("name", 100))
.expectEventType(AccountCreated::class.java)
.expectState {
assertThat(it.name, equalTo("name"))
assertThat(it.balanceAmount, equalTo(100))
}
.verify()
}
}
作者:Ahoo Wang (阿虎)
Github: https://github.com/Ahoo-Wang/
SmartSql(高效能、高生產力,超輕量級的ORM!): https://github.com/Ahoo-Wang/SmartSql
SmartCode(不只是程式碼生成器!): https://github.com/Ahoo-Wang/SmartCode
CoSky 高效能、低成本微服務治理平臺 : https://github.com/Ahoo-Wang/CoSky
CosId 通用、靈活、高效能的分散式 ID 生成器 : https://github.com/Ahoo-Wang/CosId
Wow 基於 DDD、EventSourcing 的現代響應式 CQRS 架構微服務開發框架: https://github.com/Ahoo-Wang/Wow
CoSec 基於 RBAC 和策略的多租戶響應式安全框架: https://github.com/Ahoo-Wang/CoSec
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。