偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

SpringBoot與Axon Framework整合,實(shí)現(xiàn)事件溯源驅(qū)動(dòng)的分布式業(yè)務(wù)系統(tǒng)

開(kāi)發(fā) 前端
Axon Framework 是一個(gè)用于構(gòu)建復(fù)雜分布式系統(tǒng)的開(kāi)源框架,特別適用于實(shí)現(xiàn)事件溯源(Event Sourcing)和命令查詢責(zé)任分離(CQRS)模式,提供強(qiáng)大的工具來(lái)簡(jiǎn)化事件驅(qū)動(dòng)架構(gòu)的開(kāi)發(fā)。

Axon Framework 是一個(gè)用于構(gòu)建復(fù)雜分布式系統(tǒng)的開(kāi)源框架,特別適用于實(shí)現(xiàn)事件溯源(Event Sourcing)和命令查詢責(zé)任分離(CQRS)模式,提供強(qiáng)大的工具來(lái)簡(jiǎn)化事件驅(qū)動(dòng)架構(gòu)的開(kāi)發(fā)。

選擇Axon Framework的理由

1. 事件溯源(Event Sourcing)

  • 數(shù)據(jù)完整性: 事件溯源通過(guò)記錄每個(gè)業(yè)務(wù)操作的變化事件來(lái)保持?jǐn)?shù)據(jù)的完整性和一致性。這對(duì)于金融系統(tǒng)尤為重要,因?yàn)樗枰_跟蹤每一筆交易的歷史記錄。
  • 審計(jì)和合規(guī)性: 銀行業(yè)務(wù)對(duì)審計(jì)和合規(guī)性有嚴(yán)格的要求。事件溯源可以幫助我們輕松地重建歷史狀態(tài),并提供詳細(xì)的變更日志。

2. CQRS 模式(Command Query Responsibility Segregation)

  • 分離讀寫(xiě)操作: CQRS 將讀操作和寫(xiě)操作分開(kāi),使得系統(tǒng)可以在不同的優(yōu)化方向上獨(dú)立發(fā)展。這有助于提高系統(tǒng)的性能和可擴(kuò)展性。
  • 靈活的設(shè)計(jì): 分離讀寫(xiě)邏輯可以簡(jiǎn)化復(fù)雜查詢的設(shè)計(jì),同時(shí)允許使用不同類型的數(shù)據(jù)庫(kù)來(lái)滿足不同的性能需求。

3. 高性能和可擴(kuò)展性

  • 分布式架構(gòu): Axon 支持構(gòu)建分布式的微服務(wù)架構(gòu),適用于大規(guī)模的應(yīng)用場(chǎng)景。它可以處理高并發(fā)請(qǐng)求,并且易于水平擴(kuò)展。
  • 異步處理: Axon 提供了強(qiáng)大的異步命令處理機(jī)制,減少了事務(wù)的鎖定時(shí)間,提高了系統(tǒng)的吞吐量。

4. 豐富的生態(tài)系統(tǒng)

  • 內(nèi)置支持: Axon 框架提供了許多開(kāi)箱即用的功能,如事件存儲(chǔ)、聚合管理、命令總線等,大大減少了開(kāi)發(fā)工作量。
  • 社區(qū)和支持: Axon 擁有一個(gè)活躍的開(kāi)發(fā)者社區(qū)和技術(shù)文檔,便于解決在開(kāi)發(fā)過(guò)程中遇到的問(wèn)題。

5. 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)的支持

  • 模型驅(qū)動(dòng): Axon 強(qiáng)調(diào)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),鼓勵(lì)將復(fù)雜的業(yè)務(wù)邏輯分解為小的、自治的聚合根,從而更好地反映真實(shí)的業(yè)務(wù)場(chǎng)景。
  • 清晰的職責(zé)劃分: 通過(guò)使用 DDD 原則,我們可以確保每個(gè)模塊都有明確的職責(zé),提高了代碼的可維護(hù)性和可理解性。

6. 安全性

  • 細(xì)粒度控制: Axon 提供了細(xì)粒度的安全控制機(jī)制,可以根據(jù)不同的角色和權(quán)限執(zhí)行不同的操作。
  • 加密和認(rèn)證: 結(jié)合 Spring Security 等安全框架,可以進(jìn)一步增強(qiáng)系統(tǒng)的安全性,保護(hù)敏感信息。

應(yīng)用案例

1. ING Bank

