From 05431bd77b298edce594816f14e67306379d6cbe Mon Sep 17 00:00:00 2001 From: Sambo Chea Date: Fri, 21 Aug 2020 11:18:15 +0700 Subject: [PATCH] add projection add dtos Add exception for account add repository add account service command --- .../com/cubetiqs/demo/axon/AxonApplication.kt | 9 +-- .../demo/axon/dto/AccountCreationDTO.kt | 8 +++ .../cubetiqs/demo/axon/dto/MoneyAmountDTO.kt | 7 +++ .../exception/AccountNotFoundException.kt | 5 ++ .../axon/projection/BankAccountProjection.kt | 59 +++++++++++++++++++ .../demo/axon/query/FindBankAccountQuery.kt | 7 +++ .../axon/repository/BankAccountRepository.kt | 9 +++ .../axon/service/AccountCommandService.kt | 22 +++++++ .../axon/service/AccountCommandServiceImpl.kt | 50 ++++++++++++++++ 9 files changed, 168 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/dto/AccountCreationDTO.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/dto/MoneyAmountDTO.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/exception/AccountNotFoundException.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/projection/BankAccountProjection.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/query/FindBankAccountQuery.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/repository/BankAccountRepository.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/service/AccountCommandService.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/service/AccountCommandServiceImpl.kt diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/AxonApplication.kt b/src/main/kotlin/com/cubetiqs/demo/axon/AxonApplication.kt index 7f99635..e87f52b 100644 --- a/src/main/kotlin/com/cubetiqs/demo/axon/AxonApplication.kt +++ b/src/main/kotlin/com/cubetiqs/demo/axon/AxonApplication.kt @@ -1,15 +1,11 @@ package com.cubetiqs.demo.axon -import com.cubetiqs.demo.axon.entity.BankAccount import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication -import org.springframework.data.jpa.repository.JpaRepository import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Repository import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -import java.util.UUID @SpringBootApplication class AxonApplication @@ -25,7 +21,4 @@ class DefaultController { fun index(): ResponseEntity { return ResponseEntity.ok("ok") } -} - -@Repository -interface BankAccountRepository : JpaRepository \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/dto/AccountCreationDTO.kt b/src/main/kotlin/com/cubetiqs/demo/axon/dto/AccountCreationDTO.kt new file mode 100644 index 0000000..292f8f9 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/dto/AccountCreationDTO.kt @@ -0,0 +1,8 @@ +package com.cubetiqs.demo.axon.dto + +import java.math.BigDecimal + +data class AccountCreationDTO( + var initialBalance: BigDecimal? = null, + var owner: String? = null +) \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/dto/MoneyAmountDTO.kt b/src/main/kotlin/com/cubetiqs/demo/axon/dto/MoneyAmountDTO.kt new file mode 100644 index 0000000..73658ee --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/dto/MoneyAmountDTO.kt @@ -0,0 +1,7 @@ +package com.cubetiqs.demo.axon.dto + +import java.math.BigDecimal + +data class MoneyAmountDTO( + var amount: BigDecimal? = null +) \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/exception/AccountNotFoundException.kt b/src/main/kotlin/com/cubetiqs/demo/axon/exception/AccountNotFoundException.kt new file mode 100644 index 0000000..dfe0974 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/exception/AccountNotFoundException.kt @@ -0,0 +1,5 @@ +package com.cubetiqs.demo.axon.exception + +import java.util.UUID + +class AccountNotFoundException(id: UUID?) : Throwable("Cannot found account number [$id]") \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/projection/BankAccountProjection.kt b/src/main/kotlin/com/cubetiqs/demo/axon/projection/BankAccountProjection.kt new file mode 100644 index 0000000..bf953d7 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/projection/BankAccountProjection.kt @@ -0,0 +1,59 @@ +package com.cubetiqs.demo.axon.projection + +import com.cubetiqs.demo.axon.entity.BankAccount +import com.cubetiqs.demo.axon.event.AccountCreatedEvent +import com.cubetiqs.demo.axon.event.MoneyCreditedEvent +import com.cubetiqs.demo.axon.event.MoneyDebitedEvent +import com.cubetiqs.demo.axon.exception.AccountNotFoundException +import com.cubetiqs.demo.axon.repository.BankAccountRepository +import org.axonframework.eventhandling.EventHandler +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Component +import java.util.Optional + +@Component +class BankAccountProjection @Autowired constructor( + private val bankAccountRepository: BankAccountRepository +) { + private val log = LoggerFactory.getLogger(this.javaClass) + + @EventHandler + fun on(event: AccountCreatedEvent) { + log.debug("Handling a Bank Account creation command {}", event.id) + val bankAccount = BankAccount( + event.id, + event.owner, + event.initialBalance + ) + this.bankAccountRepository.save(bankAccount) + } + + @EventHandler + @Throws(AccountNotFoundException::class) + fun on(event: MoneyCreditedEvent) { + log.debug("Handling an Account Credit command {}", event.accountId) + val optionalBankAccount: Optional = this.bankAccountRepository.findById(event.accountId!!) + if (optionalBankAccount.isPresent) { + val bankAccount: BankAccount = optionalBankAccount.get() + bankAccount.balance = bankAccount.balance!!.add(event.creditAmount) + this.bankAccountRepository.save(bankAccount) + } else { + throw AccountNotFoundException(event.accountId) + } + } + + @EventHandler + @Throws(AccountNotFoundException::class) + fun on(event: MoneyDebitedEvent) { + log.debug("Handling an Account Debit command {}", event.accountId) + val optionalBankAccount: Optional = this.bankAccountRepository.findById(event.accountId!!) + if (optionalBankAccount.isPresent) { + val bankAccount: BankAccount = optionalBankAccount.get() + bankAccount.balance = bankAccount.balance!!.subtract(event.debitAmount) + this.bankAccountRepository.save(bankAccount) + } else { + throw AccountNotFoundException(event.accountId) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/query/FindBankAccountQuery.kt b/src/main/kotlin/com/cubetiqs/demo/axon/query/FindBankAccountQuery.kt new file mode 100644 index 0000000..0a6d704 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/query/FindBankAccountQuery.kt @@ -0,0 +1,7 @@ +package com.cubetiqs.demo.axon.query + +import java.util.UUID + +data class FindBankAccountQuery( + val id: UUID +) \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/repository/BankAccountRepository.kt b/src/main/kotlin/com/cubetiqs/demo/axon/repository/BankAccountRepository.kt new file mode 100644 index 0000000..09613ae --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/repository/BankAccountRepository.kt @@ -0,0 +1,9 @@ +package com.cubetiqs.demo.axon.repository + +import com.cubetiqs.demo.axon.entity.BankAccount +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository +import java.util.UUID + +@Repository +interface BankAccountRepository : JpaRepository \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/service/AccountCommandService.kt b/src/main/kotlin/com/cubetiqs/demo/axon/service/AccountCommandService.kt new file mode 100644 index 0000000..e2358d3 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/service/AccountCommandService.kt @@ -0,0 +1,22 @@ +package com.cubetiqs.demo.axon.service + +import com.cubetiqs.demo.axon.dto.AccountCreationDTO +import com.cubetiqs.demo.axon.dto.MoneyAmountDTO +import com.cubetiqs.demo.axon.entity.BankAccount +import org.springframework.stereotype.Service +import java.util.concurrent.CompletableFuture + +@Service +interface AccountCommandService { + fun createAccount(creationDTO: AccountCreationDTO): CompletableFuture + + fun creditMoneyToAccount( + accountId: String?, + moneyCreditDTO: MoneyAmountDTO + ): CompletableFuture + + fun debitMoneyFromAccount( + accountId: String?, + moneyDebitDTO: MoneyAmountDTO + ): CompletableFuture +} \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/service/AccountCommandServiceImpl.kt b/src/main/kotlin/com/cubetiqs/demo/axon/service/AccountCommandServiceImpl.kt new file mode 100644 index 0000000..86c0162 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/service/AccountCommandServiceImpl.kt @@ -0,0 +1,50 @@ +package com.cubetiqs.demo.axon.service + +import com.cubetiqs.demo.axon.command.CreateAccountCommand +import com.cubetiqs.demo.axon.command.CreditMoneyCommand +import com.cubetiqs.demo.axon.command.DebitMoneyCommand +import com.cubetiqs.demo.axon.dto.AccountCreationDTO +import com.cubetiqs.demo.axon.dto.MoneyAmountDTO +import com.cubetiqs.demo.axon.entity.BankAccount +import org.axonframework.commandhandling.gateway.CommandGateway +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Service +import java.util.UUID +import java.util.concurrent.CompletableFuture + +@Service +class AccountCommandServiceImpl @Autowired constructor( + private val commandGateway: CommandGateway +) : AccountCommandService { + override fun createAccount(creationDTO: AccountCreationDTO): CompletableFuture { + return commandGateway.send( + CreateAccountCommand( + UUID.randomUUID(), + creationDTO.initialBalance, + creationDTO.owner, + ) + ) + } + + override fun creditMoneyToAccount(accountId: String?, moneyCreditDTO: MoneyAmountDTO): CompletableFuture { + return commandGateway.send( + CreditMoneyCommand( + formatUuid(accountId), + moneyCreditDTO.amount + ) + ) + } + + override fun debitMoneyFromAccount(accountId: String?, moneyDebitDTO: MoneyAmountDTO): CompletableFuture { + return commandGateway.send( + DebitMoneyCommand( + formatUuid(accountId), + moneyDebitDTO.amount + ) + ) + } + + private fun formatUuid(accountId: String?): UUID { + return UUID.fromString(accountId) + } +} \ No newline at end of file