国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之銀行轉(zhuǎn)賬:Wow框架實(shí)戰(zhàn)

這篇具有很好參考價(jià)值的文章主要介紹了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之銀行轉(zhuǎn)賬:Wow框架實(shí)戰(zhàn)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之銀行轉(zhuǎn)賬:Wow框架實(shí)戰(zhàn)

銀行賬戶轉(zhuǎn)賬案例是一個(gè)經(jīng)典的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)應(yīng)用場(chǎng)景。接下來(lái)我們通過(guò)一個(gè)簡(jiǎn)單的銀行賬戶轉(zhuǎn)賬案例,來(lái)了解如何使用 Wow 進(jìn)行領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)以及服務(wù)開發(fā)。

銀行轉(zhuǎn)賬流程

  1. 準(zhǔn)備轉(zhuǎn)賬(Prepare): 用戶發(fā)起轉(zhuǎn)賬請(qǐng)求,觸發(fā) Prepare 步驟。這個(gè)步驟會(huì)向源賬戶發(fā)送準(zhǔn)備轉(zhuǎn)賬的請(qǐng)求。
  2. 校驗(yàn)余額(CheckBalance): 源賬戶在收到準(zhǔn)備轉(zhuǎn)賬請(qǐng)求后,會(huì)執(zhí)行校驗(yàn)余額的操作,確保賬戶有足夠的余額進(jìn)行轉(zhuǎn)賬。
  3. 鎖定金額(LockAmount): 如果余額足夠,源賬戶會(huì)鎖定轉(zhuǎn)賬金額,防止其他操作干擾。
  4. 入賬(Entry): 接著,轉(zhuǎn)賬流程進(jìn)入到目標(biāo)賬戶,執(zhí)行入賬操作。
  5. 確認(rèn)轉(zhuǎn)賬(Confirm): 如果入賬成功,確認(rèn)轉(zhuǎn)賬;否則,執(zhí)行解鎖金額操作。
    1. 成功路徑(Success): 如果一切順利,完成轉(zhuǎn)賬流程。
    2. 失敗路徑(Fail): 如果入賬失敗,執(zhí)行解鎖金額操作,并處理失敗情況。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之銀行轉(zhuǎn)賬:Wow框架實(shí)戰(zhàn)

運(yùn)行案例

  • 運(yùn)行 TransferExampleServer.java
  • 查看 Swagger-UI : http://localhost:8080/swagger-ui.html
  • 執(zhí)行 API 測(cè)試:Transfer.http

自動(dòng)生成 API 端點(diǎn)

運(yùn)行之后,訪問(wèn) Swagger-UI : http://localhost:8080/swagger-ui.html 。
該 RESTful API 端點(diǎn)是由 Wow 自動(dòng)生成的,無(wú)需手動(dòng)編寫。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之銀行轉(zhuǎn)賬:Wow框架實(shí)戰(zhàn)

模塊劃分

模塊 說(shuō)明
example-transfer-api API 層,定義聚合命令(Command)、領(lǐng)域事件(Domain Event)以及查詢視圖模型(Query View Model),這個(gè)模塊充當(dāng)了各個(gè)模塊之間通信的“發(fā)布語(yǔ)言”。
example-transfer-domain 領(lǐng)域?qū)樱酆细蜆I(yè)務(wù)約束的實(shí)現(xiàn)。聚合根:領(lǐng)域模型的入口點(diǎn),負(fù)責(zé)協(xié)調(diào)領(lǐng)域?qū)ο蟮牟僮鳌I(yè)務(wù)約束:包括驗(yàn)證規(guī)則、領(lǐng)域事件的處理等。
example-transfer-server 宿主服務(wù),應(yīng)用程序的啟動(dòng)點(diǎn)。負(fù)責(zé)整合其他模塊,并提供應(yīng)用程序的入口。涉及配置依賴項(xiàng)、連接數(shù)據(jù)庫(kù)、啟動(dòng) API 服務(wù)

領(lǐng)域建模

狀態(tài)聚合根(AccountState)與命令聚合根(Account)分離設(shè)計(jì)保證了在執(zhí)行命令過(guò)程中,不會(huì)修改狀態(tài)聚合根的狀態(tài)。

狀態(tài)聚合根(AccountState)建模

