From 4547fb366e298018042a4e79eb29f269d54ac875 Mon Sep 17 00:00:00 2001 From: Sambo Chea Date: Fri, 21 Aug 2020 10:55:40 +0700 Subject: [PATCH] Add data classes and aggregate Add command and events --- build.gradle.kts | 14 ++- .../com/cubetiqs/demo/axon/AxonApplication.kt | 111 +++++++++++++++++- .../demo/axon/command/CreditMoneyCommand.kt | 11 ++ .../demo/axon/command/DebitMoneyCommand.kt | 11 ++ .../demo/axon/event/AccountCreatedEvent.kt | 11 ++ .../demo/axon/event/MoneyCreditedEvent.kt | 9 ++ .../demo/axon/event/MoneyDebitedEvent.kt | 9 ++ .../exception/InsufficientBalanceException.kt | 10 ++ src/main/resources/application-dev.yml | 32 +++++ src/main/resources/application.yml | 5 +- 10 files changed, 213 insertions(+), 10 deletions(-) create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/command/CreditMoneyCommand.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/command/DebitMoneyCommand.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/event/AccountCreatedEvent.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/event/MoneyCreditedEvent.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/event/MoneyDebitedEvent.kt create mode 100644 src/main/kotlin/com/cubetiqs/demo/axon/exception/InsufficientBalanceException.kt create mode 100644 src/main/resources/application-dev.yml diff --git a/build.gradle.kts b/build.gradle.kts index c497c5c..ecee0f8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,8 +3,9 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("org.springframework.boot") version "2.3.3.RELEASE" id("io.spring.dependency-management") version "1.0.10.RELEASE" - kotlin("jvm") version "1.3.72" - kotlin("plugin.spring") version "1.3.72" + kotlin("jvm") version "1.4.0" + kotlin("plugin.spring") version "1.4.0" + kotlin("plugin.jpa") version "1.4.0" } group = "com.cubetiqs.demo" @@ -17,9 +18,18 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.axonframework:axon-spring-boot-starter:4.2.1") + implementation("io.springfox:springfox-swagger2:2.9.2") + implementation("io.springfox:springfox-swagger-ui:2.9.2") + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + + runtimeOnly("org.postgresql:postgresql") + + testImplementation("org.axonframework:axon-test:4.2.1") testImplementation("org.springframework.boot:spring-boot-starter-test") { exclude(group = "org.junit.vintage", module = "junit-vintage-engine") } diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/AxonApplication.kt b/src/main/kotlin/com/cubetiqs/demo/axon/AxonApplication.kt index 941b724..ed76b9a 100644 --- a/src/main/kotlin/com/cubetiqs/demo/axon/AxonApplication.kt +++ b/src/main/kotlin/com/cubetiqs/demo/axon/AxonApplication.kt @@ -1,24 +1,123 @@ 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.runApplication import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping 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 class AxonApplication fun main(args: Array) { - runApplication(*args) + runApplication(*args) } @RestController @RequestMapping class DefaultController { - @GetMapping - fun index(): ResponseEntity { - return ResponseEntity.ok("ok") - } -} \ No newline at end of file + @GetMapping + fun index(): ResponseEntity { + 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 +) \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/command/CreditMoneyCommand.kt b/src/main/kotlin/com/cubetiqs/demo/axon/command/CreditMoneyCommand.kt new file mode 100644 index 0000000..d361de2 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/command/CreditMoneyCommand.kt @@ -0,0 +1,11 @@ +package com.cubetiqs.demo.axon.command + +import org.axonframework.modelling.command.TargetAggregateIdentifier +import java.math.BigDecimal +import java.util.UUID + +data class CreditMoneyCommand( + @TargetAggregateIdentifier + val accountId: UUID? = null, + val creditAmount: BigDecimal? = null +) \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/command/DebitMoneyCommand.kt b/src/main/kotlin/com/cubetiqs/demo/axon/command/DebitMoneyCommand.kt new file mode 100644 index 0000000..796230e --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/command/DebitMoneyCommand.kt @@ -0,0 +1,11 @@ +package com.cubetiqs.demo.axon.command + +import org.axonframework.modelling.command.TargetAggregateIdentifier +import java.math.BigDecimal +import java.util.UUID + +data class DebitMoneyCommand( + @TargetAggregateIdentifier + val accountId: UUID? = null, + val debitAmount: BigDecimal? = null +) \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/event/AccountCreatedEvent.kt b/src/main/kotlin/com/cubetiqs/demo/axon/event/AccountCreatedEvent.kt new file mode 100644 index 0000000..dfd3858 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/event/AccountCreatedEvent.kt @@ -0,0 +1,11 @@ +package com.cubetiqs.demo.axon.event + +import java.math.BigDecimal + +import java.util.UUID + +data class AccountCreatedEvent( + var id: UUID? = null, + var initialBalance: BigDecimal? = null, + var owner: String? = null +) \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/event/MoneyCreditedEvent.kt b/src/main/kotlin/com/cubetiqs/demo/axon/event/MoneyCreditedEvent.kt new file mode 100644 index 0000000..ef2d3d8 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/event/MoneyCreditedEvent.kt @@ -0,0 +1,9 @@ +package com.cubetiqs.demo.axon.event + +import java.math.BigDecimal +import java.util.UUID + +data class MoneyCreditedEvent( + val accountId: UUID? = null, + val creditAmount: BigDecimal? = null +) \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/event/MoneyDebitedEvent.kt b/src/main/kotlin/com/cubetiqs/demo/axon/event/MoneyDebitedEvent.kt new file mode 100644 index 0000000..2f5013b --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/event/MoneyDebitedEvent.kt @@ -0,0 +1,9 @@ +package com.cubetiqs.demo.axon.event + +import java.math.BigDecimal +import java.util.UUID + +data class MoneyDebitedEvent( + val accountId: UUID? = null, + val debitAmount: BigDecimal? = null +) \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/demo/axon/exception/InsufficientBalanceException.kt b/src/main/kotlin/com/cubetiqs/demo/axon/exception/InsufficientBalanceException.kt new file mode 100644 index 0000000..87b2452 --- /dev/null +++ b/src/main/kotlin/com/cubetiqs/demo/axon/exception/InsufficientBalanceException.kt @@ -0,0 +1,10 @@ +package com.cubetiqs.demo.axon.exception + +import java.math.BigDecimal + +import java.util.UUID + +class InsufficientBalanceException(accountId: UUID, debitAmount: BigDecimal) : Throwable( + "Insufficient Balance: Cannot debit " + debitAmount + + " from account number [" + accountId.toString() + "]" +) \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..a43557f --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,32 @@ +server: + port: 8182 + +spring: + datasource: + driverClassName: org.postgresql.Driver + url: jdbc:postgresql://${POSTGRES_HOST:192.168.0.202}:${POSTGRES_PORT:5432}/${POSTGRES_DB:axon_demo} + username: ${POSTGRES_USERNAME:cubetiq} + password: ${POSTGRES_PASSWORD:Root$} + hikari: + max-lifetime: 1800000 + connection-timeout: 30000 + idle-timeout: 600000 + maximum-pool-size: 30 + allow-pool-suspension: true + tomcat: + max_active: 10 + max_idle: 5 + min-idle: 2 + initial_size: 5 + remove_abandoned: true + jpa: + database-platform: org.hibernate.dialect.PostgreSQLDialect + show-sql: true + hibernate: + ddl-auto: update + properties: + hibernate: + dialect: org.hibernate.dialect.PostgreSQLDialect + temp: + use_jdbc_metadata_defaults: false + open-in-view: true \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 31fe62b..caf4dfc 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,2 +1,3 @@ -server: - port: 8182 \ No newline at end of file +spring: + profiles: + active: dev \ No newline at end of file