Compare commits
5 Commits
4547fb366e
...
652355f486
Author | SHA1 | Date | |
---|---|---|---|
652355f486 | |||
d3e8d63287 | |||
05431bd77b | |||
8e9fd6935f | |||
a85390ffb3 |
@ -1,31 +1,11 @@
|
|||||||
package com.cubetiqs.demo.axon
|
package com.cubetiqs.demo.axon
|
||||||
|
|
||||||
import com.cubetiqs.demo.axon.command.CreditMoneyCommand
|
|
||||||
import com.cubetiqs.demo.axon.command.DebitMoneyCommand
|
|
||||||
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.InsufficientBalanceException
|
|
||||||
import org.axonframework.commandhandling.CommandHandler
|
|
||||||
import org.axonframework.eventsourcing.EventSourcingHandler
|
|
||||||
import org.axonframework.modelling.command.AggregateIdentifier
|
|
||||||
import org.axonframework.modelling.command.AggregateLifecycle
|
|
||||||
import org.axonframework.modelling.command.TargetAggregateIdentifier
|
|
||||||
import org.axonframework.spring.stereotype.Aggregate
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
import org.springframework.boot.runApplication
|
import org.springframework.boot.runApplication
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
import java.io.Serializable
|
|
||||||
import java.math.BigDecimal
|
|
||||||
import java.util.UUID
|
|
||||||
import javax.persistence.Entity
|
|
||||||
import javax.persistence.GeneratedValue
|
|
||||||
import javax.persistence.GenerationType
|
|
||||||
import javax.persistence.Id
|
|
||||||
import javax.persistence.Table
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
class AxonApplication
|
class AxonApplication
|
||||||
@ -42,82 +22,3 @@ class DefaultController {
|
|||||||
return ResponseEntity.ok("ok")
|
return ResponseEntity.ok("ok")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "bank_accounts")
|
|
||||||
data class BankAccount(
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
|
||||||
var id: UUID? = null,
|
|
||||||
|
|
||||||
var owner: String? = null,
|
|
||||||
|
|
||||||
var balance: BigDecimal? = null
|
|
||||||
) : Serializable
|
|
||||||
|
|
||||||
@Aggregate
|
|
||||||
class BankAccountAggregate(
|
|
||||||
@AggregateIdentifier
|
|
||||||
private var id: UUID? = null,
|
|
||||||
private var balance: BigDecimal? = null,
|
|
||||||
private var owner: String? = null
|
|
||||||
) {
|
|
||||||
@CommandHandler
|
|
||||||
constructor(command: CreateAccountCommand) {
|
|
||||||
AggregateLifecycle.apply(
|
|
||||||
AccountCreatedEvent(
|
|
||||||
command.accountId,
|
|
||||||
command.initialBalance,
|
|
||||||
command.owner
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventSourcingHandler
|
|
||||||
fun on(event: AccountCreatedEvent) {
|
|
||||||
id = event.id
|
|
||||||
owner = event.owner
|
|
||||||
balance = event.initialBalance
|
|
||||||
}
|
|
||||||
|
|
||||||
@CommandHandler
|
|
||||||
fun handles(command: CreditMoneyCommand) {
|
|
||||||
AggregateLifecycle.apply(
|
|
||||||
MoneyCreditedEvent(
|
|
||||||
command.accountId,
|
|
||||||
command.creditAmount
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventSourcingHandler
|
|
||||||
fun on(event: MoneyCreditedEvent) {
|
|
||||||
balance = balance!!.add(event.creditAmount)
|
|
||||||
}
|
|
||||||
|
|
||||||
@CommandHandler
|
|
||||||
fun handle(command: DebitMoneyCommand) {
|
|
||||||
AggregateLifecycle.apply(
|
|
||||||
MoneyDebitedEvent(
|
|
||||||
command.accountId,
|
|
||||||
command.debitAmount
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventSourcingHandler
|
|
||||||
@Throws(InsufficientBalanceException::class)
|
|
||||||
fun on(event: MoneyDebitedEvent) {
|
|
||||||
if (balance!! < event.debitAmount) {
|
|
||||||
throw InsufficientBalanceException(event.accountId!!, event.debitAmount!!)
|
|
||||||
}
|
|
||||||
balance = balance!!.subtract(event.debitAmount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class CreateAccountCommand(
|
|
||||||
@TargetAggregateIdentifier
|
|
||||||
val accountId: UUID? = null,
|
|
||||||
val initialBalance: BigDecimal? = null,
|
|
||||||
val owner: String? = null
|
|
||||||
)
|
|
@ -0,0 +1,76 @@
|
|||||||
|
package com.cubetiqs.demo.axon.aggregate
|
||||||
|
|
||||||
|
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.event.AccountCreatedEvent
|
||||||
|
import com.cubetiqs.demo.axon.event.MoneyCreditedEvent
|
||||||
|
import com.cubetiqs.demo.axon.event.MoneyDebitedEvent
|
||||||
|
import com.cubetiqs.demo.axon.exception.InsufficientBalanceException
|
||||||
|
import org.axonframework.commandhandling.CommandHandler
|
||||||
|
import org.axonframework.eventsourcing.EventSourcingHandler
|
||||||
|
import org.axonframework.modelling.command.AggregateIdentifier
|
||||||
|
import org.axonframework.modelling.command.AggregateLifecycle
|
||||||
|
import org.axonframework.spring.stereotype.Aggregate
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@Aggregate
|
||||||
|
class BankAccountAggregate(
|
||||||
|
@AggregateIdentifier
|
||||||
|
private var id: UUID? = null,
|
||||||
|
private var balance: BigDecimal? = null,
|
||||||
|
private var owner: String? = null
|
||||||
|
) {
|
||||||
|
@CommandHandler
|
||||||
|
constructor(command: CreateAccountCommand) {
|
||||||
|
AggregateLifecycle.apply(
|
||||||
|
AccountCreatedEvent(
|
||||||
|
command.accountId,
|
||||||
|
command.initialBalance,
|
||||||
|
command.owner
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventSourcingHandler
|
||||||
|
fun on(event: AccountCreatedEvent) {
|
||||||
|
id = event.id
|
||||||
|
owner = event.owner
|
||||||
|
balance = event.initialBalance
|
||||||
|
}
|
||||||
|
|
||||||
|
@CommandHandler
|
||||||
|
fun handles(command: CreditMoneyCommand) {
|
||||||
|
AggregateLifecycle.apply(
|
||||||
|
MoneyCreditedEvent(
|
||||||
|
command.accountId,
|
||||||
|
command.creditAmount
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventSourcingHandler
|
||||||
|
fun on(event: MoneyCreditedEvent) {
|
||||||
|
balance = balance!!.add(event.creditAmount)
|
||||||
|
}
|
||||||
|
|
||||||
|
@CommandHandler
|
||||||
|
fun handle(command: DebitMoneyCommand) {
|
||||||
|
AggregateLifecycle.apply(
|
||||||
|
MoneyDebitedEvent(
|
||||||
|
command.accountId,
|
||||||
|
command.debitAmount
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventSourcingHandler
|
||||||
|
@Throws(InsufficientBalanceException::class)
|
||||||
|
fun on(event: MoneyDebitedEvent) {
|
||||||
|
if (balance!! < event.debitAmount) {
|
||||||
|
throw InsufficientBalanceException(event.accountId!!, event.debitAmount!!)
|
||||||
|
}
|
||||||
|
balance = balance!!.subtract(event.debitAmount)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.cubetiqs.demo.axon.api
|
||||||
|
|
||||||
|
import com.cubetiqs.demo.axon.dto.AccountCreationDTO
|
||||||
|
import com.cubetiqs.demo.axon.dto.MoneyAmountDTO
|
||||||
|
import com.cubetiqs.demo.axon.entity.BankAccount
|
||||||
|
import com.cubetiqs.demo.axon.service.AccountCommandService
|
||||||
|
import io.swagger.annotations.Api
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(path = ["/accounts"])
|
||||||
|
@Api(value = "Bank Account Commands", description = "Bank Account Commands API")
|
||||||
|
class AccountCommandController @Autowired constructor(
|
||||||
|
private val accountCommandService: AccountCommandService
|
||||||
|
) {
|
||||||
|
@PostMapping
|
||||||
|
@ResponseStatus(value = HttpStatus.CREATED)
|
||||||
|
fun createAccount(@RequestBody creationDTO: AccountCreationDTO): CompletableFuture<BankAccount?> {
|
||||||
|
return this.accountCommandService.createAccount(creationDTO)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping(value = ["/credit/{accountId}"])
|
||||||
|
fun creditMoneyToAccount(
|
||||||
|
@PathVariable(value = "accountId") accountId: String,
|
||||||
|
@RequestBody moneyCreditDTO: MoneyAmountDTO
|
||||||
|
): CompletableFuture<String?> {
|
||||||
|
return this.accountCommandService.creditMoneyToAccount(accountId, moneyCreditDTO)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping(value = ["/debit/{accountId}"])
|
||||||
|
fun debitMoneyFromAccount(
|
||||||
|
@PathVariable(value = "accountId") accountId: String,
|
||||||
|
@RequestBody moneyDebitDTO: MoneyAmountDTO
|
||||||
|
): CompletableFuture<String?>? {
|
||||||
|
return this.accountCommandService.debitMoneyFromAccount(accountId, moneyDebitDTO)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.cubetiqs.demo.axon.api
|
||||||
|
|
||||||
|
import com.cubetiqs.demo.axon.entity.BankAccount
|
||||||
|
import com.cubetiqs.demo.axon.service.AccountQueryService
|
||||||
|
import io.swagger.annotations.Api
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(path = ["/accounts"])
|
||||||
|
@Api(value = "Bank Account Queries", description = "Bank Account Query Events API")
|
||||||
|
class AccountQueryController @Autowired constructor(
|
||||||
|
private val accountQueryService: AccountQueryService
|
||||||
|
) {
|
||||||
|
@GetMapping("/{accountId}")
|
||||||
|
fun findById(@PathVariable("accountId") accountId: String?): CompletableFuture<BankAccount?> {
|
||||||
|
return this.accountQueryService.findById(accountId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{accountId}/events")
|
||||||
|
fun listEventsForAccount(@PathVariable(value = "accountId") accountId: String?): List<Any?> {
|
||||||
|
return this.accountQueryService.listEventsForAccount(accountId)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.cubetiqs.demo.axon.command
|
||||||
|
|
||||||
|
import org.axonframework.modelling.command.TargetAggregateIdentifier
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
data class CreateAccountCommand(
|
||||||
|
@TargetAggregateIdentifier
|
||||||
|
val accountId: UUID? = null,
|
||||||
|
val initialBalance: BigDecimal? = null,
|
||||||
|
val owner: String? = null
|
||||||
|
)
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.cubetiqs.demo.axon.config
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import springfox.documentation.builders.PathSelectors
|
||||||
|
import springfox.documentation.builders.RequestHandlerSelectors
|
||||||
|
import springfox.documentation.service.ApiInfo
|
||||||
|
import springfox.documentation.service.Contact
|
||||||
|
import springfox.documentation.spi.DocumentationType
|
||||||
|
import springfox.documentation.spring.web.plugins.Docket
|
||||||
|
import springfox.documentation.swagger2.annotations.EnableSwagger2
|
||||||
|
import java.util.Collections
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableSwagger2
|
||||||
|
class SwaggerConfiguration {
|
||||||
|
@Bean
|
||||||
|
fun apiDocket(): Docket {
|
||||||
|
return Docket(DocumentationType.SWAGGER_2)
|
||||||
|
.select()
|
||||||
|
.apis(
|
||||||
|
RequestHandlerSelectors
|
||||||
|
.basePackage("com.cubetiqs.demo.axon")
|
||||||
|
)
|
||||||
|
.paths(PathSelectors.any())
|
||||||
|
.build()
|
||||||
|
.apiInfo(apiInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val apiInfo: ApiInfo
|
||||||
|
get() = ApiInfo(
|
||||||
|
"Spring Boot + Axon Demo",
|
||||||
|
"Axon Project Demo (Event Sourcing, DDD and CQRS)",
|
||||||
|
"0.0.1-SNAPSHOT",
|
||||||
|
"Terms of Service",
|
||||||
|
Contact("Sambo Chea", "https://cs.cubetiqs.com", "sombochea@cubetiqs.com"),
|
||||||
|
"MIT",
|
||||||
|
"",
|
||||||
|
Collections.emptyList()
|
||||||
|
)
|
||||||
|
}
|
@ -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
|
||||||
|
)
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.cubetiqs.demo.axon.dto
|
||||||
|
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
data class MoneyAmountDTO(
|
||||||
|
var amount: BigDecimal? = null
|
||||||
|
)
|
22
src/main/kotlin/com/cubetiqs/demo/axon/entity/BankAccount.kt
Normal file
22
src/main/kotlin/com/cubetiqs/demo/axon/entity/BankAccount.kt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package com.cubetiqs.demo.axon.entity
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.persistence.Entity
|
||||||
|
import javax.persistence.GeneratedValue
|
||||||
|
import javax.persistence.GenerationType
|
||||||
|
import javax.persistence.Id
|
||||||
|
import javax.persistence.Table
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "bank_accounts")
|
||||||
|
data class BankAccount(
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
var id: UUID? = null,
|
||||||
|
|
||||||
|
var owner: String? = null,
|
||||||
|
|
||||||
|
var balance: BigDecimal? = null
|
||||||
|
) : Serializable
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.cubetiqs.demo.axon.exception
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
class AccountNotFoundException(id: UUID?) : Throwable("Cannot found account number [$id]")
|
@ -0,0 +1,67 @@
|
|||||||
|
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.query.FindBankAccountQuery
|
||||||
|
import com.cubetiqs.demo.axon.repository.BankAccountRepository
|
||||||
|
import org.axonframework.eventhandling.EventHandler
|
||||||
|
import org.axonframework.queryhandling.QueryHandler
|
||||||
|
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<BankAccount> = 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<BankAccount> = 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@QueryHandler
|
||||||
|
fun handle(query: FindBankAccountQuery): BankAccount? {
|
||||||
|
log.debug("Handling FindBankAccountQuery query: {}", query)
|
||||||
|
return this.bankAccountRepository.findById(query.accountId).orElse(null)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.cubetiqs.demo.axon.query
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
data class FindBankAccountQuery(
|
||||||
|
val accountId: UUID
|
||||||
|
)
|
@ -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<BankAccount, UUID>
|
@ -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<BankAccount?>
|
||||||
|
|
||||||
|
fun creditMoneyToAccount(
|
||||||
|
accountId: String?,
|
||||||
|
moneyCreditDTO: MoneyAmountDTO
|
||||||
|
): CompletableFuture<String?>
|
||||||
|
|
||||||
|
fun debitMoneyFromAccount(
|
||||||
|
accountId: String?,
|
||||||
|
moneyDebitDTO: MoneyAmountDTO
|
||||||
|
): CompletableFuture<String?>
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
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 com.cubetiqs.demo.axon.util.text.formatUuid
|
||||||
|
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<BankAccount?> {
|
||||||
|
return commandGateway.send(
|
||||||
|
CreateAccountCommand(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
creationDTO.initialBalance,
|
||||||
|
creationDTO.owner,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun creditMoneyToAccount(accountId: String?, moneyCreditDTO: MoneyAmountDTO): CompletableFuture<String?> {
|
||||||
|
return commandGateway.send(
|
||||||
|
CreditMoneyCommand(
|
||||||
|
accountId.formatUuid(),
|
||||||
|
moneyCreditDTO.amount
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun debitMoneyFromAccount(accountId: String?, moneyDebitDTO: MoneyAmountDTO): CompletableFuture<String?> {
|
||||||
|
return commandGateway.send(
|
||||||
|
DebitMoneyCommand(
|
||||||
|
accountId.formatUuid(),
|
||||||
|
moneyDebitDTO.amount
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.cubetiqs.demo.axon.service
|
||||||
|
|
||||||
|
import com.cubetiqs.demo.axon.entity.BankAccount
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
@Service
|
||||||
|
interface AccountQueryService {
|
||||||
|
fun findById(accountId: String?): CompletableFuture<BankAccount?>
|
||||||
|
fun listEventsForAccount(accountId: String?): List<Any?>
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.cubetiqs.demo.axon.service
|
||||||
|
|
||||||
|
import com.cubetiqs.demo.axon.entity.BankAccount
|
||||||
|
import com.cubetiqs.demo.axon.query.FindBankAccountQuery
|
||||||
|
import com.cubetiqs.demo.axon.util.text.formatUuid
|
||||||
|
import org.axonframework.eventsourcing.eventstore.EventStore
|
||||||
|
import org.axonframework.messaging.responsetypes.ResponseTypes
|
||||||
|
import org.axonframework.queryhandling.QueryGateway
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class AccountQueryServiceImpl @Autowired constructor(
|
||||||
|
private val queryGateway: QueryGateway,
|
||||||
|
private val eventStore: EventStore
|
||||||
|
) : AccountQueryService {
|
||||||
|
override fun findById(accountId: String?): CompletableFuture<BankAccount?> {
|
||||||
|
return this.queryGateway.query(
|
||||||
|
FindBankAccountQuery(accountId.formatUuid()),
|
||||||
|
ResponseTypes.instanceOf(BankAccount::class.java)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun listEventsForAccount(accountId: String?): List<Any?> {
|
||||||
|
return this.eventStore
|
||||||
|
.readEvents(accountId.formatUuid().toString())
|
||||||
|
.asStream()
|
||||||
|
.map { it.payload }
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.cubetiqs.demo.axon.util.text
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
fun String?.formatUuid(): UUID {
|
||||||
|
return UUID.fromString(this)
|
||||||
|
}
|
@ -30,3 +30,7 @@ spring:
|
|||||||
temp:
|
temp:
|
||||||
use_jdbc_metadata_defaults: false
|
use_jdbc_metadata_defaults: false
|
||||||
open-in-view: true
|
open-in-view: true
|
||||||
|
|
||||||
|
axon:
|
||||||
|
serializer:
|
||||||
|
general: jackson
|
Loading…
Reference in New Issue
Block a user