ING 銀行是最早采用 Axon Framework 的大型金融機(jī)構(gòu)之一。他們利用 Axon 構(gòu)建了多個(gè)分布式系統(tǒng),包括支付處理、賬戶管理和風(fēng)險(xiǎn)評(píng)估等關(guān)鍵業(yè)務(wù)流程。

  • 項(xiàng)目: ING 使用 Axon 來(lái)構(gòu)建其下一代銀行平臺(tái),實(shí)現(xiàn)了高可用性和可擴(kuò)展性。
  • 優(yōu)勢(shì): 通過(guò)事件溯源提高了數(shù)據(jù)一致性和審計(jì)能力。

2. KLM Royal Dutch Airlines

荷蘭皇家航空(KLM)使用 Axon Framework 來(lái)重構(gòu)其核心預(yù)訂系統(tǒng),以提高系統(tǒng)的靈活性和響應(yīng)速度。

  • 項(xiàng)目: KLM 通過(guò) Axon 實(shí)現(xiàn)了訂單管理系統(tǒng)的現(xiàn)代化,支持復(fù)雜的業(yè)務(wù)規(guī)則和多渠道集成。
  • 優(yōu)勢(shì): 增強(qiáng)了系統(tǒng)的可維護(hù)性和可擴(kuò)展性。

3. Baloise Insurance Group

巴洛伊茲保險(xiǎn)集團(tuán)是一家瑞士保險(xiǎn)公司,使用 Axon Framework 來(lái)改進(jìn)其理賠處理系統(tǒng)。

  • 項(xiàng)目: 巴洛伊茲利用 Axon 構(gòu)建了一個(gè)靈活且可擴(kuò)展的理賠處理平臺(tái)。
  • 優(yōu)勢(shì): 提升了理賠處理的速度和準(zhǔn)確性,并簡(jiǎn)化了系統(tǒng)的維護(hù)工作。

4. Adyen

Adyen 是一家全球領(lǐng)先的支付服務(wù)提供商,使用 Axon Framework 來(lái)處理復(fù)雜的支付交易和結(jié)算流程。

  • 項(xiàng)目: Adyen 利用 Axon 實(shí)現(xiàn)了一個(gè)高性能的支付處理引擎,支持實(shí)時(shí)交易處理。
  • 優(yōu)勢(shì): 確保了交易的可靠性和一致性,提升了系統(tǒng)的性能。

5. Deutsche Bahn

德意志鐵路公司使用 Axon Framework 來(lái)優(yōu)化其票務(wù)系統(tǒng)。

  • 項(xiàng)目: 德意志鐵路利用 Axon 構(gòu)建了一個(gè)現(xiàn)代化的票務(wù)平臺(tái),支持在線購(gòu)票和退票等功能。
  • 優(yōu)勢(shì): 提高了系統(tǒng)的穩(wěn)定性和用戶體驗(yàn)。

6. Zalando SE

Zalando 是一家德國(guó)電商平臺(tái),使用 Axon Framework 來(lái)構(gòu)建其訂單管理系統(tǒng)。

  • 項(xiàng)目: Zalando 利用 Axon 實(shí)現(xiàn)了一個(gè)高度可擴(kuò)展的訂單管理系統(tǒng),支持復(fù)雜的業(yè)務(wù)流程。
  • 優(yōu)勢(shì): 提升了系統(tǒng)的響應(yīng)能力和可維護(hù)性。

代碼實(shí)操

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.5</version>
        <relativePath/><!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>axon-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>axon-demo</name>
    <description>Demo project for Spring Boot and Axon Framework with MySQL</description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.axonframework</groupId>
            <artifactId>axon-spring-boot-starter</artifactId>
            <version>4.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.axonframework</groupId>
            <artifactId>axon-test</artifactId>
            <version>4.6.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

src/main/java/com/example/axondemo/aggregate/BankAccountAggregate.java

package com.example.axondemo.aggregate;

import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.eventsourcing.EventSourcingHandler;
import org.axonframework.modelling.command.AggregateIdentifier;
import org.axonframework.spring.stereotype.Aggregate;
import org.axonframework.modelling.command.AggregateLifecycle;

@Slf4j
@Aggregate
public class BankAccountAggregate {

    @AggregateIdentifier
    private String accountId; // 賬戶ID,作為聚合根的標(biāo)識(shí)符
    private double balance; // 賬戶余額

