Improved module name <functional-area>-<Command|Query>....
Standalone services now use the Event Store Server (many tests still use the embedded server)
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
This is the Scala/Spring version of the Event Sourcing/CQRS money transfer example application.
|
||||
|
||||
# About the application
|
||||
|
||||
This application consists of three microservices:
|
||||
|
||||
* Account Service - the command side business logic for Accounts
|
||||
@@ -8,25 +10,28 @@ This application consists of three microservices:
|
||||
|
||||
The Account Service consists of the following modules:
|
||||
|
||||
* commandside-backend-accounts - the Account aggregate
|
||||
* commandside-web-accounts - a REST API for creating and retrieving Accounts
|
||||
|
||||
* accounts-command-side-backend - the Account aggregate
|
||||
* accounts-command-side-web - a REST API for creating and retrieving Accounts
|
||||
* accounts-command-side-service - a standalone microservice
|
||||
|
||||
The Money Transfer Service consists of the following modules:
|
||||
|
||||
* commandside-backend-transactions - the MoneyTransfer aggregate
|
||||
* commandside-web-transactions - a REST API for creating and retrieving Money Transfers
|
||||
* transactions-command-side-backend - the MoneyTransfer aggregate
|
||||
* transactions-command-side-web - a REST API for creating and retrieving Money Transfers
|
||||
* transactions-command-side-service - a standalone microservice
|
||||
|
||||
The Query Service consists the following modules:
|
||||
|
||||
* queryside-backend - MongoDB-based, denormalized view of Accounts and MoneyTransfers
|
||||
* queryside-web - a REST API for querying the denormalized view
|
||||
* accounts-query-side-backend - MongoDB-based, denormalized view of Accounts and MoneyTransfers
|
||||
* accounts-query-side-web - a REST API for querying the denormalized view
|
||||
* accounts-query-side-service - a standalone microservice
|
||||
|
||||
In order to be used with the embedded Event Store, the three services are currently packaged as a single monolithic web application:
|
||||
# Deploying the application
|
||||
|
||||
* monolithic-web - all-in-one, monolithic packaging of the application
|
||||
These services can be deployed either as either separate standalone services using the Event Store server, or they can be deployed as a monolithic application for simpified integration testing.
|
||||
|
||||
As well as the above modules there are also:
|
||||
The three services can also be packaged as a single monolithic web application in order to be used with the embedded Event Store:
|
||||
|
||||
* common-backend - code that is shared between the command side and the query side, primarily events and value objects
|
||||
* backend-integration-tests - integrations tests for the backend
|
||||
|
||||
* monolithic-service - all-in-one, monolithic packaging of the application
|
||||
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@ dependencies {
|
||||
compile "org.scala-lang:scala-library:2.10.2"
|
||||
compile project(":common-backend")
|
||||
compile "net.chrisrichardson.eventstore.client:eventstore-client-event-handling:$eventStoreClientVersion"
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,8 @@ class TransferWorkflowAccountHandlers(eventStore: EventStore) extends CompoundEv
|
||||
|
||||
@EventHandlerMethod
|
||||
val performCredit = handlerForEvent[DebitRecordedEvent] { de =>
|
||||
existingEntity[Account](de.event.details.toAccountId) <== CreditAccountCommand(de.event.details.amount, de.entityId)
|
||||
existingEntity[Account](de.event.details.toAccountId) <==
|
||||
CreditAccountCommand(de.event.details.amount, de.entityId)
|
||||
}
|
||||
|
||||
}
|
||||
22
scala-spring/accounts-command-side-service/build.gradle
Normal file
22
scala-spring/accounts-command-side-service/build.gradle
Normal file
@@ -0,0 +1,22 @@
|
||||
apply plugin: 'scala'
|
||||
apply plugin: 'spring-boot'
|
||||
apply plugin: VerifyEventStoreEnvironmentPlugin
|
||||
|
||||
dependencies {
|
||||
compile "org.scala-lang:scala-library:2.10.2"
|
||||
compile project(":accounts-command-side-web")
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-actuator"
|
||||
|
||||
compile "net.chrisrichardson.eventstore.client:eventstore-http-stomp-client:$eventStoreClientVersion"
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||
testCompile scalaTestDependency
|
||||
|
||||
}
|
||||
|
||||
test {
|
||||
ignoreFailures true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web
|
||||
|
||||
import net.chrisrichardson.eventstore.client.config.EventStoreHttpClientConfiguration
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.accounts.CommandSideWebAccountsConfiguration
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.context.annotation._
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@Import(Array(classOf[CommandSideWebAccountsConfiguration], classOf[EventStoreHttpClientConfiguration]))
|
||||
@ComponentScan
|
||||
class AccountsCommandSideServiceConfiguration {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web.main
|
||||
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.AccountsCommandSideServiceConfiguration
|
||||
import org.springframework.boot.SpringApplication
|
||||
|
||||
object AccountsCommandSideServiceMain {
|
||||
|
||||
def main(args: Array[String]) : Unit = SpringApplication.run(classOf[AccountsCommandSideServiceConfiguration], args :_ *)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web
|
||||
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.accounts.controllers.{CreateAccountRequest, CreateAccountResponse}
|
||||
import org.junit.Assert
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.springframework.boot.SpringApplication
|
||||
import org.springframework.web.client.RestTemplate
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class AccountsCommandSideServiceIntegrationTest extends FlatSpec {
|
||||
|
||||
val sa = new SpringApplication(classOf[AccountsCommandSideServiceTestConfiguration])
|
||||
val ctx = sa.run()
|
||||
|
||||
// var server = ctx.getBean(classOf[EmbeddedServletContainer])
|
||||
|
||||
val port = 8080
|
||||
|
||||
val baseUrl = s"http://localhost:$port/"
|
||||
|
||||
val restTemplate = ctx.getBean(classOf[RestTemplate])
|
||||
|
||||
it should "create account" in {
|
||||
|
||||
val CreateAccountResponse(accountId) = restTemplate.postForEntity(s"$baseUrl/accounts", CreateAccountRequest(BigDecimal(500)), classOf[CreateAccountResponse]).getBody
|
||||
Assert.assertNotNull(accountId)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.context.annotation.{Bean, Import, Configuration}
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
|
||||
import org.springframework.web.client.RestTemplate
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
@Configuration
|
||||
@Import(Array(classOf[AccountsCommandSideServiceConfiguration]))
|
||||
class AccountsCommandSideServiceTestConfiguration {
|
||||
|
||||
@Bean
|
||||
def restTemplate(scalaObjectMapper: ObjectMapper) = {
|
||||
val restTemplate = new RestTemplate()
|
||||
restTemplate.getMessageConverters foreach {
|
||||
case mc: MappingJackson2HttpMessageConverter =>
|
||||
mc.setObjectMapper(scalaObjectMapper)
|
||||
case _ =>
|
||||
}
|
||||
restTemplate
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,9 +2,9 @@ apply plugin: 'scala'
|
||||
|
||||
dependencies {
|
||||
compile "org.scala-lang:scala-library:2.10.2"
|
||||
compile project(":commandside-backend-accounts")
|
||||
compile project(":web-common")
|
||||
compile project(":accounts-command-side-backend")
|
||||
compile project(":common-web")
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
testCompile scalaTestDependency
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.queryside
|
||||
|
||||
import net.chrisrichardson.eventstore.EntityId
|
||||
|
||||
class AccountInfoQueryService(accountInfoRepository : AccountInfoRepository) {
|
||||
|
||||
def findByAccountId(accountId : EntityId) : AccountInfo = {
|
||||
val account = accountInfoRepository.findOne(accountId.id)
|
||||
if (account == null)
|
||||
throw new AccountNotFoundException(accountId)
|
||||
else
|
||||
account
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AccountNotFoundException(accountId : EntityId) extends RuntimeException("Account not found " + accountId)
|
||||
@@ -29,7 +29,10 @@ class AccountInfoUpdateService(accountInfoRepository : AccountInfoRepository, mo
|
||||
logger.info("About to save")
|
||||
try {
|
||||
|
||||
accountInfoRepository.save(AccountInfo(de.entityId.id, toIntegerRepr(de.event.initialBalance), Seq(), Seq(), de.eventId.asString))
|
||||
if (de.event.initialBalance != null)
|
||||
accountInfoRepository.save(AccountInfo(de.entityId.id, toIntegerRepr(de.event.initialBalance), Seq(), Seq(), de.eventId.asString))
|
||||
else
|
||||
logger.error("Event with initialBalance == null {}", de.entityId)
|
||||
}
|
||||
catch {
|
||||
case t : Throwable =>
|
||||
23
scala-spring/accounts-query-side-service/build.gradle
Normal file
23
scala-spring/accounts-query-side-service/build.gradle
Normal file
@@ -0,0 +1,23 @@
|
||||
apply plugin: 'scala'
|
||||
apply plugin: 'spring-boot'
|
||||
apply plugin: VerifyMongoDBConfigurationPlugin
|
||||
apply plugin: VerifyEventStoreEnvironmentPlugin
|
||||
|
||||
dependencies {
|
||||
compile "org.scala-lang:scala-library:2.10.2"
|
||||
compile project(":accounts-query-side-web")
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-actuator"
|
||||
|
||||
compile "net.chrisrichardson.eventstore.client:eventstore-http-stomp-client:$eventStoreClientVersion"
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||
testCompile scalaTestDependency
|
||||
|
||||
}
|
||||
|
||||
test {
|
||||
ignoreFailures true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web
|
||||
|
||||
import net.chrisrichardson.eventstore.client.config.EventStoreHttpClientConfiguration
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.queryside.QuerySideWebConfiguration
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.context.annotation._
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@Import(Array(classOf[QuerySideWebConfiguration], classOf[EventStoreHttpClientConfiguration]))
|
||||
@ComponentScan
|
||||
class AccountsQuerySideServiceConfiguration {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web.main
|
||||
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.AccountsQuerySideServiceConfiguration
|
||||
import org.springframework.boot.SpringApplication
|
||||
|
||||
object AccountsQuerySideServiceMain {
|
||||
|
||||
def main(args: Array[String]) : Unit = SpringApplication.run(classOf[AccountsQuerySideServiceConfiguration], args :_ *)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.concurrent.Eventually._
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.scalatest.time.{Millis, Span}
|
||||
import org.springframework.boot.SpringApplication
|
||||
import org.springframework.web.client.RestTemplate
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class AccountsQuerySideServiceIntegrationTest extends FlatSpec {
|
||||
|
||||
val sa = new SpringApplication(classOf[AccountsQuerySideServiceTestConfiguration])
|
||||
val ctx = sa.run()
|
||||
|
||||
// var server = ctx.getBean(classOf[EmbeddedServletContainer])
|
||||
|
||||
val port = 8080
|
||||
|
||||
val baseUrl = s"http://localhost:$port/"
|
||||
|
||||
val restTemplate = ctx.getBean(classOf[RestTemplate])
|
||||
|
||||
implicit val reallyLongPatienceConfig = PatienceConfig(timeout = Span(10 * 1000, Millis), interval = Span(1 * 1000, Millis))
|
||||
|
||||
it should "create accounts and transfer money" in {
|
||||
// FIXME
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import org.springframework.context.annotation.{Bean, Configuration, Import}
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
|
||||
import org.springframework.web.client.RestTemplate
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
@Configuration
|
||||
@Import(Array(classOf[AccountsQuerySideServiceConfiguration]))
|
||||
class AccountsQuerySideServiceTestConfiguration {
|
||||
|
||||
@Bean
|
||||
def restTemplate(scalaObjectMapper: ObjectMapper) = {
|
||||
val restTemplate = new RestTemplate()
|
||||
restTemplate.getMessageConverters foreach {
|
||||
case mc: MappingJackson2HttpMessageConverter =>
|
||||
mc.setObjectMapper(scalaObjectMapper)
|
||||
case _ =>
|
||||
}
|
||||
restTemplate
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,8 +2,8 @@ apply plugin: 'scala'
|
||||
|
||||
dependencies {
|
||||
compile "org.scala-lang:scala-library:2.10.2"
|
||||
compile project(":queryside-backend")
|
||||
compile project(":web-common")
|
||||
compile project(":accounts-query-side-backend")
|
||||
compile project(":common-web")
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web.queryside
|
||||
|
||||
import net.chrisrichardson.eventstore.examples.bank.queryside.QuerySideConfiguration
|
||||
import net.chrisrichardson.eventstore.subscriptions.EnableEventHandlers
|
||||
import org.springframework.context.annotation._
|
||||
|
||||
@Configuration
|
||||
@Import(Array(classOf[QuerySideConfiguration]))
|
||||
@ComponentScan
|
||||
@EnableEventHandlers
|
||||
class QuerySideWebConfiguration {
|
||||
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web.queryside.controllers
|
||||
|
||||
import net.chrisrichardson.eventstore.EntityId
|
||||
import net.chrisrichardson.eventstore.examples.bank.queryside.AccountInfoQueryService
|
||||
import net.chrisrichardson.eventstore.examples.bank.queryside.{AccountNotFoundException, AccountInfoQueryService}
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.web.bind.annotation.{PathVariable, RequestMapping, RequestMethod, RestController}
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.web.bind.annotation._
|
||||
|
||||
@RestController
|
||||
class AccountQuerySideController @Autowired() (accountInfoQueryService : AccountInfoQueryService) {
|
||||
@@ -11,4 +12,7 @@ class AccountQuerySideController @Autowired() (accountInfoQueryService : Account
|
||||
@RequestMapping(value=Array("/accounts/{accountId}"), method = Array(RequestMethod.GET))
|
||||
def get(@PathVariable accountId : String) = accountInfoQueryService.findByAccountId(EntityId(accountId))
|
||||
|
||||
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "account not found")
|
||||
@ExceptionHandler(Array(classOf[AccountNotFoundException]))
|
||||
def accountNotFound() {}
|
||||
}
|
||||
@@ -5,9 +5,9 @@ dependencies {
|
||||
|
||||
compile "org.scala-lang:scala-library:2.10.2"
|
||||
|
||||
compile project(":commandside-backend-transactions")
|
||||
compile project(":commandside-backend-accounts")
|
||||
compile project(":queryside-backend")
|
||||
compile project(":transactions-command-side-backend")
|
||||
compile project(":accounts-command-side-backend")
|
||||
compile project(":accounts-query-side-backend")
|
||||
|
||||
testCompile scalaTestDependency
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import org.gradle.api.*
|
||||
|
||||
|
||||
class VerifyEventStoreEnvironmentPlugin implements Plugin<Project> {
|
||||
void apply(Project project) {
|
||||
project.test {
|
||||
beforeSuite { x ->
|
||||
if (x.parent == null) {
|
||||
if (System.getenv("EVENT_STORE_URL") == null)
|
||||
logger.warn("\nPLEASE make sure that Event Store-related environment variables including EVENT_STORE_URL are set, see sample-set-remote-env.sh !!!!\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
scala-spring/e2e-test/build.gradle
Normal file
20
scala-spring/e2e-test/build.gradle
Normal file
@@ -0,0 +1,20 @@
|
||||
apply plugin: 'scala'
|
||||
apply plugin: VerifyMongoDBConfigurationPlugin
|
||||
|
||||
dependencies {
|
||||
compile "org.scala-lang:scala-library:2.10.2"
|
||||
|
||||
testCompile project(":accounts-command-side-web")
|
||||
testCompile project(":transactions-command-side-web")
|
||||
testCompile project(":accounts-query-side-web")
|
||||
|
||||
testCompile "junit:junit:4.11"
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
testCompile scalaTestDependency
|
||||
|
||||
}
|
||||
|
||||
test {
|
||||
ignoreFailures true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web
|
||||
|
||||
import net.chrisrichardson.eventstore.EntityId
|
||||
import net.chrisrichardson.eventstore.examples.bank.queryside.AccountInfo
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.accounts.controllers.{CreateAccountRequest, CreateAccountResponse}
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.transactions.controllers.{CreateMoneyTransferResponse, GetMoneyTransferResponse, MoneyTransferRequest}
|
||||
import net.chrisrichardson.eventstore.json.EventStoreCommonObjectMapping.EventStoreCommonObjectMapper
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.Matchers._
|
||||
import org.scalatest.concurrent.Eventually._
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.scalatest.time.{Millis, Span}
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
|
||||
import org.springframework.web.client.RestTemplate
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class EndToEndTest extends FlatSpec {
|
||||
|
||||
val accountsCommandSideBaseUrl = s"http://localhost:8080/"
|
||||
val accountsQuerySideBaseUrl = s"http://localhost:8081/"
|
||||
val transactionsCommandSideBaseUrl = s"http://localhost:8082/"
|
||||
|
||||
val restTemplate = new RestTemplate()
|
||||
restTemplate.getMessageConverters foreach {
|
||||
case mc: MappingJackson2HttpMessageConverter =>
|
||||
mc.setObjectMapper(EventStoreCommonObjectMapper)
|
||||
case _ =>
|
||||
}
|
||||
|
||||
implicit val reallyLongPatienceConfig = PatienceConfig(timeout = Span(10 * 1000, Millis), interval = Span(1 * 1000, Millis))
|
||||
|
||||
it should "create accounts and transfer money" in {
|
||||
|
||||
val CreateAccountResponse(fromAccountId) = restTemplate.postForEntity(s"$accountsCommandSideBaseUrl/accounts", CreateAccountRequest(BigDecimal(500)), classOf[CreateAccountResponse]).getBody
|
||||
val CreateAccountResponse(toAccountId) = restTemplate.postForEntity(s"$accountsCommandSideBaseUrl/accounts", CreateAccountRequest(BigDecimal(100)), classOf[CreateAccountResponse]).getBody
|
||||
|
||||
eventually {
|
||||
val accountInfo = restTemplate.getForEntity(s"$accountsQuerySideBaseUrl/accounts/" + fromAccountId, classOf[AccountInfo]).getBody
|
||||
accountInfo should not be null
|
||||
val AccountInfo(accountId, initialBalance, _, _, _) = accountInfo
|
||||
accountId should be(fromAccountId)
|
||||
initialBalance should be(500*100)
|
||||
}(reallyLongPatienceConfig)
|
||||
|
||||
|
||||
val CreateMoneyTransferResponse(transactionId) = restTemplate.postForEntity(s"$transactionsCommandSideBaseUrl/transfers",
|
||||
MoneyTransferRequest(EntityId(fromAccountId), EntityId(toAccountId), BigDecimal(150)), classOf[CreateMoneyTransferResponse]).getBody
|
||||
|
||||
eventually {
|
||||
val AccountInfo(_, newFromAccountBalance, _, _, _) = restTemplate.getForEntity(s"$accountsQuerySideBaseUrl/accounts/" + fromAccountId, classOf[AccountInfo]).getBody
|
||||
newFromAccountBalance should be(350*100)
|
||||
}(reallyLongPatienceConfig)
|
||||
eventually {
|
||||
val AccountInfo(_, newToAccountBalance, _, _, _) = restTemplate.getForEntity(s"$accountsQuerySideBaseUrl/accounts/" + toAccountId, classOf[AccountInfo]).getBody
|
||||
newToAccountBalance should be(250*100)
|
||||
}(reallyLongPatienceConfig)
|
||||
|
||||
eventually {
|
||||
val GetMoneyTransferResponse(_, state) = restTemplate.getForEntity(s"$transactionsCommandSideBaseUrl/transfers/" + transactionId, classOf[GetMoneyTransferResponse]).getBody
|
||||
state should be("COMPLETED$")
|
||||
}(reallyLongPatienceConfig)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,5 +5,5 @@ scalaTestDependency=org.scalatest:scalatest_2.10:2.0
|
||||
|
||||
springBootVersion=1.1.10.RELEASE
|
||||
|
||||
eventStoreCommonVersion=0.2
|
||||
eventStoreClientVersion=0.2
|
||||
eventStoreClientVersion=0.5
|
||||
eventStoreCommonVersion=0.5
|
||||
|
||||
@@ -4,9 +4,9 @@ apply plugin: VerifyMongoDBConfigurationPlugin
|
||||
|
||||
dependencies {
|
||||
compile "org.scala-lang:scala-library:2.10.2"
|
||||
compile project(":commandside-web-accounts")
|
||||
compile project(":commandside-web-transactions")
|
||||
compile project(":queryside-web")
|
||||
compile project(":accounts-command-side-web")
|
||||
compile project(":transactions-command-side-web")
|
||||
compile project(":accounts-query-side-web")
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-actuator"
|
||||
@@ -2,17 +2,17 @@ package net.chrisrichardson.eventstore.examples.bank.web
|
||||
|
||||
import net.chrisrichardson.eventstore.EntityId
|
||||
import net.chrisrichardson.eventstore.examples.bank.queryside.AccountInfo
|
||||
import net.chrisrichardson.eventstore.examples.bank.transactions.TransferStates
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.accounts.controllers.{GetAccountResponse, CreateAccountRequest, CreateAccountResponse}
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.transactions.controllers.{GetMoneyTransferResponse, MoneyTransferRequest, CreateMoneyTransferResponse}
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.accounts.controllers.{CreateAccountRequest, CreateAccountResponse}
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.transactions.controllers.{CreateMoneyTransferResponse, GetMoneyTransferResponse, MoneyTransferRequest}
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.Matchers._
|
||||
import org.scalatest.concurrent.Eventually._
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.scalatest.time.{Millis, Span}
|
||||
import org.springframework.boot.SpringApplication
|
||||
import org.springframework.web.client.RestTemplate
|
||||
import org.scalatest.Matchers._
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.web.client.{HttpClientErrorException, RestTemplate}
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class BankWebIntegrationTest extends FlatSpec {
|
||||
@@ -35,7 +35,11 @@ class BankWebIntegrationTest extends FlatSpec {
|
||||
val CreateAccountResponse(fromAccountId) = restTemplate.postForEntity(s"$baseUrl/accounts", CreateAccountRequest(BigDecimal(500)), classOf[CreateAccountResponse]).getBody
|
||||
val CreateAccountResponse(toAccountId) = restTemplate.postForEntity(s"$baseUrl/accounts", CreateAccountRequest(BigDecimal(100)), classOf[CreateAccountResponse]).getBody
|
||||
|
||||
val AccountInfo(accountId, initialBalance, _, _, _) = restTemplate.getForEntity(s"$baseUrl/accounts/" + fromAccountId, classOf[AccountInfo]).getBody
|
||||
val AccountInfo(accountId, initialBalance, _, _, _) = eventually {
|
||||
val accountInfo = restTemplate.getForEntity(s"$baseUrl/accounts/" + fromAccountId, classOf[AccountInfo]).getBody
|
||||
accountInfo should not be null
|
||||
accountInfo
|
||||
}(reallyLongPatienceConfig)
|
||||
|
||||
accountId should be(fromAccountId)
|
||||
initialBalance should be(500*100)
|
||||
@@ -59,4 +63,8 @@ class BankWebIntegrationTest extends FlatSpec {
|
||||
|
||||
}
|
||||
|
||||
it should "Return 404 for non-existent account" in {
|
||||
val t = the[HttpClientErrorException] thrownBy restTemplate.getForEntity(s"$baseUrl/accounts/" + "non-existent-account-id", classOf[AccountInfo])
|
||||
t.getStatusCode shouldBe HttpStatus.NOT_FOUND
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.queryside
|
||||
|
||||
import net.chrisrichardson.eventstore.EntityId
|
||||
|
||||
class AccountInfoQueryService(accountInfoRepository : AccountInfoRepository) {
|
||||
|
||||
def findByAccountId(accountId : EntityId) : AccountInfo = accountInfoRepository.findOne(accountId.id)
|
||||
|
||||
}
|
||||
@@ -1,18 +1,23 @@
|
||||
include 'web-common'
|
||||
include 'common-web'
|
||||
|
||||
include 'common-backend'
|
||||
|
||||
include 'commandside-backend-accounts'
|
||||
include 'commandside-backend-transactions'
|
||||
include 'commandside-web-accounts'
|
||||
include 'commandside-web-transactions'
|
||||
include 'accounts-command-side-backend'
|
||||
include 'transactions-command-side-backend'
|
||||
include 'accounts-command-side-web'
|
||||
include 'transactions-command-side-web'
|
||||
|
||||
|
||||
include 'queryside-backend'
|
||||
include 'queryside-web'
|
||||
include 'accounts-query-side-backend'
|
||||
include 'accounts-query-side-web'
|
||||
|
||||
include 'backend-integration-tests'
|
||||
|
||||
include 'monolithic-web'
|
||||
include 'monolithic-service'
|
||||
include 'accounts-command-side-service'
|
||||
include 'accounts-query-side-service'
|
||||
include 'transactions-command-side-service'
|
||||
|
||||
include 'e2e-test'
|
||||
|
||||
rootProject.name = 'scala-spring-event-sourcing-example'
|
||||
|
||||
@@ -2,4 +2,4 @@ dependencies {
|
||||
compile "org.scala-lang:scala-library:2.10.2"
|
||||
compile project(":common-backend")
|
||||
compile "net.chrisrichardson.eventstore.client:eventstore-client-event-handling:$eventStoreClientVersion"
|
||||
}
|
||||
}
|
||||
22
scala-spring/transactions-command-side-service/build.gradle
Normal file
22
scala-spring/transactions-command-side-service/build.gradle
Normal file
@@ -0,0 +1,22 @@
|
||||
apply plugin: 'scala'
|
||||
apply plugin: 'spring-boot'
|
||||
apply plugin: VerifyEventStoreEnvironmentPlugin
|
||||
|
||||
dependencies {
|
||||
compile "org.scala-lang:scala-library:2.10.2"
|
||||
compile project(":transactions-command-side-web")
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-actuator"
|
||||
|
||||
compile "net.chrisrichardson.eventstore.client:eventstore-http-stomp-client:$eventStoreClientVersion"
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||
testCompile scalaTestDependency
|
||||
|
||||
}
|
||||
|
||||
test {
|
||||
ignoreFailures true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web
|
||||
|
||||
import net.chrisrichardson.eventstore.client.config.EventStoreHttpClientConfiguration
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.transactions.CommandSideWebTransactionsConfiguration
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.context.annotation._
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@Import(Array(classOf[CommandSideWebTransactionsConfiguration], classOf[EventStoreHttpClientConfiguration]))
|
||||
@ComponentScan
|
||||
class TransactionsCommandSideServiceConfiguration {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web.main
|
||||
|
||||
import net.chrisrichardson.eventstore.examples.bank.web.TransactionsCommandSideServiceConfiguration
|
||||
import org.springframework.boot.SpringApplication
|
||||
|
||||
object TransactionsCommandSideServiceMain {
|
||||
|
||||
def main(args: Array[String]) : Unit = SpringApplication.run(classOf[TransactionsCommandSideServiceConfiguration], args :_ *)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.concurrent.Eventually._
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.scalatest.time.{Millis, Span}
|
||||
import org.springframework.boot.SpringApplication
|
||||
import org.springframework.web.client.RestTemplate
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class TransactionsCommandSideServiceIntegrationTest extends FlatSpec {
|
||||
|
||||
val sa = new SpringApplication(classOf[TransactionsCommandSideServiceTestConfiguration])
|
||||
val ctx = sa.run()
|
||||
|
||||
// var server = ctx.getBean(classOf[EmbeddedServletContainer])
|
||||
|
||||
val port = 8080
|
||||
|
||||
val baseUrl = s"http://localhost:$port/"
|
||||
|
||||
val restTemplate = ctx.getBean(classOf[RestTemplate])
|
||||
|
||||
implicit val reallyLongPatienceConfig = PatienceConfig(timeout = Span(10 * 1000, Millis), interval = Span(1 * 1000, Millis))
|
||||
|
||||
it should "create accounts and transfer money" in {
|
||||
// FIXME
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.chrisrichardson.eventstore.examples.bank.web
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.context.annotation.{Bean, Import, Configuration}
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
|
||||
import org.springframework.web.client.RestTemplate
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
@Configuration
|
||||
@Import(Array(classOf[TransactionsCommandSideServiceConfiguration]))
|
||||
class TransactionsCommandSideServiceTestConfiguration {
|
||||
|
||||
@Bean
|
||||
def restTemplate(scalaObjectMapper: ObjectMapper) = {
|
||||
val restTemplate = new RestTemplate()
|
||||
restTemplate.getMessageConverters foreach {
|
||||
case mc: MappingJackson2HttpMessageConverter =>
|
||||
mc.setObjectMapper(scalaObjectMapper)
|
||||
case _ =>
|
||||
}
|
||||
restTemplate
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,9 +2,9 @@ apply plugin: 'scala'
|
||||
|
||||
dependencies {
|
||||
compile "org.scala-lang:scala-library:2.10.2"
|
||||
compile project(":commandside-backend-transactions")
|
||||
compile project(":web-common")
|
||||
compile project(":transactions-command-side-backend")
|
||||
compile project(":common-web")
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
testCompile scalaTestDependency
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user