public class AccountState implements Identifier {
    private final String id;
    private String name;
    /**
     * 余額
     */
    private long balanceAmount = 0L;
    /**
     * 已鎖定金額
     */
    private long lockedAmount = 0L;
    /**
     * 賬號(hào)已凍結(jié)標(biāo)記
     */
    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("賬號(hào)已凍結(jié)無(wú)法轉(zhuǎn)賬.");
        }
        if (state.getBalanceAmount() < amount) {
            throw new IllegalStateException("賬號(hào)余額不足.");
        }
    }

    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());
    }
}

轉(zhuǎn)賬流程管理器(TransferSaga)

轉(zhuǎn)賬流程管理器(TransferSaga)負(fù)責(zé)協(xié)調(diào)處理轉(zhuǎn)賬的事件,并生成相應(yīng)的命令。

  • onEvent(Prepared): 訂閱轉(zhuǎn)賬已準(zhǔn)備就緒事件(Prepared),并生成入賬命令(Entry)。
  • onEvent(AmountEntered): 訂閱轉(zhuǎn)賬已入賬事件(AmountEntered),并生成確認(rèn)轉(zhuǎn)賬命令(Confirm)。
  • onEvent(EntryFailed): 訂閱轉(zhuǎn)賬入賬失敗事件(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());
    }
}

單元測(cè)試

借助 Wow 單元測(cè)試套件,可以輕松的編寫聚合根和 Saga 的單元測(cè)試。從而提升代碼覆蓋率,保證代碼質(zhì)量。

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之銀行轉(zhuǎn)賬:Wow框架實(shí)戰(zhàn)

使用 aggregateVerifier 進(jìn)行聚合根單元測(cè)試,可以有效的減少單元測(cè)試的編寫工作量。

Account 聚合根單元測(cè)試

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()
    }

    @Test
    fun prepare() {
        aggregateVerifier<Account, AccountState>()
            .given(AccountCreated("name", 100))
            .`when`(Prepare("name", 100))
            .expectEventType(AmountLocked::class.java, Prepared::class.java)
            .expectState {
                assertThat(it.name, equalTo("name"))
                assertThat(it.balanceAmount, equalTo(0))
            }
            .verify()
    }

    @Test
    fun entry() {
        val aggregateId = GlobalIdGenerator.generateAsString()
        aggregateVerifier<Account, AccountState>(aggregateId)
            .given(AccountCreated("name", 100))
            .`when`(Entry(aggregateId, "sourceId", 100))
            .expectEventType(AmountEntered::class.java)
            .expectState {
                assertThat(it.name, equalTo("name"))
                assertThat(it.balanceAmount, equalTo(200))
            }
            .verify()
    }

    @Test
    fun entryGivenFrozen() {
        val aggregateId = GlobalIdGenerator.generateAsString()
        aggregateVerifier<Account, AccountState>(aggregateId)
            .given(AccountCreated("name", 100), AccountFrozen(""))
            .`when`(Entry(aggregateId, "sourceId", 100))
            .expectEventType(EntryFailed::class.java)
            .expectState {
                assertThat(it.name, equalTo("name"))
                assertThat(it.balanceAmount, equalTo(100))
                assertThat(it.isFrozen, equalTo(true))
            }
            .verify()
    }

    @Test
    fun confirm() {
        val aggregateId = GlobalIdGenerator.generateAsString()
        aggregateVerifier<Account, AccountState>(aggregateId)
            .given(AccountCreated("name", 100), AmountLocked(100))
            .`when`(Confirm(aggregateId, 100))
            .expectEventType(Confirmed::class.java)
            .expectState {
                assertThat(it.name, equalTo("name"))
                assertThat(it.balanceAmount, equalTo(0))
                assertThat(it.lockedAmount, equalTo(0))
                assertThat(it.isFrozen, equalTo(false))
            }
            .verify()
    }

    @Test
    fun unlockAmount() {
        val aggregateId = GlobalIdGenerator.generateAsString()
        aggregateVerifier<Account, AccountState>(aggregateId)
            .given(AccountCreated("name", 100), AmountLocked(100))
            .`when`(UnlockAmount(aggregateId, 100))
            .expectEventType(AmountUnlocked::class.java)
            .expectState {
                assertThat(it.name, equalTo("name"))
                assertThat(it.balanceAmount, equalTo(100))
                assertThat(it.lockedAmount, equalTo(0))
                assertThat(it.isFrozen, equalTo(false))
            }
            .verify()
    }

    @Test
    fun freezeAccount() {
        val aggregateId = GlobalIdGenerator.generateAsString()
        aggregateVerifier<Account, AccountState>(aggregateId)
            .given(AccountCreated("name", 100))
            .`when`(FreezeAccount(""))
            .expectEventType(AccountFrozen::class.java)
            .expectState {
                assertThat(it.name, equalTo("name"))
                assertThat(it.balanceAmount, equalTo(100))
                assertThat(it.lockedAmount, equalTo(0))
                assertThat(it.isFrozen, equalTo(true))
            }
            .verify()
    }
}