    public BankAccountAggregate() {} // 默認(rèn)構(gòu)造函數(shù)

    // 處理創(chuàng)建賬戶命令
    @CommandHandler
    public BankAccountAggregate(com.example.axondemo.command.CreateBankAccountCommand command) {
        if (command.getInitialDeposit() < 0) {
            throw new IllegalArgumentException("初始存款必須為正數(shù)");
        }
        log.info("處理創(chuàng)建賬戶命令,賬戶ID: {}", command.getAccountId());
        // 應(yīng)用事件來(lái)更改狀態(tài)
        AggregateLifecycle.apply(new com.example.axondemo.event.BankAccountCreatedEvent(command.getAccountId(), command.getInitialDeposit()));
    }

    // 處理存款命令
    @CommandHandler
    public void handle(com.example.axondemo.command.DepositMoneyCommand command) {
        if (command.getAmount() <= 0) {
            throw new IllegalArgumentException("存款金額必須為正數(shù)");
        }
        log.info("處理存款命令,賬戶ID: {}", command.getAccountId());
        // 應(yīng)用事件來(lái)更改狀態(tài)
        AggregateLifecycle.apply(new com.example.axondemo.event.MoneyDepositedEvent(command.getAccountId(), command.getAmount()));
    }

    // 處理取款命令
    @CommandHandler
    public void handle(com.example.axondemo.command.WithdrawMoneyCommand command) {
        if (command.getAmount() > balance || command.getAmount() <= 0) {
            throw new IllegalArgumentException("無(wú)效的取款金額");
        }
        log.info("處理取款命令,賬戶ID: {}", command.getAccountId());
        // 應(yīng)用事件來(lái)更改狀態(tài)
        AggregateLifecycle.apply(new com.example.axondemo.event.MoneyWithdrewEvent(command.getAccountId(), command.getAmount()));
    }

    // 處理賬戶創(chuàng)建事件
    @EventSourcingHandler
    protected void on(com.example.axondemo.event.BankAccountCreatedEvent event) {
        this.accountId = event.getAccountId();
        this.balance = event.getInitialDeposit();
        log.info("應(yīng)用賬戶創(chuàng)建事件,賬戶ID: {}", event.getAccountId());
    }

    // 處理存款事件
    @EventSourcingHandler
    protected void on(com.example.axondemo.event.MoneyDepositedEvent event) {
        this.balance += event.getAmount();
        log.info("應(yīng)用存款事件,賬戶ID: {}, 金額: {}", event.getAccountId(), event.getAmount());
    }

    // 處理取款事件
    @EventSourcingHandler
    protected void on(com.example.axondemo.event.MoneyWithdrewEvent event) {
        this.balance -= event.getAmount();
        log.info("應(yīng)用取款事件,賬戶ID: {}, 金額: {}", event.getAccountId(), event.getAmount());
    }
}

src/main/java/com/example/axondemo/command/CreateBankAccountCommand.java

package com.example.axondemo.command;

import lombok.Builder;
import lombok.Data;
import org.axonframework.modelling.command.TargetAggregateIdentifier;

@Data
@Builder
public class CreateBankAccountCommand {
    @TargetAggregateIdentifier // 標(biāo)記目標(biāo)聚合根的標(biāo)識(shí)符
    private final String accountId; // 賬戶ID
    private final double initialDeposit; // 初始存款
}

src/main/java/com/example/axondemo/command/DepositMoneyCommand.java

package com.example.axondemo.command;

import lombok.Builder;
import lombok.Data;
import org.axonframework.modelling.command.TargetAggregateIdentifier;

@Data
@Builder
public class DepositMoneyCommand {
    @TargetAggregateIdentifier // 標(biāo)記目標(biāo)聚合根的標(biāo)識(shí)符
    private final String accountId; // 賬戶ID
    private final double amount; // 存款金額
}

src/main/java/com/example/axondemo/command/WithdrawMoneyCommand.java

package com.example.axondemo.command;

import lombok.Builder;
import lombok.Data;
import org.axonframework.modelling.command.TargetAggregateIdentifier;

@Data
@Builder
public class WithdrawMoneyCommand {
    @TargetAggregateIdentifier // 標(biāo)記目標(biāo)聚合根的標(biāo)識(shí)符
    private final String accountId; // 賬戶ID
    private final double amount; // 取款金額
}

src/main/java/com/example/axondemo/controller/AccountController.java

