Compare commits

..

16 Commits

Author SHA1 Message Date
16e954101f Add exec uitils for command line with executable 2020-08-21 21:31:30 +07:00
61369fc2bf Updated swagger ui config 2020-08-21 15:57:51 +07:00
da16ea9afe Updated the revision for back account 2020-08-21 13:43:55 +07:00
fedc1029ce updated the git cached 2020-08-21 13:00:51 +07:00
857d97c142 Add completed points 2020-08-21 12:57:42 +07:00
4b333ff748 Updated the ignore types for swagger 2020-08-21 12:21:39 +07:00
92ba0f5729 Updated response ? 2020-08-21 12:17:28 +07:00
371c4fa5aa Updated command qeury and model id 2020-08-21 11:59:08 +07:00
559633e58a Fixed the bank account aggregate class 2020-08-21 11:37:27 +07:00
652355f486 Completed for api queries 2020-08-21 11:32:16 +07:00
d3e8d63287 completely implementation about cqrs and event sourcing
But not testing yet
And not yet for rest too for query
2020-08-21 11:30:40 +07:00
05431bd77b add projection
add dtos
Add exception for account
add repository
add account service command
2020-08-21 11:18:15 +07:00
8e9fd6935f Add config for swagger 2020-08-21 11:04:39 +07:00
a85390ffb3 Refactoring on classes and modelling 2020-08-21 10:59:20 +07:00
4547fb366e Add data classes and aggregate
Add command and events
2020-08-21 10:55:40 +07:00
fddec51712 Add sample route
And before upgrade to v1.4 of kotlin
2020-08-21 09:35:31 +07:00
33 changed files with 659 additions and 6 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@ build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
application-dev.yml
### STS ###
.apt_generated

View File

@@ -1,8 +1,8 @@
# Spring Boot + Axon Framework
### Concepts to implement
- [ ] Event Sourcing
- [x] Event Sourcing
- [x] CQRS System
- [ ] DDD
- [ ] CQRS System
### Contributors
- [x] Sambo Chea <sombochea@cubetiqs.com>

View File

@@ -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")
}

View File

@@ -1,11 +1,29 @@
package com.cubetiqs.demo.axon
import com.cubetiqs.demo.axon.util.ExecUtils
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.http.MediaType
import org.springframework.util.FileCopyUtils
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import javax.servlet.http.HttpServletResponse
@SpringBootApplication
class AxonApplication
fun main(args: Array<String>) {
runApplication<AxonApplication>(*args)
runApplication<AxonApplication>(*args)
}
@RestController
class MyDumper {
@GetMapping("/dump")
fun dumper(
response: HttpServletResponse
) {
response.contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE
val data = ExecUtils.execMySqlDump()
FileCopyUtils.copy(data ?: ByteArray(0), response.outputStream)
}
}

View File

@@ -0,0 +1,78 @@
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.serialization.Revision
import org.axonframework.spring.stereotype.Aggregate
import java.math.BigDecimal
import java.util.UUID
@Aggregate
@Revision("1.0")
final class BankAccountAggregate() {
@AggregateIdentifier
private var id: UUID? = null
private var balance: BigDecimal? = null
private var owner: String? = null
@CommandHandler
constructor(command: CreateAccountCommand) : this() {
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)
}
}

View File

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

View File

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

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -0,0 +1,42 @@
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
import java.util.concurrent.CompletableFuture
@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()
)
}

View File

@@ -0,0 +1,17 @@
package com.cubetiqs.demo.axon.config
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.EnableWebMvc
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/")
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
}
}

View File

@@ -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
)

View File

@@ -0,0 +1,7 @@
package com.cubetiqs.demo.axon.dto
import java.math.BigDecimal
data class MoneyAmountDTO(
var amount: BigDecimal? = null
)

View File

@@ -0,0 +1,19 @@
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.Id
import javax.persistence.Table
@Entity
@Table(name = "bank_accounts")
data class BankAccount(
@Id
var id: UUID? = null,
var owner: String? = null,
var balance: BigDecimal? = null
) : Serializable

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -0,0 +1,5 @@
package com.cubetiqs.demo.axon.exception
import java.util.UUID
class AccountNotFoundException(id: UUID?) : Throwable("Cannot found account number [$id]")

View File

@@ -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() + "]"
)

View File

@@ -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.id).orElse(null)
}
}

View File

@@ -0,0 +1,7 @@
package com.cubetiqs.demo.axon.query
import java.util.UUID
data class FindBankAccountQuery(
val id: UUID
)

View File

@@ -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>

View File

@@ -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?>
}

View File

@@ -0,0 +1,47 @@
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
)
)
}
}

View File

@@ -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?>
}

View File

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

View File

@@ -0,0 +1,43 @@
package com.cubetiqs.demo.axon.util
import java.io.BufferedInputStream
import java.io.ByteArrayOutputStream
object ExecUtils {
private const val MYSQLDUMP_FILE = "mysqldump"
fun execMySqlDump(): ByteArray? {
var results: ByteArray?
try {
val command: MutableList<String> = mutableListOf()
command.add(MYSQLDUMP_FILE)
command.add("--databases")
command.add("orderwebapp")
command.add("--host")
command.add("192.168.0.204")
command.add("-usombochea")
command.add("-p@Csb632612")
val builder = ProcessBuilder(*command.toTypedArray())
.redirectErrorStream(false)
val process = builder.start()
BufferedInputStream(process.inputStream).use {
ByteArrayOutputStream().use { stdout ->
while (true) {
val x = it.read()
if (x == -1) {
break
}
stdout.write(x)
}
results = stdout.toByteArray()
process.waitFor()
}
}
} catch (e: Exception) {
println(e.message)
return null
}
return results
}
}

View File

@@ -0,0 +1,7 @@
package com.cubetiqs.demo.axon.util.text
import java.util.UUID
fun String?.formatUuid(): UUID {
return UUID.fromString(this)
}

View File

@@ -0,0 +1,43 @@
server:
port: 8182
spring:
application:
name: axon-demo
datasource:
driverClassName: org.postgresql.Driver
url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:axon_demo}
username: ${POSTGRES_USERNAME:root}
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
axon:
serializer:
general: jackson
axonserver:
servers: localhost
logging:
level.root: debug

View File

@@ -1 +0,0 @@

View File

@@ -0,0 +1,3 @@
spring:
profiles:
active: dev

View File

@@ -0,0 +1,10 @@
import com.cubetiqs.demo.axon.util.ExecUtils
import org.junit.jupiter.api.Test
class TestExecUtils {
@Test
fun dump() {
val dump = ExecUtils.execMySqlDump()
println("Dump size: ${dump?.size}")
}
}