使用 sagaVerifier 進(jìn)行 Saga 單元測(cè)試,可以有效的減少單元測(cè)試的編寫工作量。

TransferSaga 單元測(cè)試文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-746672.html

internal class TransferSagaTest {

    @Test
    fun onPrepared() {
        val event = Prepared("to", 1)
        sagaVerifier<TransferSaga>()
            .`when`(event)
            .expectCommandBody<Entry> {
                assertThat(it.id, equalTo(event.to))
                assertThat(it.amount, equalTo(event.amount))
            }
            .verify()
    }

    @Test
    fun onAmountEntered() {
        val event = AmountEntered("sourceId", 1)
        sagaVerifier<TransferSaga>()
            .`when`(event)
            .expectCommandBody<Confirm> {
                assertThat(it.id, equalTo(event.sourceId))
                assertThat(it.amount, equalTo(event.amount))
            }
            .verify()
    }

    @Test
    fun onEntryFailed() {
        val event = EntryFailed("sourceId", 1)
        sagaVerifier<TransferSaga>()
            .`when`(event)
            .expectCommandBody<UnlockAmount> {
                assertThat(it.id, equalTo(event.sourceId))
                assertThat(it.amount, equalTo(event.amount))
            }
            .verify()
    }
}

到了這里,關(guān)于領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之銀行轉(zhuǎn)賬:Wow框架實(shí)戰(zhàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 該怎么用設(shè)計(jì)測(cè)試用例測(cè)網(wǎng)上銀行轉(zhuǎn)賬?

    目錄 前言 1、網(wǎng)上銀行轉(zhuǎn)賬是怎么測(cè)的,設(shè)計(jì)一下測(cè)試用例。 回答思路: 2、測(cè)試工作的流程?缺陷狀態(tài)有什么?設(shè)計(jì)測(cè)試用例有幾種方法? 修改完以后,有兩種處理情況: 3、在項(xiàng)目中找到的經(jīng)典BUG是什么? 4、定期存款到期自動(dòng)轉(zhuǎn)存該怎么測(cè)?

    2024年02月08日
    瀏覽(16)
  • 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)——DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)進(jìn)階

    領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)——DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)進(jìn)階

    進(jìn)階篇主要講解領(lǐng)域事件、DDD 分層架構(gòu)、幾種常見(jiàn)的微服務(wù)架構(gòu)模型以及中臺(tái)設(shè)計(jì)思想等內(nèi)容。如何通過(guò)領(lǐng)域事件實(shí)現(xiàn)微服務(wù)解耦?、怎樣進(jìn)行微服務(wù)分層設(shè)計(jì)?、如何實(shí)現(xiàn)層與層之間的服務(wù)協(xié)作?、通過(guò)幾種微服務(wù)架構(gòu)模型的對(duì)比分析,讓你了解領(lǐng)域模型和微服務(wù)分層的作

    2024年01月15日
    瀏覽(21)
  • 《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》:從領(lǐng)域視角深入倉(cāng)儲(chǔ)(Repository)的設(shè)計(jì)和實(shí)現(xiàn)

    《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》:從領(lǐng)域視角深入倉(cāng)儲(chǔ)(Repository)的設(shè)計(jì)和實(shí)現(xiàn)

    一、前言 “ DDD設(shè)計(jì)的目標(biāo)是關(guān)注領(lǐng)域模型而并非技術(shù)來(lái)創(chuàng)建更好的軟件,假設(shè)開發(fā)人員構(gòu)建了一個(gè)SQL,并將它傳遞給基礎(chǔ)設(shè)施層中的某個(gè)查詢服務(wù)然后根據(jù)表數(shù)據(jù)的結(jié)構(gòu)集取出所需信息,最后將這些信息提供給構(gòu)造函數(shù)或者Factory,開發(fā)人員在做這一切的時(shí)候早已不把模型看

    2024年02月08日
    瀏覽(20)
  • 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之認(rèn)知篇

    領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之認(rèn)知篇

    作為技術(shù)人,都有一個(gè)成為大牛的夢(mèng)。 有些人可以通過(guò)自己掌握了比較底層、有深度、有難度的技術(shù)來(lái)證明自己的能力。 但對(duì)于絕大多數(shù)的應(yīng)用研發(fā)工程師來(lái)說(shuō),其大部分的時(shí)間精力,會(huì)被消耗在讀不懂、講不清的屎山代碼中,以及復(fù)雜多變的業(yè)務(wù)迭代中。很少會(huì)有需要去

    2024年02月08日
    瀏覽(22)
  • 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)四論

    領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)四論

    ? ? 經(jīng)過(guò)多年的研究與思考,實(shí)踐與總結(jié),本人逐漸對(duì) DDD 有所領(lǐng)悟,本文以一個(gè)較短的篇幅,提綱挈領(lǐng)地梳理出 DDD 的核心脈絡(luò),希望與各位做一探討。 1776 年亞當(dāng)斯密發(fā)表《國(guó)富論》,標(biāo)志著經(jīng)濟(jì)學(xué)的誕生。2004 年,一本名為《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)·軟件核心復(fù)雜性應(yīng)對(duì)之道》的

    2024年02月08日
    瀏覽(23)
  • DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(六)

    領(lǐng)域?qū)ο笮枰Y源存儲(chǔ)。存儲(chǔ)手段多樣化,常見(jiàn)就是數(shù)據(jù)庫(kù),分布式緩存,localCache.資源庫(kù)的作用,就是對(duì)領(lǐng)域的存儲(chǔ)和訪問(wèn)進(jìn)行統(tǒng)一管理對(duì)象。在抽獎(jiǎng)平臺(tái)中。通過(guò)下面這種方式組織資源庫(kù)。

    2024年01月24日
    瀏覽(22)
  • 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)入門指南

    領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)入門指南

    ? 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(Domain-Driven Design,簡(jiǎn)稱DDD)是一種軟件架構(gòu)風(fēng)格,它強(qiáng)調(diào)在軟件開發(fā)過(guò)程中緊密關(guān)注業(yè)務(wù)需求和領(lǐng)域知識(shí)。本文將簡(jiǎn)要介紹領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的核心概念,幫助人開始學(xué)習(xí)和實(shí)踐領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)。 什么是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)? 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)是一種軟件開發(fā)方法,它側(cè)重

    2024年02月11日
    瀏覽(30)
  • 05.領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):掌握領(lǐng)域事件,解耦微服務(wù)的關(guān)鍵

    05.領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):掌握領(lǐng)域事件,解耦微服務(wù)的關(guān)鍵

    目錄 1、概述 2、領(lǐng)域事件 2.1 如何識(shí)別領(lǐng)域事件 1.微服務(wù)內(nèi)的領(lǐng)域事件 2.微服務(wù)之間的領(lǐng)域事件 3、領(lǐng)域事件總體架構(gòu) 3.1 事件構(gòu)建和發(fā)布 3.2 事件數(shù)據(jù)持久化 3.3 事件總線 (EventBus) 3.4 消息中間件 3.5 事件接收和處理 4、案例 5、總結(jié) 1、概述 在事件風(fēng)暴(Event Storming)時(shí),我們

    2024年02月22日
    瀏覽(19)
  • 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(Domain Driven Design)之建立領(lǐng)域模型

    在實(shí)際項(xiàng)目中,模型設(shè)計(jì)者往往過(guò)早陷入具體構(gòu)造塊類型的識(shí)別,比如實(shí)體、聚合、領(lǐng)域服務(wù),而忽略了領(lǐng)域模型表達(dá)領(lǐng)域概念的目的。我們應(yīng)該基于領(lǐng)域概念設(shè)計(jì)領(lǐng)域模型,然后再采用合適的模式降低領(lǐng)域模型的復(fù)雜度,進(jìn)一步增加領(lǐng)域模型的表達(dá)能力。 領(lǐng)域模型的作用,

    2024年02月03日
    瀏覽(18)
  • 聊一聊對(duì)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中“領(lǐng)域”這個(gè)詞語(yǔ)的理解與分析方法

    百度百科對(duì)領(lǐng)域的解釋: 領(lǐng)域具體指一種特定的范圍或區(qū)域 領(lǐng)域一般指的是業(yè)務(wù)的問(wèn)題域,領(lǐng)域是有邊界的,邊界內(nèi),規(guī)定了我們要做什么,要做的范圍,軟件項(xiàng)目從開始到交付的過(guò)程中, 所有涵蓋的業(yè)務(wù),每個(gè)業(yè)務(wù)模塊或者方向都有自己的業(yè)務(wù)范圍和問(wèn)題 比如做家裝行業(yè)

    2023年04月14日
    瀏覽(23)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包