package com.example.axondemo.controller;

import com.example.axondemo.command.*;
import com.example.axondemo.dto.CreateBankAccountRequest;
import com.example.axondemo.dto.DepositRequest;
import com.example.axondemo.dto.WithdrawRequest;
import com.example.axondemo.exception.InsufficientFundsException;
import com.example.axondemo.exception.InvalidAmountException;
import com.example.axondemo.repository.BankAccountRepository;
import lombok.RequiredArgsConstructor;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

@RestController
@RequestMapping("/accounts")
@RequiredArgsConstructor
public class AccountController {

    private final CommandGateway commandGateway; // 命令網(wǎng)關(guān),用于發(fā)送命令
    private final BankAccountRepository bankAccountRepository; // 銀行賬戶倉(cāng)庫(kù)

    // 創(chuàng)建賬戶
    @PostMapping("/")
    public ResponseEntity<String> createAccount(@Valid @RequestBody CreateBankAccountRequest request) {
        String accountId = UUID.randomUUID().toString(); // 生成唯一的賬戶ID
        CompletableFuture<Object> future = commandGateway.send(
                CreateBankAccountCommand.builder()
                        .accountId(accountId)
                        .initialDeposit(request.getInitialDeposit())
                        .build()
        );
        return future.thenApply(response -> ResponseEntity.ok(accountId)) // 成功時(shí)返回賬戶ID
                     .exceptionally(ex -> ResponseEntity.badRequest().body(ex.getMessage())) // 失敗時(shí)返回錯(cuò)誤信息
                     .join();
    }

    // 存款
    @PostMapping("/{accountId}/deposit")
    public ResponseEntity<Void> deposit(@PathVariable String accountId, @Valid @RequestBody DepositRequest request) {
        CompletableFuture<Object> future = commandGateway.send(
                DepositMoneyCommand.builder()
                        .accountId(accountId)
                        .amount(request.getAmount())
                        .build()
        );
        return future.thenApply(response -> ResponseEntity.ok().<Void>build()) // 成功時(shí)返回200 OK
                     .exceptionally(ex -> ResponseEntity.badRequest().body(null)) // 失敗時(shí)返回400 Bad Request
                     .join();
    }

    // 取款
    @PostMapping("/{accountId}/withdraw")
    public ResponseEntity<Void> withdraw(@PathVariable String accountId, @Valid @RequestBody WithdrawRequest request) {
        CompletableFuture<Object> future = commandGateway.send(
                WithdrawMoneyCommand.builder()
                        .accountId(accountId)
                        .amount(request.getAmount())
                        .build()
        );
        return future.thenApply(response -> ResponseEntity.ok().<Void>build()) // 成功時(shí)返回200 OK
                     .exceptionally(ex -> ResponseEntity.badRequest().body(null)) // 失敗時(shí)返回400 Bad Request
                     .join();
    }

    // 查詢賬戶余額
    @GetMapping("/{accountId}/balance")
    public ResponseEntity<Double> getBalance(@PathVariable String accountId) {
        Double balance = bankAccountRepository.findById(accountId).map(it -> it.getBalance()).orElse(0.0); // 獲取賬戶余額
        return ResponseEntity.ok(balance); // 返回賬戶余額
    }
}

src/main/java/com/example/axondemo/dto/CreateBankAccountRequest.java

package com.example.axondemo.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CreateBankAccountRequest {
    @NotNull(message = "初始存款不能為空") // 驗(yàn)證初始存款不為空
    @DecimalMin(value = "0", message = "初始存款必須非負(fù)") // 驗(yàn)證初始存款非負(fù)
    private double initialDeposit; // 初始存款
}

src/main/java/com/example/axondemo/dto/DepositRequest.java

package com.example.axondemo.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class DepositRequest {
    @NotNull(message = "金額不能為空") // 驗(yàn)證金額不為空
    @DecimalMin(value = "0", message = "金額必須非負(fù)") // 驗(yàn)證金額非負(fù)
    private double amount; // 存款金額
}

src/main/java/com/example/axondemo/dto/WithdrawRequest.java

package com.example.axondemo.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class WithdrawRequest {
    @NotNull(message = "金額不能為空") // 驗(yàn)證金額不為空
    @DecimalMin(value = "0", message = "金額必須非負(fù)") // 驗(yàn)證金額非負(fù)
    private double amount; // 取款金額
}

src/main/java/com/example/axondemo/event/BankAccountCreatedEvent.java

賬戶創(chuàng)建: 通過(guò)事件 BankAccountCreatedEvent 記錄賬戶的初始狀態(tài)。

package com.example.axondemo.event;

import lombok.Builder;
import lombok.Data;
import org.axonframework.serialization.Revision;

@Data
@Builder
@Revision("1")
public class BankAccountCreatedEvent {
    private final String accountId; // 賬戶ID
    private final double initialDeposit; // 初始存款
}

src/main/java/com/example/axondemo/event/MoneyDepositedEvent.java

存款和取款: 通過(guò)事件 MoneyDepositedEvent 和 MoneyWithdrewEvent 記錄每一次的資金變動(dòng)。

package com.example.axondemo.event;

import lombok.Builder;
import lombok.Data;
import org.axonframework.serialization.Revision;

@Data
@Builder
@Revision("1")
public class MoneyDepositedEvent {
    private final String accountId; // 賬戶ID
    private final double amount; // 存款金額
}

src/main/java/com/example/axondemo/event/MoneyWithdrewEvent.java

package com.example.axondemo.event;

import lombok.Builder;
import lombok.Data;
import org.axonframework.serialization.Revision;

@Data
@Builder
@Revision("1")
public class MoneyWithdrewEvent {
    private final String accountId; // 賬戶ID
    private final double amount; // 取款金額
}

src/main/java/com/example/axondemo/exception/InsufficientFundsException.java

package com.example.axondemo.exception;

public class InsufficientFundsException extends RuntimeException {
    public InsufficientFundsException(String message) {
        super(message);
    }
}

src/main/java/com/example/axondemo/exception/InvalidAmountException.java

package com.example.axondemo.exception;

public class InvalidAmountException extends RuntimeException {
    public InvalidAmountException(String message) {
        super(message);
    }
}

src/main/java/com/example/axondemo/exception/GlobalExceptionHandler.java

package com.example.axondemo.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class GlobalExceptionHandler {

    // 處理驗(yàn)證異常
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error ->
                errors.put(error.getField(), error.getDefaultMessage()));
        return errors;
    }

    // 處理非法參數(shù)異常
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
        return ResponseEntity.badRequest().body(ex.getMessage());
    }

    // 處理資金不足異常
    @ExceptionHandler(InsufficientFundsException.class)
    public ResponseEntity<String> handleInsufficientFundsException(InsufficientFundsException ex) {
        return ResponseEntity.status(HttpStatus.CONFLICT).body(ex.getMessage());
    }

    // 處理解析金額異常
    @ExceptionHandler(InvalidAmountException.class)
    public ResponseEntity<String> handleInvalidAmountException(InvalidAmountException ex) {
        return ResponseEntity.badRequest().body(ex.getMessage());
    }
}

src/main/java/com/example/axondemo/projection/BankAccountProjection.java

余額查詢: 使用投影類 BankAccountProjection 將事件轉(zhuǎn)換為可供查詢的數(shù)據(jù)視圖。

package com.example.axondemo.projection;

import com.example.axondemo.event.BankAccountCreatedEvent;
import com.example.axondemo.event.MoneyDepositedEvent;
import com.example.axondemo.event.MoneyWithdrewEvent;
import com.example.axondemo.repository.BankAccountEntity;
import com.example.axondemo.repository.BankAccountRepository;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.eventhandling.EventHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class BankAccountProjection {

    @Autowired
    private BankAccountRepository bankAccountRepository; // 銀行賬戶倉(cāng)庫(kù)

    // 處理賬戶創(chuàng)建事件
    @EventHandler
    public void on(BankAccountCreatedEvent event) {
        BankAccountEntity bankAccountEntity = BankAccountEntity.builder()
                .accountId(event.getAccountId())
                .balance(event.getInitialDeposit())
                .build();
        bankAccountRepository.save(bankAccountEntity);
        log.info("投影賬戶創(chuàng)建事件,賬戶ID: {}", event.getAccountId());
    }

    // 處理存款事件
    @EventHandler
    public void on(MoneyDepositedEvent event) {
        bankAccountRepository.findById(event.getAccountId())
                .ifPresentOrElse(
                        bankAccountEntity -> {
                            bankAccountEntity.setBalance(bankAccountEntity.getBalance() + event.getAmount());
                            bankAccountRepository.save(bankAccountEntity);
                            log.info("投影存款事件,賬戶ID: {}, 金額: {}", event.getAccountId(), event.getAmount());
                        },
                        () -> log.error("未找到賬戶ID: {}", event.getAccountId())
                );
    }

    // 處理取款事件
    @EventHandler
    public void on(MoneyWithdrewEvent event) {
        bankAccountRepository.findById(event.getAccountId())
                .ifPresentOrElse(
                        bankAccountEntity -> {
                            bankAccountEntity.setBalance(bankAccountEntity.getBalance() - event.getAmount());
                            bankAccountRepository.save(bankAccountEntity);
                            log.info("投影取款事件,賬戶ID: {}, 金額: {}", event.getAccountId(), event.getAmount());
                        },
                        () -> log.error("未找到賬戶ID: {}", event.getAccountId())
                );
    }
}

src/main/java/com/example/axondemo/repository/BankAccountEntity.java

package com.example.axondemo.repository;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BankAccountEntity {
    @Id
    private String accountId; // 賬戶ID
    private double balance; // 賬戶余額
}

src/main/java/com/example/axondemo/repository/BankAccountRepository.java

package com.example.axondemo.repository;

import org.springframework.data.jpa.repository.JpaRepository;

public interface BankAccountRepository extends JpaRepository<BankAccountEntity, String> {
}

src/main/resources/application.yml

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/banktest?useSSL=false&serverTimezone=UTC
    username: root
    password: 12345678

  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

logging:
  level:
    org.axonframework: INFO

src/main/java/com/example/axondemo/AxonDemoApplication.java

package com.example.axondemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AxonDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(AxonDemoApplication.class, args);
    }
}

測(cè)試

創(chuàng)建賬戶

  • URL: http://localhost:8080/accounts/
  • Method: POST
  • Headers:

a.Content-Type: application/json

  • Body (raw, JSON):
{
    "initialDeposit": 100
  }
  • Response Body:
9f4c1b8e-2a0f-4e5f-b2f2-f8f1e5f1e5f1

存款

  • URL: http://localhost:8080/accounts/9f4c1b8e-2a0f-4e5f-b2f2-f8f1e5f1e5f1/deposit
  • Method: POST
  • Headers:
  • Content-Type: application/json
  • Body (raw, JSON):
{
    "amount": 50
  }
  • Status Code: 200 OK
  • Response Body: (空)

取款

  • URL: http://localhost:8080/accounts/9f4c1b8e-2a0f-4e5f-b2f2-f8f1e5f1e5f1/withdraw
  • Method: POST
  • Headers:

a.Content-Type: application/json

  • Body (raw, JSON):
{
    "amount": 30
  }
  • Status Code: 200 OK
  • Response Body: (空)

查詢賬戶余額

  • URL: http://localhost:8080/accounts/9f4c1b8e-2a0f-4e5f-b2f2-f8f1e5f1e5f1/balance
  • Method: GET
  • Status Code: 200 OK
  • Response Body:
120.0

責(zé)任編輯:武曉燕 來(lái)源: Java知識(shí)日歷
相關(guān)推薦

2023-01-13 07:39:07

2022-06-27 08:21:05

Seata分布式事務(wù)微服務(wù)

2022-01-10 11:58:51

SpringBootPulsar分布式

2022-06-02 10:35:20

架構(gòu)驅(qū)動(dòng)

2020-05-18 14:00:01

Dubbo分布式架構(gòu)

2024-02-01 12:38:22

事件流事件溯源系統(tǒng)

2023-05-12 08:23:03

分布式系統(tǒng)網(wǎng)絡(luò)

2025-01-06 08:53:37

2023-01-04 09:23:58

2023-09-04 08:12:16

分布式鎖Springboot

2023-02-11 00:04:17

分布式系統(tǒng)安全

2024-10-09 17:12:34

2023-05-29 14:07:00

Zuul網(wǎng)關(guān)系統(tǒng)

2025-06-27 02:00:00

Spring高并發(fā)庫(kù)存

2023-03-07 08:19:16

接口冪等性SpringBoot

2024-07-29 09:57:47

2023-12-13 10:44:57

事件驅(qū)動(dòng)事件溯源架構(gòu)

2017-10-27 08:40:44

分布式存儲(chǔ)剪枝系統(tǒng)

2023-10-26 18:10:43

分布式并行技術(shù)系統(tǒng)

2021-03-11 07:27:15

CAPBASE分布式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)