├── .gitignore ├── LICENSE.md ├── README.md ├── _build-and-test-all.sh ├── java-spring ├── README.md ├── accounts-service │ ├── Dockerfile │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── net │ │ │ └── chrisrichardson │ │ │ └── eventstore │ │ │ └── javaexamples │ │ │ └── banking │ │ │ └── accountsservice │ │ │ ├── AccountsServiceMain.java │ │ │ ├── backend │ │ │ ├── Account.java │ │ │ ├── AccountCommand.java │ │ │ ├── AccountService.java │ │ │ ├── AccountWorkflow.java │ │ │ ├── AccountsBackendConfiguration.java │ │ │ ├── CreditAccountCommand.java │ │ │ ├── DebitAccountCommand.java │ │ │ ├── DeleteAccountCommand.java │ │ │ └── OpenAccountCommand.java │ │ │ └── web │ │ │ ├── AccountController.java │ │ │ └── AccountsWebConfiguration.java │ │ └── test │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── accountsservice │ │ ├── AccountsCommandSideServiceIntegrationTest.java │ │ ├── AccountsCommandSideServiceTestConfiguration.java │ │ ├── backend │ │ ├── AccountEventTest.java │ │ └── AccountTest.java │ │ └── web │ │ ├── AccountControllerIntegrationTest.java │ │ └── AccountControllerIntegrationTestConfiguration.java ├── accounts-view-service │ ├── Dockerfile │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── net │ │ │ └── chrisrichardson │ │ │ └── eventstore │ │ │ └── javaexamples │ │ │ └── banking │ │ │ └── accountsviewservice │ │ │ ├── AccountsViewServiceMain.java │ │ │ ├── backend │ │ │ ├── AccountInfo.java │ │ │ ├── AccountInfoRepository.java │ │ │ ├── AccountInfoUpdateService.java │ │ │ ├── AccountNotFoundException.java │ │ │ ├── AccountQueryService.java │ │ │ ├── AccountQueryWorkflow.java │ │ │ ├── AccountViewBackendConfiguration.java │ │ │ ├── MoneyUtil.java │ │ │ └── QuerySideDependencyChecker.java │ │ │ └── web │ │ │ ├── AccountQueryController.java │ │ │ └── AccountViewWebConfiguration.java │ │ └── test │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── web │ │ ├── AccountInfoUpdateServiceTest.java │ │ ├── AccountsQuerySideServiceIntegrationTest.java │ │ └── AccountsQuerySideServiceTestConfiguration.java ├── api-gateway-service │ ├── Dockerfile │ ├── build.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── net │ │ │ └── chrisrichardson │ │ │ └── eventstore │ │ │ └── javaexamples │ │ │ └── banking │ │ │ └── apigateway │ │ │ ├── ApiGatewayProperties.java │ │ │ ├── ApiGatewayServiceConfiguration.java │ │ │ ├── RestTemplateErrorHandler.java │ │ │ ├── RestUtil.java │ │ │ ├── controller │ │ │ └── GatewayController.java │ │ │ ├── main │ │ │ └── ApiGatewayServiceMain.java │ │ │ └── utils │ │ │ ├── ContentRequestTransformer.java │ │ │ ├── HeadersRequestTransformer.java │ │ │ ├── ProxyRequestTransformer.java │ │ │ └── URLRequestTransformer.java │ │ └── resources │ │ ├── application.properties │ │ └── logback.xml ├── backend-integration-tests │ ├── build.gradle │ └── src │ │ └── test │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── backend │ │ ├── BankingTestConfiguration.java │ │ ├── MoneyTransferIntegrationTest.java │ │ └── queryside │ │ ├── accounts │ │ ├── AccountQuerySideIntegrationTest.java │ │ └── AccountQuerySideTestConfiguration.java │ │ └── customers │ │ ├── CustomerQuerySideIntegrationTest.java │ │ └── CustomerQuerySideTestConfiguration.java ├── build-and-test-all-eventuate-local.sh ├── build-and-test-all.sh ├── build.gradle ├── buildSrc │ └── src │ │ └── main │ │ └── groovy │ │ ├── EventuateDependencyPlugin.groovy │ │ ├── VerifyEventStoreEnvironmentPlugin.groovy │ │ └── VerifyMongoDBConfigurationPlugin.groovy ├── common-auth-web │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── net │ │ │ └── chrisrichardson │ │ │ └── eventstore │ │ │ └── javaexamples │ │ │ └── banking │ │ │ └── commonauth │ │ │ ├── controller │ │ │ └── AuthController.java │ │ │ └── model │ │ │ ├── AuthRequest.java │ │ │ └── ErrorResponse.java │ │ └── test │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── commonauth │ │ └── controller │ │ ├── AuthControllerIntegrationTest.java │ │ └── AuthControllerIntegrationTestConfiguration.java ├── common-auth │ ├── build.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── net │ │ │ └── chrisrichardson │ │ │ └── eventstore │ │ │ └── javaexamples │ │ │ └── banking │ │ │ └── commonauth │ │ │ ├── AuthConfiguration.java │ │ │ ├── AuthProperties.java │ │ │ ├── CustomerAuthRepository.java │ │ │ ├── CustomerAuthService.java │ │ │ ├── TokenAuthenticationService.java │ │ │ ├── filter │ │ │ └── StatelessAuthenticationFilter.java │ │ │ └── model │ │ │ ├── User.java │ │ │ └── UserAuthentication.java │ │ └── resources │ │ └── auth.properties ├── common-backend │ ├── build.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── net │ │ │ │ └── chrisrichardson │ │ │ │ └── eventstore │ │ │ │ └── javaexamples │ │ │ │ └── banking │ │ │ │ └── backend │ │ │ │ └── common │ │ │ │ ├── accounts │ │ │ │ ├── AccountChangedEvent.java │ │ │ │ ├── AccountCreditedEvent.java │ │ │ │ ├── AccountDebitFailedDueToInsufficientFundsEvent.java │ │ │ │ ├── AccountDebitedEvent.java │ │ │ │ ├── AccountDeletedEvent.java │ │ │ │ ├── AccountEvent.java │ │ │ │ └── AccountOpenedEvent.java │ │ │ │ ├── customers │ │ │ │ ├── CustomerAddedToAccount.java │ │ │ │ ├── CustomerCreatedEvent.java │ │ │ │ ├── CustomerEvent.java │ │ │ │ └── CustomerToAccountDeleted.java │ │ │ │ └── transactions │ │ │ │ ├── CreditRecordedEvent.java │ │ │ │ ├── DebitRecordedEvent.java │ │ │ │ ├── FailedDebitRecordedEvent.java │ │ │ │ ├── MoneyTransferCreatedEvent.java │ │ │ │ ├── MoneyTransferEvent.java │ │ │ │ └── TransferDetails.java │ │ └── resources │ │ │ └── logback.xml │ │ └── test │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── common │ │ └── accounts │ │ └── AccountOpenEventSerializationTest.java ├── common-swagger │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── commonswagger │ │ └── CommonSwaggerConfiguration.java ├── common │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── common │ │ ├── accounts │ │ ├── AccountChangeInfo.java │ │ ├── AccountHistoryEntry.java │ │ ├── AccountHistoryResponse.java │ │ ├── AccountOpenInfo.java │ │ ├── AccountTransactionInfo.java │ │ ├── CreateAccountRequest.java │ │ ├── CreateAccountResponse.java │ │ ├── DeleteAccountResponse.java │ │ ├── GetAccountResponse.java │ │ └── GetAccountsResponse.java │ │ ├── customers │ │ ├── AddToAccountResponse.java │ │ ├── Address.java │ │ ├── CustomerInfo.java │ │ ├── CustomerResponse.java │ │ ├── Name.java │ │ ├── ToAccountInfo.java │ │ └── UserCredentials.java │ │ └── transactions │ │ ├── CreateMoneyTransferRequest.java │ │ ├── CreateMoneyTransferResponse.java │ │ └── TransferState.java ├── customers-query-side-common │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── web │ │ └── customers │ │ └── queryside │ │ └── common │ │ └── QuerySideCustomer.java ├── customers-service │ ├── Dockerfile │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── net │ │ │ └── chrisrichardson │ │ │ └── eventstore │ │ │ └── javaexamples │ │ │ └── banking │ │ │ └── customersservice │ │ │ ├── CustomersServiceMain.java │ │ │ ├── backend │ │ │ ├── AddToAccountCommand.java │ │ │ ├── CreateCustomerCommand.java │ │ │ ├── Customer.java │ │ │ ├── CustomerBackendConfiguration.java │ │ │ ├── CustomerCommand.java │ │ │ ├── CustomerService.java │ │ │ └── DeleteToAccountCommand.java │ │ │ └── web │ │ │ ├── CustomerController.java │ │ │ └── CustomersWebConfiguration.java │ │ └── test │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── customersservice │ │ ├── CustomersCommandSideServiceIntegrationTest.java │ │ ├── CustomersCommandSideServiceTestConfiguration.java │ │ └── backend │ │ ├── CustomerEventTest.java │ │ └── CustomerTest.java ├── customers-view-service │ ├── Dockerfile │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── net │ │ │ └── chrisrichardson │ │ │ └── eventstore │ │ │ └── javaexamples │ │ │ └── banking │ │ │ └── customersviewservice │ │ │ ├── CustomersViewServiceMain.java │ │ │ ├── backend │ │ │ ├── CustomerInfoUpdateService.java │ │ │ ├── CustomerQueryService.java │ │ │ ├── CustomerQueryWorkflow.java │ │ │ ├── CustomerViewBackendConfiguration.java │ │ │ ├── CustomerViewRepository.java │ │ │ └── ViewDependencyChecker.java │ │ │ └── web │ │ │ ├── CustomerQueryController.java │ │ │ ├── CustomersQueryResponse.java │ │ │ └── CustomersViewWebConfiguration.java │ │ └── test │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── customersviewservice │ │ ├── CustomersQuerySideServiceIntegrationTest.java │ │ ├── CustomersQuerySideServiceTestConfiguration.java │ │ └── backend │ │ └── CustomerInfoUpdateServiceTest.java ├── docker-compose-common.yml ├── docker-compose-eventuate-local.yml ├── docker-compose.yml ├── e2e-test │ ├── build.gradle │ └── src │ │ └── test │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── web │ │ └── EndToEndTest.java ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── handy-curl-commands.sh ├── mongodb-cli.sh ├── rest-api-integration-tests │ ├── build.gradle │ └── src │ │ └── test │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── web │ │ ├── BankingAuthTest.java │ │ ├── BankingWebIntegrationTest.java │ │ └── BankingWebTestConfiguration.java ├── schemas │ └── java-mt-demo-extended-api.json ├── set-env.sh ├── settings.gradle ├── show-urls.sh ├── testutil │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstorestore │ │ └── javaexamples │ │ └── testutil │ │ ├── AbstractEntityEventTest.java │ │ ├── AbstractRestAPITest.java │ │ ├── AuthenticatedRestTemplate.java │ │ ├── BasicAuthUtils.java │ │ ├── CustomersTestUtils.java │ │ ├── Producer.java │ │ ├── RestTemplateErrorHandler.java │ │ ├── RestUtil.java │ │ ├── TestUtil.java │ │ └── Verifier.java └── transactions-service │ ├── Dockerfile │ ├── build.gradle │ └── src │ ├── main │ └── java │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── javaexamples │ │ └── banking │ │ └── transactionsservice │ │ ├── TransactionsServiceMain.java │ │ ├── backend │ │ ├── CreateMoneyTransferCommand.java │ │ ├── MoneyTransfer.java │ │ ├── MoneyTransferBackendConfiguration.java │ │ ├── MoneyTransferCommand.java │ │ ├── MoneyTransferService.java │ │ ├── MoneyTransferWorkflow.java │ │ ├── RecordCreditCommand.java │ │ ├── RecordDebitCommand.java │ │ └── RecordDebitFailedCommand.java │ │ └── web │ │ ├── MoneyTransferController.java │ │ └── MoneyTransferWebConfiguration.java │ └── test │ └── java │ └── net │ └── chrisrichardson │ └── eventstore │ └── javaexamples │ └── banking │ └── transactionsservice │ ├── TransactionsCommandSideServiceIntegrationTest.java │ ├── TransactionsCommandSideServiceTestConfiguration.java │ ├── backend │ └── MoneyTransferEventTest.java │ └── web │ ├── MoneyTransferControllerIntegrationTest.java │ └── MoneyTransferControllerIntegrationTestConfiguration.java ├── js-frontend ├── .babelrc ├── .gitignore ├── README.md ├── build │ ├── .gitkeep │ ├── app.30980d75b78111237ddf.js │ ├── app.30980d75b78111237ddf.js.map │ ├── index.html │ ├── manifest.2790e4278ac45db31919.js │ ├── manifest.2790e4278ac45db31919.js.map │ ├── robots.txt │ ├── style.b588c60da106277d78c8.css │ ├── style.b588c60da106277d78c8.css.map │ ├── style.b588c60da106277d78c8.js │ ├── style.b588c60da106277d78c8.js.map │ ├── style2.css │ ├── vendor.85781b28c9410377534e.js │ └── vendor.85781b28c9410377534e.js.map ├── config │ ├── environments │ │ ├── development.js │ │ └── production.js │ └── webpackConfigParts.js ├── nightwatch.conf.js ├── nightwatch.json ├── package.json ├── public │ ├── .gitkeep │ ├── index.ejs │ ├── robots.txt │ └── style2.css ├── reports │ └── .gitkeep ├── src │ ├── App.js │ ├── actions │ │ ├── authenticate.js │ │ ├── configure.js │ │ ├── entities.js │ │ ├── navigate.js │ │ ├── signIn.js │ │ ├── signOut.js │ │ └── signUp.js │ ├── client.js │ ├── components │ │ ├── AccountInfo.js │ │ ├── AuthComponent.js │ │ ├── HeaderLinks.js │ │ ├── Money.js │ │ ├── TransfersTable.js │ │ └── partials │ │ │ ├── Container.js │ │ │ └── IndexPanel.js │ ├── constants │ │ └── ACTION_TYPES.js │ ├── controls │ │ └── bootstrap │ │ │ ├── AuxErrorLabel.js │ │ │ ├── ButtonLoader.js │ │ │ ├── EmailSignInForm.js │ │ │ ├── EmailSignUpForm.js │ │ │ └── Input.js │ ├── entities │ │ └── formToPayloadMappers.js │ ├── index.html │ ├── main.less │ ├── reducers │ │ ├── auth │ │ │ ├── authenticate.js │ │ │ ├── configure.js │ │ │ ├── index.js │ │ │ ├── signin.js │ │ │ ├── signout.js │ │ │ ├── signup.js │ │ │ └── user.js │ │ ├── createDataReducer.js │ │ ├── createFormReducer.js │ │ ├── data │ │ │ ├── accounts.js │ │ │ ├── bookmarkAccount.js │ │ │ ├── entities.js │ │ │ ├── index.js │ │ │ └── transfers.js │ │ ├── index.js │ │ └── ui │ │ │ ├── account.js │ │ │ ├── bookmarkAccount.js │ │ │ ├── errors.js │ │ │ ├── index.js │ │ │ └── transfersMake.js │ ├── static │ │ ├── .gitkeep │ │ └── style2.css │ ├── theme │ │ └── .gitkeep │ ├── utils │ │ ├── actions.js │ │ ├── api.js │ │ ├── apiEndpoints.js │ │ ├── blockedExecution.js │ │ ├── clientSettings.js │ │ ├── compact.js │ │ ├── compose.js │ │ ├── constants.js │ │ ├── defineActionTypes.js │ │ ├── fetch.js │ │ ├── handleFetchResponse.js │ │ ├── parseEndpointConfig.js │ │ ├── parseUrl.js │ │ ├── partial.js │ │ ├── readProp.js │ │ ├── root.js │ │ ├── sessionStorage.js │ │ ├── typeReducers.js │ │ └── uuid.js │ └── views │ │ ├── Account.js │ │ ├── MyAccounts.js │ │ ├── SignIn.js │ │ ├── SignUp.js │ │ └── modals │ │ ├── Add3rdPartyAccountModal.js │ │ ├── NewAccountModal.js │ │ ├── RemoveAccountModal.js │ │ └── index.js ├── tests │ ├── e2e-globals │ │ └── globals.js │ ├── e2e-pages │ │ ├── instancesPage.js │ │ ├── loginPage.js │ │ └── signupPage.js │ └── e2e-tests │ │ ├── test010_Signup.js │ │ ├── test020_Login.js │ │ ├── test030_CreateAccount.js │ │ └── test040_Create3rdPartyAccs.js └── webpack.config.js ├── prebuilt-web-client ├── index.html ├── main-3c1aebc49347fa338f9c.js ├── style.css └── style2.css ├── scala-spring ├── README.md ├── accounts-command-side-backend │ ├── build.gradle │ └── src │ │ └── main │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── accounts │ │ ├── Account.scala │ │ ├── AccountCommands.scala │ │ ├── AccountConfiguration.scala │ │ ├── AccountService.scala │ │ └── TransferWorkflowAccountHandlers.scala ├── accounts-command-side-service │ ├── build.gradle │ └── src │ │ ├── main │ │ └── scala │ │ │ └── net │ │ │ └── chrisrichardson │ │ │ └── eventstore │ │ │ └── examples │ │ │ └── bank │ │ │ └── web │ │ │ ├── AccountsCommandSideServiceConfiguration.scala │ │ │ └── main │ │ │ └── AccountsCommandSideServiceMain.scala │ │ └── test │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── web │ │ ├── AccountsCommandSideServiceIntegrationTest.scala │ │ └── AccountsCommandSideServiceTestConfiguration.scala ├── accounts-command-side-web │ ├── build.gradle │ └── src │ │ └── main │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── web │ │ └── accounts │ │ ├── CommandSideWebAccountsConfiguration.scala │ │ └── controllers │ │ ├── AccountController.scala │ │ ├── CreateAccountRequest.scala │ │ └── CreateAccountResponse.scala ├── accounts-query-side-backend │ ├── build.gradle │ └── src │ │ └── main │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── queryside │ │ ├── AccountInfo.scala │ │ ├── AccountInfoQueryService.scala │ │ ├── AccountInfoUpdateService.scala │ │ ├── QuerySideConfiguration.scala │ │ └── QuerysideDependencyChecker.scala ├── accounts-query-side-service │ ├── build.gradle │ └── src │ │ ├── main │ │ └── scala │ │ │ └── net │ │ │ └── chrisrichardson │ │ │ └── eventstore │ │ │ └── examples │ │ │ └── bank │ │ │ └── web │ │ │ ├── AccountsQuerySideServiceConfiguration.scala │ │ │ └── main │ │ │ └── AccountsQuerySideServiceMain.scala │ │ └── test │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── web │ │ ├── AccountsQuerySideServiceIntegrationTest.scala │ │ └── AccountsQuerySideServiceTestConfiguration.scala ├── accounts-query-side-web │ ├── build.gradle │ └── src │ │ └── main │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── web │ │ └── queryside │ │ ├── QuerySideWebConfiguration.scala │ │ └── controllers │ │ └── AccountQuerySideController.scala ├── backend-integration-tests │ ├── build.gradle │ └── src │ │ └── test │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ ├── MoneyTransferIntegrationTest.scala │ │ └── config │ │ └── BankingTestConfiguration.scala ├── build-and-test-all.sh ├── build.gradle ├── buildSrc │ └── src │ │ └── main │ │ └── groovy │ │ ├── VerifyEventStoreEnvironmentPlugin.groovy │ │ └── VerifyMongoDBConfigurationPlugin.groovy ├── common-backend │ ├── build.gradle │ └── src │ │ └── main │ │ ├── resources │ │ └── logback.xml │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── backend │ │ └── common │ │ ├── accounts │ │ ├── AccountEvents.scala │ │ └── package-info.java │ │ └── transactions │ │ ├── TransactionEvents.scala │ │ ├── TransferDetails.scala │ │ └── package-info.java ├── common-web │ ├── build.gradle │ └── src │ │ └── main │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── web │ │ └── util │ │ └── WebUtil.scala ├── docker-compose.yml ├── e2e-test │ ├── build.gradle │ └── src │ │ └── test │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── web │ │ └── EndToEndTest.scala ├── gradle.properties ├── gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── handy-curl-commands.sh ├── monolithic-service │ ├── build.gradle │ └── src │ │ ├── main │ │ └── scala │ │ │ └── net │ │ │ └── chrisrichardson │ │ │ └── eventstore │ │ │ └── examples │ │ │ └── bank │ │ │ └── web │ │ │ ├── BankingWebAppConfiguration.scala │ │ │ └── main │ │ │ └── BankingMain.scala │ │ └── test │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── web │ │ ├── BankWebIntegrationTest.scala │ │ └── BankingWebAppTestConfiguration.scala ├── settings.gradle ├── transactions-command-side-backend │ ├── build.gradle │ └── src │ │ └── main │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── transactions │ │ ├── MoneyTransfer.scala │ │ ├── MoneyTransferCommands.scala │ │ ├── MoneyTransferEventHandlers.scala │ │ ├── MoneyTransferService.scala │ │ └── TransactionConfiguration.scala ├── transactions-command-side-service │ ├── build.gradle │ └── src │ │ ├── main │ │ └── scala │ │ │ └── net │ │ │ └── chrisrichardson │ │ │ └── eventstore │ │ │ └── examples │ │ │ └── bank │ │ │ └── web │ │ │ ├── TransactionsCommandSideServiceConfiguration.scala │ │ │ └── main │ │ │ └── TransactionsCommandSideServiceMain.scala │ │ └── test │ │ └── scala │ │ └── net │ │ └── chrisrichardson │ │ └── eventstore │ │ └── examples │ │ └── bank │ │ └── web │ │ ├── TransactionsCommandSideServiceIntegrationTest.scala │ │ └── TransactionsCommandSideServiceTestConfiguration.scala └── transactions-command-side-web │ ├── build.gradle │ └── src │ └── main │ └── scala │ └── net │ └── chrisrichardson │ └── eventstore │ └── examples │ └── bank │ └── web │ └── transactions │ ├── CommandSideWebTransactionsConfiguration.scala │ └── controllers │ ├── CreateMoneyTransferResponse.scala │ └── MoneyTransferController.scala ├── wait-for-docker-services.sh └── wait-for-services.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.ipr 3 | *.iws 4 | .DS_Store 5 | .cache 6 | .classpath 7 | .gradle 8 | .idea 9 | .project 10 | .scala_dependencies 11 | .settings 12 | .springBeans 13 | bin 14 | build 15 | build*.log 16 | classes 17 | genjs 18 | node_modules 19 | npm-debug.log 20 | target 21 | out 22 | app-staging 23 | apigateway/reports 24 | browserapp/reports 25 | loginapp/reports 26 | local_developer_config 27 | 28 | js-frontend/node_modules 29 | js-frontend/build 30 | js-frontend/dist 31 | js-frontend/dist-intermediate 32 | 33 | 34 | 35 | /web/web.log 36 | *.log 37 | *.pid 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2015 Chris Richardson Consulting, Inc. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /java-spring/README.md: -------------------------------------------------------------------------------- 1 | This is the Java/Spring version of the Event Sourcing/CQRS money transfer example application. 2 | 3 | # About the application 4 | 5 | This application consists of the following microservices: 6 | 7 | * Account Service - the command side business logic for Accounts 8 | * Account View Service - query side implementation of a MongoDB-based, denormalized view of Accounts 9 | * Customer Service - the command side business logic for Customers 10 | * Customer View Service - query side implementation of a MongoDB-based, denormalized view of Customers 11 | * Transaction Service - the command side business logic for Money Transfers 12 | -------------------------------------------------------------------------------- /java-spring/accounts-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:openjdk-8u91-jdk 2 | CMD java ${JAVA_OPTS} -jar accounts-service.jar 3 | EXPOSE 8080 4 | COPY build/libs/accounts-service.jar . -------------------------------------------------------------------------------- /java-spring/accounts-service/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: VerifyEventStoreEnvironmentPlugin 2 | apply plugin: EventuateDependencyPlugin 3 | 4 | apply plugin: 'spring-boot' 5 | 6 | dependencies { 7 | compile project(":common-backend") 8 | compile project(":common-swagger") 9 | 10 | compile "org.springframework.boot:spring-boot-starter-web" 11 | compile "org.springframework.boot:spring-boot-starter-actuator" 12 | 13 | testCompile project(":testutil") 14 | testCompile "junit:junit:4.11" 15 | testCompile "org.springframework.boot:spring-boot-starter-test" 16 | testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" 17 | } 18 | 19 | test { 20 | ignoreFailures System.getenv("EVENTUATE_API_KEY_ID") == null 21 | } 22 | -------------------------------------------------------------------------------- /java-spring/accounts-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/AccountsServiceMain.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice; 2 | 3 | import io.eventuate.javaclient.driver.EventuateDriverConfiguration; 4 | import net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.web.AccountsWebConfiguration; 5 | import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 8 | import org.springframework.context.annotation.ComponentScan; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Import; 11 | 12 | @Configuration 13 | @Import({AccountsWebConfiguration.class, EventuateDriverConfiguration.class, CommonSwaggerConfiguration.class}) 14 | @EnableAutoConfiguration 15 | @ComponentScan 16 | public class AccountsServiceMain { 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(AccountsServiceMain.class, args); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /java-spring/accounts-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/backend/AccountCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend; 2 | 3 | 4 | import io.eventuate.Command; 5 | 6 | interface AccountCommand extends Command { 7 | } 8 | -------------------------------------------------------------------------------- /java-spring/accounts-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/backend/AccountService.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend; 2 | 3 | 4 | import io.eventuate.AggregateRepository; 5 | import io.eventuate.EntityWithIdAndVersion; 6 | 7 | import java.math.BigDecimal; 8 | import java.util.concurrent.CompletableFuture; 9 | 10 | public class AccountService { 11 | 12 | private final AggregateRepository<Account, AccountCommand> accountRepository; 13 | 14 | public AccountService(AggregateRepository<Account, AccountCommand> accountRepository) { 15 | this.accountRepository = accountRepository; 16 | } 17 | 18 | public CompletableFuture<EntityWithIdAndVersion<Account>> openAccount(String customerId, String title, BigDecimal initialBalance, String description) { 19 | return accountRepository.save(new OpenAccountCommand(customerId, title, initialBalance, description)); 20 | } 21 | 22 | public CompletableFuture<EntityWithIdAndVersion<Account>> deleteAccount(String accountId) { 23 | return accountRepository.update(accountId, new DeleteAccountCommand()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /java-spring/accounts-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/backend/AccountsBackendConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend; 2 | 3 | import io.eventuate.AggregateRepository; 4 | import io.eventuate.EventuateAggregateStore; 5 | import io.eventuate.javaclient.spring.EnableEventHandlers; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | @EnableEventHandlers 11 | public class AccountsBackendConfiguration { 12 | 13 | @Bean 14 | public AccountWorkflow accountWorkflow() { 15 | return new AccountWorkflow(); 16 | } 17 | 18 | 19 | @Bean 20 | public AccountService accountService(AggregateRepository<Account, AccountCommand> accountRepository) { 21 | return new AccountService(accountRepository); 22 | } 23 | 24 | @Bean 25 | public AggregateRepository<Account, AccountCommand> accountRepository(EventuateAggregateStore eventStore) { 26 | return new AggregateRepository<Account, AccountCommand>(Account.class, eventStore); 27 | } 28 | 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /java-spring/accounts-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/backend/CreditAccountCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class CreditAccountCommand implements AccountCommand { 6 | private final BigDecimal amount; 7 | private final String transactionId; 8 | 9 | public CreditAccountCommand(BigDecimal amount, String transactionId) { 10 | 11 | this.amount = amount; 12 | this.transactionId = transactionId; 13 | } 14 | 15 | public BigDecimal getAmount() { 16 | return amount; 17 | } 18 | 19 | public String getTransactionId() { 20 | return transactionId; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /java-spring/accounts-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/backend/DebitAccountCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class DebitAccountCommand implements AccountCommand { 6 | private final BigDecimal amount; 7 | private final String transactionId; 8 | 9 | public DebitAccountCommand(BigDecimal amount, String transactionId) { 10 | 11 | this.amount = amount; 12 | this.transactionId = transactionId; 13 | } 14 | 15 | public BigDecimal getAmount() { 16 | return amount; 17 | } 18 | 19 | public String getTransactionId() { 20 | return transactionId; 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /java-spring/accounts-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/backend/DeleteAccountCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend; 2 | 3 | public class DeleteAccountCommand implements AccountCommand { 4 | } 5 | -------------------------------------------------------------------------------- /java-spring/accounts-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/backend/OpenAccountCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend; 2 | 3 | 4 | import java.math.BigDecimal; 5 | 6 | public class OpenAccountCommand implements AccountCommand { 7 | 8 | private String customerId; 9 | private String title; 10 | private BigDecimal initialBalance; 11 | private String description; 12 | 13 | public OpenAccountCommand(String customerId, String title, BigDecimal initialBalance, String description) { 14 | this.customerId = customerId; 15 | this.title = title; 16 | this.initialBalance = initialBalance; 17 | this.description = description; 18 | } 19 | 20 | public BigDecimal getInitialBalance() { 21 | return initialBalance; 22 | } 23 | 24 | public String getCustomerId() { 25 | return customerId; 26 | } 27 | 28 | public String getTitle() { 29 | return title; 30 | } 31 | 32 | public String getDescription() { 33 | return description; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /java-spring/accounts-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/web/AccountsWebConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.web; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend.AccountsBackendConfiguration; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.context.annotation.ComponentScan; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Import; 8 | 9 | @Configuration 10 | @Import({AccountsBackendConfiguration.class}) 11 | @ComponentScan 12 | public class AccountsWebConfiguration { 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /java-spring/accounts-service/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/backend/AccountEventTest.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend; 2 | 3 | 4 | import net.chrisrichardson.eventstorestore.javaexamples.testutil.AbstractEntityEventTest; 5 | 6 | public class AccountEventTest extends AbstractEntityEventTest { 7 | 8 | @Override 9 | protected Class<Account> entityClass() { 10 | return Account.class; 11 | } 12 | } -------------------------------------------------------------------------------- /java-spring/accounts-service/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/backend/AccountTest.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend; 2 | 3 | import io.eventuate.Event; 4 | import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountOpenedEvent; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.math.BigDecimal; 9 | import java.util.List; 10 | 11 | public class AccountTest { 12 | 13 | @Test 14 | public void testSomething() { 15 | Account account = new Account(); 16 | String title = "My Account"; 17 | String customerId = "00000000-00000000"; 18 | BigDecimal initialBalance = new BigDecimal(512); 19 | 20 | List<Event> events = account.process(new OpenAccountCommand(customerId, title, initialBalance, "")); 21 | 22 | Assert.assertEquals(1, events.size()); 23 | Assert.assertEquals(AccountOpenedEvent.class, events.get(0).getClass()); 24 | 25 | account.applyEvent(events.get(0)); 26 | Assert.assertEquals(initialBalance, account.getBalance()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /java-spring/accounts-service/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsservice/web/AccountControllerIntegrationTestConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.web; 2 | 3 | import io.eventuate.javaclient.spring.jdbc.EmbeddedTestAggregateStoreConfiguration; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.Import; 7 | 8 | @Configuration 9 | @Import({AccountsWebConfiguration.class, EmbeddedTestAggregateStoreConfiguration.class}) 10 | @EnableAutoConfiguration 11 | public class AccountControllerIntegrationTestConfiguration { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /java-spring/accounts-view-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:openjdk-8u91-jdk 2 | CMD java ${JAVA_OPTS} -jar accounts-view-service.jar 3 | EXPOSE 8080 4 | COPY build/libs/accounts-view-service.jar . 5 | -------------------------------------------------------------------------------- /java-spring/accounts-view-service/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: VerifyMongoDBConfigurationPlugin 2 | apply plugin: VerifyEventStoreEnvironmentPlugin 3 | apply plugin: EventuateDependencyPlugin 4 | 5 | apply plugin: 'spring-boot' 6 | 7 | dependencies { 8 | compile project(":common-swagger") 9 | compile project(":common-backend") 10 | 11 | compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" 12 | compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" 13 | compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion" 14 | compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion" 15 | 16 | testCompile project(":testutil") 17 | testCompile "junit:junit:4.11" 18 | testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" 19 | testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" 20 | } 21 | 22 | test { 23 | ignoreFailures System.getenv("EVENTUATE_API_KEY_ID") == null 24 | } 25 | -------------------------------------------------------------------------------- /java-spring/accounts-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsviewservice/AccountsViewServiceMain.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsviewservice; 2 | 3 | import io.eventuate.javaclient.driver.EventuateDriverConfiguration; 4 | import net.chrisrichardson.eventstore.javaexamples.banking.accountsviewservice.web.AccountViewWebConfiguration; 5 | import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 8 | import org.springframework.context.annotation.ComponentScan; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Import; 11 | 12 | @Configuration 13 | @Import({AccountViewWebConfiguration.class, EventuateDriverConfiguration.class, CommonSwaggerConfiguration.class}) 14 | @EnableAutoConfiguration 15 | @ComponentScan 16 | public class AccountsViewServiceMain { 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(AccountsViewServiceMain.class, args); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /java-spring/accounts-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsviewservice/backend/AccountInfoRepository.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsviewservice.backend; 2 | 3 | import org.springframework.data.mongodb.repository.MongoRepository; 4 | 5 | import java.util.List; 6 | 7 | interface AccountInfoRepository extends MongoRepository<AccountInfo, String> { 8 | 9 | List<AccountInfo> findByCustomerId(String customerId); 10 | } 11 | -------------------------------------------------------------------------------- /java-spring/accounts-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsviewservice/backend/AccountNotFoundException.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsviewservice.backend; 2 | 3 | public class AccountNotFoundException extends RuntimeException { 4 | 5 | public AccountNotFoundException(String accountId) { 6 | super("Account not found " + accountId); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /java-spring/accounts-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsviewservice/backend/AccountQueryService.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsviewservice.backend; 2 | 3 | import java.util.List; 4 | 5 | public class AccountQueryService { 6 | 7 | private AccountInfoRepository accountInfoRepository; 8 | 9 | public AccountQueryService(AccountInfoRepository accountInfoRepository) { 10 | this.accountInfoRepository = accountInfoRepository; 11 | } 12 | 13 | public AccountInfo findByAccountId(String accountId) { 14 | AccountInfo account = accountInfoRepository.findOne(accountId); 15 | if (account == null) 16 | throw new AccountNotFoundException(accountId); 17 | else 18 | return account; 19 | } 20 | 21 | public List<AccountInfo> findByCustomerId(String customerId) { 22 | return accountInfoRepository.findByCustomerId(customerId); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java-spring/accounts-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsviewservice/backend/MoneyUtil.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsviewservice.backend; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class MoneyUtil { 6 | 7 | public static long toIntegerRepr(BigDecimal d) { 8 | return d.multiply(new BigDecimal(100)).longValueExact(); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /java-spring/accounts-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsviewservice/backend/QuerySideDependencyChecker.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsviewservice.backend; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.data.mongodb.core.MongoTemplate; 6 | 7 | import javax.annotation.PostConstruct; 8 | 9 | public class QuerySideDependencyChecker { 10 | private Logger logger = LoggerFactory.getLogger(getClass()); 11 | private MongoTemplate mongoTemplate; 12 | 13 | public QuerySideDependencyChecker(MongoTemplate mongoTemplate) { 14 | this.mongoTemplate = mongoTemplate; 15 | } 16 | 17 | @PostConstruct 18 | public void checkDependencies() { 19 | try { 20 | logger.info("Checking mongodb connectivity {}", System.getenv("SPRING_DATA_MONGODB_URI")); 21 | 22 | mongoTemplate.getDb().getCollectionNames(); 23 | 24 | } catch (Throwable e) { 25 | throw new RuntimeException("Error connecting to Mongo - have you set SPRING_DATA_MONGODB_URI or --spring.data.mongodb_uri?", e); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /java-spring/accounts-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/accountsviewservice/web/AccountViewWebConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.accountsviewservice.web; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.accountsviewservice.backend.AccountViewBackendConfiguration; 4 | import org.springframework.boot.autoconfigure.web.HttpMessageConverters; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Import; 9 | import org.springframework.http.converter.HttpMessageConverter; 10 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 11 | 12 | @Configuration 13 | @Import({AccountViewBackendConfiguration.class}) 14 | @ComponentScan 15 | public class AccountViewWebConfiguration { 16 | 17 | @Bean 18 | public HttpMessageConverters customConverters() { 19 | HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter(); 20 | return new HttpMessageConverters(additional); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /java-spring/api-gateway-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:openjdk-8u91-jdk 2 | CMD java ${JAVA_OPTS} -jar api-gateway-service.jar 3 | EXPOSE 8080 4 | COPY build/libs/api-gateway-service.jar . 5 | -------------------------------------------------------------------------------- /java-spring/api-gateway-service/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'spring-boot' 3 | 4 | dependencies { 5 | compile project(":common-auth-web") 6 | 7 | compile "org.apache.httpcomponents:httpclient:4.5" 8 | compile "org.apache.httpcomponents:fluent-hc:4.5.1" 9 | 10 | compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" 11 | compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" 12 | 13 | testCompile "junit:junit:4.11" 14 | } 15 | 16 | task copyWebStatic(type: Copy) { 17 | from "../../js-frontend/build" 18 | into "build/resources/main/static" 19 | } 20 | 21 | jar.dependsOn(copyWebStatic) -------------------------------------------------------------------------------- /java-spring/api-gateway-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/apigateway/ApiGatewayProperties.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.apigateway; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.web.bind.annotation.RequestMethod; 5 | 6 | import java.util.List; 7 | 8 | @ConfigurationProperties(prefix = "api.gateway") 9 | public class ApiGatewayProperties { 10 | 11 | private List<Endpoint> endpoints; 12 | 13 | public static class Endpoint { 14 | private String path; 15 | private RequestMethod method; 16 | private String location; 17 | 18 | public Endpoint() { 19 | } 20 | 21 | public Endpoint(String location) { 22 | this.location = location; 23 | } 24 | 25 | public String getPath() { 26 | return path; 27 | } 28 | 29 | public void setPath(String path) { 30 | this.path = path; 31 | } 32 | 33 | public RequestMethod getMethod() { 34 | return method; 35 | } 36 | 37 | public void setMethod(RequestMethod method) { 38 | this.method = method; 39 | } 40 | 41 | public String getLocation() { 42 | return location; 43 | } 44 | 45 | public void setLocation(String location) { 46 | this.location = location; 47 | } 48 | } 49 | 50 | public List<Endpoint> getEndpoints() { 51 | return endpoints; 52 | } 53 | 54 | public void setEndpoints(List<Endpoint> endpoints) { 55 | this.endpoints = endpoints; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /java-spring/api-gateway-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/apigateway/RestTemplateErrorHandler.java: -------------------------------------------------------------------------------- 1 | 2 | package net.chrisrichardson.eventstore.javaexamples.banking.apigateway; 3 | 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.http.client.ClientHttpResponse; 7 | import org.springframework.web.client.ResponseErrorHandler; 8 | 9 | import java.io.IOException; 10 | 11 | public class RestTemplateErrorHandler implements ResponseErrorHandler { 12 | 13 | private static final Logger log = LoggerFactory.getLogger(RestTemplateErrorHandler.class); 14 | 15 | @Override 16 | public void handleError(ClientHttpResponse response) throws IOException { 17 | log.error("Response error: {} {}", response.getStatusCode(), response.getStatusText()); 18 | } 19 | 20 | @Override 21 | public boolean hasError(ClientHttpResponse response) throws IOException { 22 | return RestUtil.isError(response.getStatusCode()); 23 | } 24 | } -------------------------------------------------------------------------------- /java-spring/api-gateway-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/apigateway/RestUtil.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.apigateway; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class RestUtil { 6 | 7 | public static boolean isError(HttpStatus status) { 8 | HttpStatus.Series series = status.series(); 9 | return (HttpStatus.Series.CLIENT_ERROR.equals(series) 10 | || HttpStatus.Series.SERVER_ERROR.equals(series)); 11 | } 12 | } -------------------------------------------------------------------------------- /java-spring/api-gateway-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/apigateway/main/ApiGatewayServiceMain.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.apigateway.main; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.apigateway.ApiGatewayServiceConfiguration; 4 | import org.springframework.boot.SpringApplication; 5 | 6 | /** 7 | * Created by Main on 19.01.2016. 8 | */ 9 | public class ApiGatewayServiceMain { 10 | public static void main(String[] args) { 11 | SpringApplication.run(ApiGatewayServiceConfiguration.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /java-spring/api-gateway-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/apigateway/utils/ContentRequestTransformer.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils; 2 | 3 | import org.apache.http.client.methods.RequestBuilder; 4 | import org.apache.http.entity.ContentType; 5 | import org.apache.http.entity.StringEntity; 6 | import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import java.io.IOException; 10 | import java.net.URISyntaxException; 11 | import java.util.stream.Collectors; 12 | 13 | public class ContentRequestTransformer extends ProxyRequestTransformer { 14 | 15 | @Override 16 | public RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException { 17 | RequestBuilder requestBuilder = predecessor.transform(request); 18 | 19 | String requestContent = request.getReader().lines().collect(Collectors.joining("")); 20 | if (!requestContent.isEmpty()) { 21 | StringEntity entity = new StringEntity(requestContent, ContentType.APPLICATION_JSON); 22 | requestBuilder.setEntity(entity); 23 | } 24 | 25 | return requestBuilder; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /java-spring/api-gateway-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/apigateway/utils/HeadersRequestTransformer.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils; 2 | 3 | import org.apache.http.client.methods.RequestBuilder; 4 | import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import java.io.IOException; 8 | import java.net.URISyntaxException; 9 | import java.util.Enumeration; 10 | 11 | public class HeadersRequestTransformer extends ProxyRequestTransformer { 12 | 13 | @Override 14 | public RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException { 15 | RequestBuilder requestBuilder = predecessor.transform(request); 16 | 17 | Enumeration<String> headerNames = request.getHeaderNames(); 18 | while (headerNames.hasMoreElements()) { 19 | String headerName = headerNames.nextElement(); 20 | String headerValue = request.getHeader(headerName); 21 | if (headerName.equals("x-access-token")) { 22 | requestBuilder.addHeader(headerName, headerValue); 23 | } 24 | } 25 | 26 | return requestBuilder; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /java-spring/api-gateway-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/apigateway/utils/ProxyRequestTransformer.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils; 2 | 3 | import org.apache.http.client.methods.RequestBuilder; 4 | import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import java.io.IOException; 8 | import java.net.URISyntaxException; 9 | 10 | public abstract class ProxyRequestTransformer { 11 | 12 | protected ProxyRequestTransformer predecessor; 13 | 14 | public abstract RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException; 15 | 16 | public void setPredecessor(ProxyRequestTransformer transformer) { 17 | this.predecessor = transformer; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /java-spring/api-gateway-service/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <configuration> 3 | 4 | <!-- [%thread] --> 5 | 6 | <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 7 | <layout class="ch.qos.logback.classic.PatternLayout"> 8 | <Pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</Pattern> 9 | </layout> 10 | </appender> 11 | 12 | <root level="error"> 13 | <appender-ref ref="STDOUT" /> 14 | </root> 15 | <logger name="org.springframework" level='info'> 16 | </logger> 17 | 18 | <logger name="net.chrisrichardson.eventstore.javaexamples.banking" level='info'> 19 | </logger> 20 | 21 | <logger name="io.eventuate.activity" level='debug'> 22 | </logger> 23 | 24 | </configuration> -------------------------------------------------------------------------------- /java-spring/backend-integration-tests/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: VerifyMongoDBConfigurationPlugin 2 | 3 | dependencies { 4 | 5 | testCompile project(":transactions-service") 6 | testCompile project(":accounts-service") 7 | testCompile project(":accounts-view-service") 8 | testCompile project(":customers-service") 9 | testCompile project(":customers-view-service") 10 | testCompile project(":testutil") 11 | testCompile "junit:junit:4.11" 12 | testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" 13 | testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" 14 | 15 | } 16 | -------------------------------------------------------------------------------- /java-spring/backend-integration-tests/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/BankingTestConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend; 2 | 3 | import io.eventuate.javaclient.spring.jdbc.EmbeddedTestAggregateStoreConfiguration; 4 | import net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend.AccountsBackendConfiguration; 5 | import net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend.MoneyTransferBackendConfiguration; 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Import; 9 | 10 | @Configuration 11 | @Import({AccountsBackendConfiguration.class, MoneyTransferBackendConfiguration.class, EmbeddedTestAggregateStoreConfiguration.class}) 12 | @EnableAutoConfiguration 13 | public class BankingTestConfiguration { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /java-spring/backend-integration-tests/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/queryside/accounts/AccountQuerySideTestConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts; 2 | 3 | import io.eventuate.javaclient.spring.jdbc.EmbeddedTestAggregateStoreConfiguration; 4 | import net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend.AccountsBackendConfiguration; 5 | import net.chrisrichardson.eventstore.javaexamples.banking.accountsviewservice.backend.AccountViewBackendConfiguration; 6 | import net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend.MoneyTransferBackendConfiguration; 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.Import; 10 | 11 | @Configuration 12 | @Import({AccountsBackendConfiguration.class, MoneyTransferBackendConfiguration.class, EmbeddedTestAggregateStoreConfiguration.class, 13 | AccountViewBackendConfiguration.class}) 14 | @EnableAutoConfiguration 15 | public class AccountQuerySideTestConfiguration { 16 | } 17 | -------------------------------------------------------------------------------- /java-spring/backend-integration-tests/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/queryside/customers/CustomerQuerySideTestConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers; 2 | 3 | import io.eventuate.javaclient.spring.jdbc.EmbeddedTestAggregateStoreConfiguration; 4 | import net.chrisrichardson.eventstore.javaexamples.banking.customersservice.backend.CustomerBackendConfiguration; 5 | import net.chrisrichardson.eventstore.javaexamples.banking.customersviewservice.backend.CustomerViewBackendConfiguration; 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Import; 9 | 10 | @Configuration 11 | @Import({CustomerBackendConfiguration.class, EmbeddedTestAggregateStoreConfiguration.class, CustomerViewBackendConfiguration.class}) 12 | @EnableAutoConfiguration 13 | public class CustomerQuerySideTestConfiguration { 14 | } 15 | -------------------------------------------------------------------------------- /java-spring/build-and-test-all-eventuate-local.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | export JAVA_OPTS="-Xmx128m -Xms128m" 4 | export EXTRA_INFRASTRUCTURE_SERVICES=cdcservice 5 | export EVENTUATE_LOCAL=yes 6 | ../_build-and-test-all.sh -f docker-compose-eventuate-local.yml $* -P eventuateDriver=local 7 | -------------------------------------------------------------------------------- /java-spring/build-and-test-all.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | export JAVA_OPTS="-Xmx128m -Xms128m" 4 | ../_build-and-test-all.sh $* 5 | -------------------------------------------------------------------------------- /java-spring/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | dependencies { 6 | classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion") 7 | } 8 | } 9 | 10 | allprojects { 11 | group = "net.chrisrichardson.eventstore" 12 | } 13 | 14 | 15 | task wrapper(type: Wrapper) { 16 | gradleVersion = '2.0' 17 | } 18 | 19 | subprojects { 20 | apply plugin: 'java' 21 | sourceCompatibility = 1.8 22 | targetCompatibility = 1.8 23 | 24 | repositories { 25 | mavenCentral() 26 | jcenter() 27 | eventuateMavenRepoUrl.split(',').each { repoUrl -> maven { url repoUrl } } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /java-spring/buildSrc/src/main/groovy/EventuateDependencyPlugin.groovy: -------------------------------------------------------------------------------- 1 | import org.gradle.api.Plugin 2 | import org.gradle.api.Project 3 | 4 | class EventuateDependencyPlugin implements Plugin<Project> { 5 | 6 | @Override 7 | void apply(Project project) { 8 | project.dependencies { 9 | if (project.hasProperty("eventuateDriver") && project.property("eventuateDriver").equals("local")) { 10 | compile "io.eventuate.local.java:eventuate-local-java-jdbc:${project.eventuateLocalVersion}" 11 | compile "io.eventuate.local.java:eventuate-local-java-embedded-cdc-autoconfigure:${project.eventuateLocalVersion}" 12 | } else 13 | compile "io.eventuate.client.java:eventuate-client-java-http-stomp-spring:${project.eventuateClientVersion}" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /java-spring/buildSrc/src/main/groovy/VerifyEventStoreEnvironmentPlugin.groovy: -------------------------------------------------------------------------------- 1 | import org.gradle.api.* 2 | 3 | 4 | class VerifyEventStoreEnvironmentPlugin implements Plugin<Project> { 5 | void apply(Project project) { 6 | project.test { 7 | beforeSuite { x -> 8 | if (x.parent == null) { 9 | if (System.getenv("EVENTUATE_API_KEY_ID") == null && System.getenv("EVENTUATE_API_KEY_SECRET") == null) 10 | logger.warn("\nPLEASE make sure that Eventuate-related environment variables EVENTUATE_API_KEY_ID and EVENTUATE_API_KEY_SECRET are set, see sample-set-remote-env.sh !!!!\n") 11 | } 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java-spring/buildSrc/src/main/groovy/VerifyMongoDBConfigurationPlugin.groovy: -------------------------------------------------------------------------------- 1 | import org.gradle.api.* 2 | 3 | 4 | class VerifyMongoDBConfigurationPlugin implements Plugin<Project> { 5 | void apply(Project project) { 6 | project.test { 7 | beforeSuite { x -> 8 | if (x.parent == null) { 9 | if (System.getenv("SPRING_DATA_MONGODB_URI") == null) 10 | throw new RuntimeException("Please make sure that the environment variable SPRING_DATA_MONGODB_URI is set, e.g. export SPRING_DATA_MONGODB_URI=mongodb://192.168.59.103/mydb") 11 | } 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /java-spring/common-auth-web/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile project(":common-auth") 5 | compile project(":common") 6 | 7 | compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" 8 | compile "org.springframework.boot:spring-boot-starter-security:$springBootVersion" 9 | 10 | testCompile "junit:junit:4.11" 11 | testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" 12 | } 13 | -------------------------------------------------------------------------------- /java-spring/common-auth-web/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/commonauth/model/AuthRequest.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model; 2 | 3 | import org.hibernate.validator.constraints.Email; 4 | import org.hibernate.validator.constraints.NotBlank; 5 | 6 | public class AuthRequest { 7 | 8 | @NotBlank 9 | @Email 10 | private String email; 11 | 12 | @NotBlank 13 | private String password; 14 | 15 | public AuthRequest() { 16 | } 17 | 18 | public AuthRequest(String email, String password) { 19 | this.email = email; 20 | this.password = password; 21 | } 22 | 23 | public String getEmail() { 24 | return email; 25 | } 26 | 27 | public void setEmail(String email) { 28 | this.email = email; 29 | } 30 | 31 | public String getPassword() { 32 | return password; 33 | } 34 | 35 | public void setPassword(String password) { 36 | this.password = password; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /java-spring/common-auth-web/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/commonauth/model/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model; 2 | 3 | /** 4 | * Created by Main on 17.02.2016. 5 | */ 6 | public class ErrorResponse { 7 | 8 | private String message; 9 | 10 | public ErrorResponse() { 11 | } 12 | 13 | public ErrorResponse(String message) { 14 | this.message = message; 15 | } 16 | 17 | public String getMessage() { 18 | return message; 19 | } 20 | 21 | public void setMessage(String message) { 22 | this.message = message; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java-spring/common-auth-web/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/commonauth/controller/AuthControllerIntegrationTestConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.controller; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.AuthConfiguration; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.Import; 7 | 8 | @Configuration 9 | @Import(AuthConfiguration.class) 10 | @EnableAutoConfiguration 11 | public class AuthControllerIntegrationTestConfiguration { 12 | } 13 | -------------------------------------------------------------------------------- /java-spring/common-auth/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile project(":common") 5 | compile project(":customers-query-side-common") 6 | 7 | compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" 8 | compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion" 9 | 10 | compile "org.springframework.security:spring-security-config:4.0.2.RELEASE" 11 | compile "org.springframework.security:spring-security-web:4.0.2.RELEASE" 12 | 13 | 14 | testCompile "junit:junit:4.11" 15 | } -------------------------------------------------------------------------------- /java-spring/common-auth/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/commonauth/AuthProperties.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.commonauth; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | @ConfigurationProperties(locations = "classpath:auth.properties", ignoreUnknownFields = false, prefix = "auth") 6 | public class AuthProperties { 7 | private String serverSecret; 8 | private Integer serverInteger; 9 | 10 | public String getServerSecret() { 11 | return serverSecret; 12 | } 13 | 14 | public void setServerSecret(String serverSecret) { 15 | this.serverSecret = serverSecret; 16 | } 17 | 18 | public Integer getServerInteger() { 19 | return serverInteger; 20 | } 21 | 22 | public void setServerInteger(Integer serverInteger) { 23 | this.serverInteger = serverInteger; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /java-spring/common-auth/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/commonauth/CustomerAuthRepository.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.commonauth; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.web.customers.queryside.common.QuerySideCustomer; 4 | import org.springframework.data.mongodb.repository.MongoRepository; 5 | 6 | import java.util.List; 7 | 8 | interface CustomerAuthRepository extends MongoRepository<QuerySideCustomer, String> { 9 | 10 | List<QuerySideCustomer> findByEmail(String email); 11 | 12 | List<QuerySideCustomer> findByEmailAndPassword(String email, String password); 13 | } -------------------------------------------------------------------------------- /java-spring/common-auth/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/commonauth/CustomerAuthService.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.commonauth; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.web.customers.queryside.common.QuerySideCustomer; 4 | import org.springframework.dao.EmptyResultDataAccessException; 5 | import org.springframework.dao.support.DataAccessUtils; 6 | 7 | /** 8 | * Created by Main on 15.02.2016. 9 | */ 10 | public class CustomerAuthService { 11 | private CustomerAuthRepository customerAuthRepository; 12 | 13 | public CustomerAuthService(CustomerAuthRepository customerAuthRepository) { 14 | this.customerAuthRepository = customerAuthRepository; 15 | } 16 | 17 | public QuerySideCustomer findByEmail(String email) { 18 | QuerySideCustomer result = DataAccessUtils.uniqueResult(customerAuthRepository.findByEmail(email)); 19 | if (result==null) 20 | throw new EmptyResultDataAccessException(1); 21 | 22 | return result; 23 | } 24 | 25 | public QuerySideCustomer findByEmailAndPassword(String email, String password) { 26 | QuerySideCustomer result = DataAccessUtils.uniqueResult(customerAuthRepository.findByEmailAndPassword(email, password)); 27 | if (result==null) 28 | throw new EmptyResultDataAccessException(1); 29 | 30 | return result; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /java-spring/common-auth/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/commonauth/filter/StatelessAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.filter; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.TokenAuthenticationService; 4 | import org.springframework.security.core.context.SecurityContextHolder; 5 | import org.springframework.web.filter.GenericFilterBean; 6 | 7 | import javax.servlet.FilterChain; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | import javax.servlet.http.HttpServletRequest; 12 | import java.io.IOException; 13 | 14 | public class StatelessAuthenticationFilter extends GenericFilterBean { 15 | 16 | private final TokenAuthenticationService tokenAuthenticationService; 17 | 18 | public StatelessAuthenticationFilter(TokenAuthenticationService taService) { 19 | this.tokenAuthenticationService = taService; 20 | } 21 | 22 | @Override 23 | public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 24 | if (SecurityContextHolder.getContext().getAuthentication() == null) { 25 | SecurityContextHolder.getContext().setAuthentication( 26 | tokenAuthenticationService.getAuthentication((HttpServletRequest) req)); 27 | } 28 | chain.doFilter(req, res); 29 | } 30 | } -------------------------------------------------------------------------------- /java-spring/common-auth/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/commonauth/model/UserAuthentication.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model; 2 | 3 | import org.springframework.security.core.Authentication; 4 | import org.springframework.security.core.GrantedAuthority; 5 | 6 | import java.util.Collection; 7 | 8 | public class UserAuthentication implements Authentication { 9 | 10 | private final User user; 11 | private boolean authenticated = true; 12 | 13 | public UserAuthentication(User user) { 14 | this.user = user; 15 | } 16 | 17 | @Override 18 | public String getName() { 19 | return user.getUsername(); 20 | } 21 | 22 | @Override 23 | public Collection<? extends GrantedAuthority> getAuthorities() { 24 | return user.getAuthorities(); 25 | } 26 | 27 | @Override 28 | public Object getCredentials() { 29 | return user.getPassword(); 30 | } 31 | 32 | @Override 33 | public User getDetails() { 34 | return user; 35 | } 36 | 37 | @Override 38 | public Object getPrincipal() { 39 | return user.getUsername(); 40 | } 41 | 42 | @Override 43 | public boolean isAuthenticated() { 44 | return authenticated; 45 | } 46 | 47 | @Override 48 | public void setAuthenticated(boolean authenticated) { 49 | this.authenticated = authenticated; 50 | } 51 | } -------------------------------------------------------------------------------- /java-spring/common-auth/src/main/resources/auth.properties: -------------------------------------------------------------------------------- 1 | auth.serverSecret=the_cake_is_a_lie 2 | auth.serverInteger=1 -------------------------------------------------------------------------------- /java-spring/common-backend/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile project(":common") 5 | 6 | compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion" 7 | 8 | testCompile "junit:junit:4.11" 9 | testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" 10 | testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/accounts/AccountChangedEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class AccountChangedEvent extends AccountEvent { 6 | protected BigDecimal amount; 7 | protected String transactionId; 8 | 9 | public AccountChangedEvent(BigDecimal amount, String transactionId) { 10 | this.amount = amount; 11 | this.transactionId = transactionId; 12 | } 13 | 14 | public AccountChangedEvent() { 15 | } 16 | 17 | public String getTransactionId() { 18 | return transactionId; 19 | } 20 | 21 | public BigDecimal getAmount() { 22 | return amount; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/accounts/AccountCreditedEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class AccountCreditedEvent extends AccountChangedEvent { 6 | 7 | private AccountCreditedEvent() { 8 | } 9 | 10 | public AccountCreditedEvent(BigDecimal amount, String transactionId) { 11 | super(amount, transactionId); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/accounts/AccountDebitFailedDueToInsufficientFundsEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts; 2 | 3 | public class AccountDebitFailedDueToInsufficientFundsEvent extends AccountEvent { 4 | private String transactionId; 5 | 6 | private AccountDebitFailedDueToInsufficientFundsEvent() { 7 | } 8 | 9 | public AccountDebitFailedDueToInsufficientFundsEvent(String transactionId) { 10 | this.transactionId = transactionId; 11 | } 12 | 13 | public String getTransactionId() { 14 | return transactionId; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/accounts/AccountDebitedEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class AccountDebitedEvent extends AccountChangedEvent { 6 | 7 | private AccountDebitedEvent() { 8 | } 9 | 10 | public AccountDebitedEvent(BigDecimal amount, String transactionId) { 11 | super(amount, transactionId); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/accounts/AccountDeletedEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts; 2 | 3 | public class AccountDeletedEvent extends AccountEvent { 4 | } 5 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/accounts/AccountEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts; 2 | 3 | import io.eventuate.Event; 4 | import io.eventuate.EventEntity; 5 | 6 | @EventEntity(entity="net.chrisrichardson.eventstore.javaexamples.banking.accountsservice.backend.Account") 7 | public abstract class AccountEvent implements Event{ 8 | } 9 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/accounts/AccountOpenedEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class AccountOpenedEvent extends AccountEvent { 6 | 7 | private String customerId; 8 | private String title; 9 | private BigDecimal initialBalance; 10 | private String description; 11 | 12 | private AccountOpenedEvent() { 13 | } 14 | 15 | public AccountOpenedEvent(String customerId, String title, BigDecimal initialBalance, String description) { 16 | this.customerId = customerId; 17 | this.title = title; 18 | this.initialBalance = initialBalance; 19 | this.description = description; 20 | } 21 | 22 | public String getCustomerId() { 23 | return customerId; 24 | } 25 | 26 | public String getTitle() { 27 | return title; 28 | } 29 | 30 | public BigDecimal getInitialBalance() { 31 | return initialBalance; 32 | } 33 | 34 | public String getDescription() { 35 | return description; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/customers/CustomerAddedToAccount.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo; 4 | 5 | /** 6 | * Created by Main on 08.02.2016. 7 | */ 8 | public class CustomerAddedToAccount extends CustomerEvent { 9 | 10 | private ToAccountInfo toAccountInfo; 11 | 12 | public CustomerAddedToAccount() { 13 | } 14 | 15 | public CustomerAddedToAccount(ToAccountInfo toAccountInfo) { 16 | this.toAccountInfo = toAccountInfo; 17 | } 18 | 19 | public ToAccountInfo getToAccountInfo() { 20 | return toAccountInfo; 21 | } 22 | 23 | public void setToAccountInfo(ToAccountInfo toAccountInfo) { 24 | this.toAccountInfo = toAccountInfo; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/customers/CustomerCreatedEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo; 4 | 5 | public class CustomerCreatedEvent extends CustomerEvent { 6 | 7 | private CustomerInfo customerInfo; 8 | 9 | public CustomerCreatedEvent() { 10 | } 11 | 12 | public CustomerCreatedEvent(CustomerInfo customerInfo) { 13 | this.customerInfo = customerInfo; 14 | } 15 | 16 | public CustomerInfo getCustomerInfo() { 17 | return customerInfo; 18 | } 19 | 20 | public void setCustomerInfo(CustomerInfo customerInfo) { 21 | this.customerInfo = customerInfo; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/customers/CustomerEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers; 2 | 3 | 4 | import io.eventuate.Event; 5 | import io.eventuate.EventEntity; 6 | 7 | /** 8 | * Created by Main on 11.02.2016. 9 | */ 10 | @EventEntity(entity = "net.chrisrichardson.eventstore.javaexamples.banking.customersservice.backend.Customer") 11 | public abstract class CustomerEvent implements Event { 12 | } -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/customers/CustomerToAccountDeleted.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers; 2 | 3 | public class CustomerToAccountDeleted extends CustomerEvent { 4 | 5 | private String accountId; 6 | 7 | public CustomerToAccountDeleted() { 8 | } 9 | 10 | public CustomerToAccountDeleted(String accountId) { 11 | this.accountId = accountId; 12 | } 13 | 14 | public String getAccountId() { 15 | return accountId; 16 | } 17 | 18 | public void setAccountId(String accountId) { 19 | this.accountId = accountId; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/transactions/CreditRecordedEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions; 2 | 3 | 4 | public class CreditRecordedEvent extends MoneyTransferEvent { 5 | private TransferDetails details; 6 | 7 | private CreditRecordedEvent() { 8 | } 9 | 10 | public CreditRecordedEvent(TransferDetails details) { 11 | this.details = details; 12 | } 13 | 14 | public TransferDetails getDetails() { 15 | return details; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/transactions/DebitRecordedEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions; 2 | 3 | public class DebitRecordedEvent extends MoneyTransferEvent { 4 | private TransferDetails details; 5 | 6 | private DebitRecordedEvent() { 7 | } 8 | 9 | public DebitRecordedEvent(TransferDetails details) { 10 | this.details = details; 11 | } 12 | 13 | public TransferDetails getDetails() { 14 | return details; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/transactions/FailedDebitRecordedEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions; 2 | 3 | 4 | public class FailedDebitRecordedEvent extends MoneyTransferEvent { 5 | private TransferDetails details; 6 | 7 | private FailedDebitRecordedEvent() { 8 | } 9 | 10 | public FailedDebitRecordedEvent(TransferDetails details) { 11 | this.details = details; 12 | } 13 | 14 | public TransferDetails getDetails() { 15 | return details; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/transactions/MoneyTransferCreatedEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions; 2 | 3 | 4 | public class MoneyTransferCreatedEvent extends MoneyTransferEvent { 5 | private TransferDetails details; 6 | 7 | private MoneyTransferCreatedEvent() { 8 | } 9 | 10 | public MoneyTransferCreatedEvent(TransferDetails details) { 11 | this.details = details; 12 | } 13 | 14 | public TransferDetails getDetails() { 15 | return details; 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/backend/common/transactions/MoneyTransferEvent.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions; 2 | 3 | import io.eventuate.Event; 4 | import io.eventuate.EventEntity; 5 | 6 | @EventEntity(entity="net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend.MoneyTransfer") 7 | public abstract class MoneyTransferEvent implements Event { 8 | } 9 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <configuration> 3 | 4 | <!-- [%thread] --> 5 | 6 | <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 7 | <layout class="ch.qos.logback.classic.PatternLayout"> 8 | <Pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</Pattern> 9 | </layout> 10 | </appender> 11 | 12 | <root level="error"> 13 | <appender-ref ref="STDOUT" /> 14 | </root> 15 | 16 | <logger name="org.springframework" level='info'> 17 | </logger> 18 | 19 | <logger name="io.eventuate.activity" level='debug'> 20 | </logger> 21 | 22 | </configuration> 23 | -------------------------------------------------------------------------------- /java-spring/common-backend/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/common/accounts/AccountOpenEventSerializationTest.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts; 2 | 3 | import io.eventuate.javaclient.commonimpl.JSonMapper; 4 | import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountOpenedEvent; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | import java.math.BigDecimal; 9 | 10 | public class AccountOpenEventSerializationTest { 11 | 12 | @Test 13 | public void shouldSerde() { 14 | 15 | AccountOpenedEvent event = new AccountOpenedEvent("00000000-00000000", "My Account", new BigDecimal(55), ""); 16 | String json = JSonMapper.toJson(event); 17 | System.out.println("json=" + json); 18 | 19 | AccountOpenedEvent event2 = JSonMapper.fromJson(json, AccountOpenedEvent.class); 20 | 21 | Assert.assertEquals(event.getInitialBalance(), event2.getInitialBalance()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /java-spring/common-swagger/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion" 3 | 4 | compile "io.springfox:springfox-swagger2:2.2.2" 5 | compile 'io.springfox:springfox-swagger-ui:2.2.2' 6 | 7 | } -------------------------------------------------------------------------------- /java-spring/common/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile "commons-lang:commons-lang:2.6" 5 | compile "com.fasterxml.jackson.core:jackson-databind:2.6.6" 6 | compile "org.springframework.boot:spring-boot-starter-validation:$springBootVersion" 7 | 8 | testCompile group: 'junit', name: 'junit', version: '4.11' 9 | } 10 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/accounts/AccountHistoryEntry.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts; 2 | 3 | import com.fasterxml.jackson.annotation.JsonSubTypes; 4 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 5 | 6 | import java.util.Date; 7 | 8 | @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, 9 | include = JsonTypeInfo.As.PROPERTY, 10 | property = "entryType") 11 | @JsonSubTypes({ 12 | @JsonSubTypes.Type(value = AccountTransactionInfo.class, name = "transaction"), 13 | @JsonSubTypes.Type(value = AccountOpenInfo.class, name = "account") 14 | }) 15 | public class AccountHistoryEntry { 16 | 17 | protected Date date; 18 | protected EntryType entryType; 19 | 20 | public AccountHistoryEntry() { 21 | } 22 | 23 | public AccountHistoryEntry(Date date, EntryType entryType) { 24 | this.date = date; 25 | this.entryType = entryType; 26 | } 27 | 28 | public Date getDate() { 29 | return date; 30 | } 31 | 32 | public void setDate(Date date) { 33 | this.date = date; 34 | } 35 | 36 | public EntryType getEntryType() { 37 | return entryType; 38 | } 39 | 40 | public void setEntryType(EntryType entryType) { 41 | this.entryType = entryType; 42 | } 43 | 44 | public enum EntryType { 45 | transaction, account 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/accounts/AccountHistoryResponse.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts; 2 | 3 | import java.util.List; 4 | 5 | public class AccountHistoryResponse { 6 | private List<AccountHistoryEntry> transactionsHistory; 7 | 8 | public AccountHistoryResponse() { 9 | } 10 | 11 | public AccountHistoryResponse(List<AccountHistoryEntry> transactionsHistory) { 12 | 13 | this.transactionsHistory = transactionsHistory; 14 | } 15 | 16 | public List<AccountHistoryEntry> getTransactionsHistory() { 17 | return transactionsHistory; 18 | } 19 | 20 | public void setTransactionsHistory(List<AccountHistoryEntry> transactionsHistory) { 21 | this.transactionsHistory = transactionsHistory; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/accounts/AccountOpenInfo.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts; 2 | 3 | import java.util.Date; 4 | 5 | public class AccountOpenInfo extends AccountHistoryEntry { 6 | 7 | private long initialBalance; 8 | 9 | public AccountOpenInfo() { 10 | } 11 | 12 | public AccountOpenInfo(Date date, EntryType entryType, long initialBalance) { 13 | super(date, entryType); 14 | this.initialBalance=initialBalance; 15 | } 16 | 17 | public long getInitialBalance() { 18 | return initialBalance; 19 | } 20 | 21 | public void setInitialBalance(long initialBalance) { 22 | this.initialBalance = initialBalance; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/accounts/CreateAccountResponse.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts; 2 | 3 | 4 | public class CreateAccountResponse { 5 | 6 | private String accountId; 7 | 8 | public CreateAccountResponse() { 9 | } 10 | 11 | public CreateAccountResponse(String accountId) { 12 | this.accountId = accountId; 13 | } 14 | 15 | public String getAccountId() { 16 | return accountId; 17 | } 18 | 19 | public void setAccountId(String accountId) { 20 | this.accountId = accountId; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/accounts/DeleteAccountResponse.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts; 2 | 3 | 4 | public class DeleteAccountResponse { 5 | 6 | private String accountId; 7 | 8 | public DeleteAccountResponse() { 9 | } 10 | 11 | public DeleteAccountResponse(String accountId) { 12 | this.accountId = accountId; 13 | } 14 | 15 | public String getAccountId() { 16 | return accountId; 17 | } 18 | 19 | public void setAccountId(String accountId) { 20 | this.accountId = accountId; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/accounts/GetAccountResponse.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts; 2 | 3 | import java.math.BigDecimal; 4 | 5 | 6 | public class GetAccountResponse { 7 | private String accountId; 8 | private BigDecimal balance; 9 | private String title; 10 | private String description; 11 | 12 | public GetAccountResponse() { 13 | } 14 | 15 | public GetAccountResponse(String accountId, BigDecimal balance, String title, String description) { 16 | this.accountId = accountId; 17 | this.balance = balance; 18 | this.title = title; 19 | this.description = description; 20 | } 21 | 22 | public void setBalance(BigDecimal balance) { 23 | this.balance = balance; 24 | } 25 | 26 | public void setAccountId(String accountId) { 27 | this.accountId = accountId; 28 | } 29 | 30 | public String getAccountId() { 31 | return accountId; 32 | } 33 | 34 | public BigDecimal getBalance() { 35 | return balance; 36 | } 37 | 38 | public String getTitle() { 39 | return title; 40 | } 41 | 42 | public void setTitle(String title) { 43 | this.title = title; 44 | } 45 | 46 | public String getDescription() { 47 | return description; 48 | } 49 | 50 | public void setDescription(String description) { 51 | this.description = description; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/accounts/GetAccountsResponse.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts; 2 | 3 | import java.util.List; 4 | 5 | public class GetAccountsResponse { 6 | private List<GetAccountResponse> accounts; 7 | 8 | public GetAccountsResponse() { 9 | } 10 | 11 | public GetAccountsResponse(List<GetAccountResponse> accounts) { 12 | this.accounts = accounts; 13 | } 14 | 15 | public List<GetAccountResponse> getAccounts() { 16 | return accounts; 17 | } 18 | 19 | public void setAccounts(List<GetAccountResponse> accounts) { 20 | this.accounts = accounts; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/customers/AddToAccountResponse.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.customers; 2 | 3 | public class AddToAccountResponse { 4 | 5 | private String version; 6 | 7 | public AddToAccountResponse() { 8 | } 9 | 10 | public AddToAccountResponse(String version) { 11 | this.version = version; 12 | } 13 | 14 | public String getVersion() { 15 | return version; 16 | } 17 | 18 | public void setVersion(String version) { 19 | this.version = version; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/customers/CustomerResponse.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.customers; 2 | 3 | import org.apache.commons.lang.builder.EqualsBuilder; 4 | import org.apache.commons.lang.builder.HashCodeBuilder; 5 | 6 | public class CustomerResponse { 7 | 8 | private String id; 9 | private CustomerInfo customerInfo; 10 | 11 | public CustomerResponse() { 12 | } 13 | 14 | public CustomerResponse(String id, CustomerInfo customerInfo) { 15 | this.id = id; 16 | this.customerInfo = customerInfo; 17 | } 18 | 19 | public String getId() { 20 | return id; 21 | } 22 | 23 | public void setId(String id) { 24 | this.id = id; 25 | } 26 | 27 | public CustomerInfo getCustomerInfo() { 28 | return customerInfo; 29 | } 30 | 31 | public void setCustomerInfo(CustomerInfo customerInfo) { 32 | this.customerInfo = customerInfo; 33 | } 34 | 35 | @Override 36 | public boolean equals(Object o) { 37 | return EqualsBuilder.reflectionEquals(this, o); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return HashCodeBuilder.reflectionHashCode(this); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/customers/Name.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.customers; 2 | 3 | import org.apache.commons.lang.builder.EqualsBuilder; 4 | import org.apache.commons.lang.builder.HashCodeBuilder; 5 | 6 | import javax.validation.constraints.NotNull; 7 | 8 | /** 9 | * Created by Main on 10.02.2016. 10 | */ 11 | public class Name { 12 | @NotNull 13 | private String firstName; 14 | @NotNull 15 | private String lastName; 16 | 17 | public Name() { 18 | } 19 | 20 | public Name(String firstName, String lastName) { 21 | this.firstName = firstName; 22 | this.lastName = lastName; 23 | } 24 | 25 | public String getFirstName() { 26 | return firstName; 27 | } 28 | 29 | public void setFirstName(String firstName) { 30 | this.firstName = firstName; 31 | } 32 | 33 | public String getLastName() { 34 | return lastName; 35 | } 36 | 37 | public void setLastName(String lastName) { 38 | this.lastName = lastName; 39 | } 40 | 41 | @Override 42 | public boolean equals(Object o) { 43 | return EqualsBuilder.reflectionEquals(this, o); 44 | } 45 | 46 | @Override 47 | public int hashCode() { 48 | return HashCodeBuilder.reflectionHashCode(this); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/customers/UserCredentials.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.customers; 2 | 3 | import org.apache.commons.lang.builder.EqualsBuilder; 4 | import org.apache.commons.lang.builder.HashCodeBuilder; 5 | import org.hibernate.validator.constraints.Email; 6 | 7 | import javax.validation.constraints.NotNull; 8 | 9 | public class UserCredentials { 10 | @NotNull 11 | @Email 12 | private String email; 13 | @NotNull 14 | private String password; 15 | 16 | public UserCredentials() { 17 | } 18 | 19 | public UserCredentials(String email, String password) { 20 | this.email = email; 21 | this.password = password; 22 | } 23 | 24 | public String getEmail() { 25 | return email; 26 | } 27 | 28 | public void setEmail(String email) { 29 | this.email = email; 30 | } 31 | 32 | public String getPassword() { 33 | return password; 34 | } 35 | 36 | public void setPassword(String password) { 37 | this.password = password; 38 | } 39 | 40 | @Override 41 | public boolean equals(Object o) { 42 | return EqualsBuilder.reflectionEquals(this, o); 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | return HashCodeBuilder.reflectionHashCode(this); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/transactions/CreateMoneyTransferResponse.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.transactions; 2 | 3 | 4 | public class CreateMoneyTransferResponse { 5 | private String moneyTransferId; 6 | 7 | public CreateMoneyTransferResponse() { 8 | } 9 | 10 | public CreateMoneyTransferResponse(String moneyTransferId) { 11 | 12 | this.moneyTransferId = moneyTransferId; 13 | } 14 | 15 | public String getMoneyTransferId() { 16 | return moneyTransferId; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /java-spring/common/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/common/transactions/TransferState.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.common.transactions; 2 | 3 | public enum TransferState { 4 | NEW, INITIAL, DEBITED, COMPLETED, FAILED_DUE_TO_INSUFFICIENT_FUNDS 5 | } 6 | -------------------------------------------------------------------------------- /java-spring/customers-query-side-common/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile project(":common") 5 | 6 | compile "commons-lang:commons-lang:2.6" 7 | compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion" 8 | 9 | testCompile group: 'junit', name: 'junit', version: '4.11' 10 | } 11 | -------------------------------------------------------------------------------- /java-spring/customers-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:openjdk-8u91-jdk 2 | CMD java ${JAVA_OPTS} -jar customers-service.jar 3 | EXPOSE 8080 4 | COPY build/libs/customers-service.jar . 5 | -------------------------------------------------------------------------------- /java-spring/customers-service/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: VerifyMongoDBConfigurationPlugin 2 | apply plugin: VerifyEventStoreEnvironmentPlugin 3 | 4 | apply plugin: 'spring-boot' 5 | apply plugin: EventuateDependencyPlugin 6 | 7 | dependencies { 8 | compile project(":common") 9 | compile project(":common-backend") 10 | compile project(":common-swagger") 11 | 12 | compile "org.springframework.boot:spring-boot-starter-web" 13 | compile "org.springframework.boot:spring-boot-starter-actuator" 14 | 15 | testCompile project(":testutil") 16 | testCompile "junit:junit:4.11" 17 | testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" 18 | testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" 19 | } -------------------------------------------------------------------------------- /java-spring/customers-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersservice/CustomersServiceMain.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersservice; 2 | 3 | import io.eventuate.javaclient.driver.EventuateDriverConfiguration; 4 | import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration; 5 | import net.chrisrichardson.eventstore.javaexamples.banking.customersservice.web.CustomersWebConfiguration; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 8 | import org.springframework.boot.autoconfigure.web.HttpMessageConverters; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Import; 12 | import org.springframework.http.converter.HttpMessageConverter; 13 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 14 | 15 | @Configuration 16 | @Import({CustomersWebConfiguration.class, EventuateDriverConfiguration.class, CommonSwaggerConfiguration.class}) 17 | @EnableAutoConfiguration 18 | public class CustomersServiceMain { 19 | 20 | public static void main(String[] args) { 21 | SpringApplication.run(CustomersServiceMain.class, args); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /java-spring/customers-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersservice/backend/AddToAccountCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersservice.backend; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo; 4 | 5 | /** 6 | * Created by Main on 08.02.2016. 7 | */ 8 | public class AddToAccountCommand implements CustomerCommand { 9 | 10 | private ToAccountInfo toAccountInfo; 11 | 12 | public AddToAccountCommand(ToAccountInfo toAccountInfo) { 13 | this.toAccountInfo = toAccountInfo; 14 | } 15 | 16 | public ToAccountInfo getToAccountInfo() { 17 | return toAccountInfo; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /java-spring/customers-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersservice/backend/CreateCustomerCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersservice.backend; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo; 4 | 5 | public class CreateCustomerCommand implements CustomerCommand { 6 | private CustomerInfo customerInfo; 7 | 8 | public CreateCustomerCommand(CustomerInfo customerInfo) { 9 | this.customerInfo = customerInfo; 10 | } 11 | 12 | public CustomerInfo getCustomerInfo() { 13 | return customerInfo; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java-spring/customers-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersservice/backend/CustomerBackendConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersservice.backend; 2 | 3 | import io.eventuate.AggregateRepository; 4 | import io.eventuate.EventuateAggregateStore; 5 | import io.eventuate.javaclient.spring.EnableEventHandlers; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | @EnableEventHandlers 12 | @ComponentScan 13 | public class CustomerBackendConfiguration { 14 | 15 | @Bean 16 | public CustomerService customerService(AggregateRepository<Customer, CustomerCommand> customerRepository) { 17 | return new CustomerService(customerRepository); 18 | } 19 | 20 | @Bean 21 | public AggregateRepository<Customer, CustomerCommand> customerRepository(EventuateAggregateStore eventStore) { 22 | return new AggregateRepository<Customer, CustomerCommand>(Customer.class, eventStore); 23 | } 24 | 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /java-spring/customers-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersservice/backend/CustomerCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersservice.backend; 2 | 3 | import io.eventuate.Command; 4 | 5 | interface CustomerCommand extends Command { 6 | } 7 | -------------------------------------------------------------------------------- /java-spring/customers-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersservice/backend/CustomerService.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersservice.backend; 2 | 3 | 4 | import io.eventuate.AggregateRepository; 5 | import io.eventuate.EntityWithIdAndVersion; 6 | import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo; 7 | import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo; 8 | 9 | import java.util.concurrent.CompletableFuture; 10 | 11 | public class CustomerService { 12 | 13 | private final AggregateRepository<Customer, CustomerCommand> accountRepository; 14 | 15 | public CustomerService(AggregateRepository<Customer, CustomerCommand> accountRepository) { 16 | this.accountRepository = accountRepository; 17 | } 18 | 19 | public CompletableFuture<EntityWithIdAndVersion<Customer>> createCustomer(CustomerInfo customerInfo) { 20 | return accountRepository.save(new CreateCustomerCommand(customerInfo)); 21 | } 22 | 23 | public CompletableFuture<EntityWithIdAndVersion<Customer>> addToAccount(String customerId, ToAccountInfo toAccountInfo) { 24 | return accountRepository.update(customerId, new AddToAccountCommand(toAccountInfo)); 25 | } 26 | 27 | public CompletableFuture<EntityWithIdAndVersion<Customer>> deleteToAccount(String customerId, String accountId) { 28 | return accountRepository.update(customerId, new DeleteToAccountCommand(accountId)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /java-spring/customers-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersservice/backend/DeleteToAccountCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersservice.backend; 2 | 3 | public class DeleteToAccountCommand implements CustomerCommand { 4 | 5 | private String accountId; 6 | 7 | public DeleteToAccountCommand() { 8 | } 9 | 10 | public DeleteToAccountCommand(String accountId) { 11 | this.accountId = accountId; 12 | } 13 | 14 | public String getAccountId() { 15 | return accountId; 16 | } 17 | 18 | public void setAccountId(String accountId) { 19 | this.accountId = accountId; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /java-spring/customers-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersservice/web/CustomersWebConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersservice.web; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.customersservice.backend.CustomerBackendConfiguration; 4 | import org.springframework.boot.autoconfigure.web.HttpMessageConverters; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Import; 9 | import org.springframework.http.converter.HttpMessageConverter; 10 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 12 | 13 | @Configuration 14 | @Import({CustomerBackendConfiguration.class}) 15 | @ComponentScan 16 | public class CustomersWebConfiguration extends WebMvcConfigurerAdapter { 17 | 18 | @Bean 19 | public HttpMessageConverters customConverters() { 20 | HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter(); 21 | return new HttpMessageConverters(additional); 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /java-spring/customers-service/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/customersservice/backend/CustomerEventTest.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersservice.backend; 2 | 3 | 4 | import net.chrisrichardson.eventstorestore.javaexamples.testutil.AbstractEntityEventTest; 5 | 6 | public class CustomerEventTest extends AbstractEntityEventTest { 7 | 8 | @Override 9 | protected Class<Customer> entityClass() { 10 | return Customer.class; 11 | } 12 | } -------------------------------------------------------------------------------- /java-spring/customers-service/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/customersservice/backend/CustomerTest.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersservice.backend; 2 | 3 | import io.eventuate.Event; 4 | import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers.CustomerCreatedEvent; 5 | import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import java.util.List; 10 | 11 | import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateCustomerInfo; 12 | 13 | public class CustomerTest { 14 | 15 | @Test 16 | public void testSomething() { 17 | Customer customer = new Customer(); 18 | 19 | CustomerInfo customerInfo = generateCustomerInfo(); 20 | 21 | List<Event> events = customer.process(new CreateCustomerCommand(customerInfo)); 22 | 23 | Assert.assertEquals(1, events.size()); 24 | Assert.assertEquals(CustomerCreatedEvent.class, events.get(0).getClass()); 25 | 26 | customer.applyEvent(events.get(0)); 27 | Assert.assertEquals(customerInfo, customer.getCustomerInfo()); 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /java-spring/customers-view-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:openjdk-8u91-jdk 2 | CMD java ${JAVA_OPTS} -jar customers-view-service.jar 3 | EXPOSE 8080 4 | COPY build/libs/customers-view-service.jar . -------------------------------------------------------------------------------- /java-spring/customers-view-service/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: VerifyMongoDBConfigurationPlugin 2 | apply plugin: VerifyEventStoreEnvironmentPlugin 3 | apply plugin: EventuateDependencyPlugin 4 | 5 | apply plugin: 'spring-boot' 6 | 7 | dependencies { 8 | compile project(":common-backend") 9 | compile project(":customers-query-side-common") 10 | compile project(":common-swagger") 11 | 12 | compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" 13 | compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" 14 | compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion" 15 | 16 | compile 'com.fasterxml.jackson.core:jackson-core:2.4.3' 17 | compile 'com.fasterxml.jackson.core:jackson-databind:2.4.3' 18 | compile 'com.fasterxml.jackson.module:jackson-module-scala_2.10:2.4.3' 19 | 20 | testCompile project(":testutil") 21 | testCompile project(":customers-service") 22 | testCompile "junit:junit:4.11" 23 | testCompile "org.springframework.boot:spring-boot-starter-test" 24 | testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" 25 | } 26 | 27 | test { 28 | ignoreFailures System.getenv("EVENTUATE_API_KEY_ID") == null 29 | } 30 | 31 | -------------------------------------------------------------------------------- /java-spring/customers-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersviewservice/CustomersViewServiceMain.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersviewservice; 2 | 3 | import io.eventuate.javaclient.driver.EventuateDriverConfiguration; 4 | import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration; 5 | import net.chrisrichardson.eventstore.javaexamples.banking.customersviewservice.web.CustomersViewWebConfiguration; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 8 | import org.springframework.boot.autoconfigure.web.HttpMessageConverters; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Import; 12 | import org.springframework.http.converter.HttpMessageConverter; 13 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 14 | 15 | @Configuration 16 | @Import({CustomersViewWebConfiguration.class, 17 | EventuateDriverConfiguration.class, 18 | CommonSwaggerConfiguration.class}) 19 | @EnableAutoConfiguration 20 | public class CustomersViewServiceMain { 21 | 22 | public static void main(String[] args) { 23 | SpringApplication.run(CustomersViewServiceMain.class, args); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /java-spring/customers-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersviewservice/backend/CustomerViewRepository.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersviewservice.backend; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.web.customers.queryside.common.QuerySideCustomer; 4 | import org.springframework.data.mongodb.repository.MongoRepository; 5 | 6 | import java.util.List; 7 | 8 | interface CustomerViewRepository extends MongoRepository<QuerySideCustomer, String> { 9 | 10 | List<QuerySideCustomer> findByEmailLike(String email); 11 | } 12 | -------------------------------------------------------------------------------- /java-spring/customers-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersviewservice/backend/ViewDependencyChecker.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersviewservice.backend; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.data.mongodb.core.MongoTemplate; 6 | 7 | import javax.annotation.PostConstruct; 8 | 9 | public class ViewDependencyChecker { 10 | private Logger logger = LoggerFactory.getLogger(getClass()); 11 | private MongoTemplate mongoTemplate; 12 | 13 | public ViewDependencyChecker(MongoTemplate mongoTemplate) { 14 | this.mongoTemplate = mongoTemplate; 15 | } 16 | 17 | @PostConstruct 18 | public void checkDependencies() { 19 | try { 20 | logger.info("Checking mongodb connectivity {}", System.getenv("SPRING_DATA_MONGODB_URI")); 21 | 22 | mongoTemplate.getDb().getCollectionNames(); 23 | 24 | } catch (Throwable e) { 25 | throw new RuntimeException("Error connecting to Mongo - have you set SPRING_DATA_MONGODB_URI or --spring.data.mongodb_uri?", e); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /java-spring/customers-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersviewservice/web/CustomersQueryResponse.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersviewservice.web; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.web.customers.queryside.common.QuerySideCustomer; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by Main on 05.02.2016. 9 | */ 10 | public class CustomersQueryResponse { 11 | 12 | private List<QuerySideCustomer> customers; 13 | 14 | public CustomersQueryResponse() { 15 | } 16 | 17 | public CustomersQueryResponse(List<QuerySideCustomer> customers) { 18 | this.customers = customers; 19 | } 20 | 21 | public List<QuerySideCustomer> getCustomers() { 22 | return customers; 23 | } 24 | 25 | public void setCustomers(List<QuerySideCustomer> customers) { 26 | this.customers = customers; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /java-spring/customers-view-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/customersviewservice/web/CustomersViewWebConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.customersviewservice.web; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.customersviewservice.backend.CustomerViewBackendConfiguration; 4 | import org.springframework.boot.autoconfigure.web.HttpMessageConverters; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Import; 9 | import org.springframework.http.converter.HttpMessageConverter; 10 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 12 | 13 | @Configuration 14 | @Import({CustomerViewBackendConfiguration.class}) 15 | @ComponentScan 16 | public class CustomersViewWebConfiguration extends WebMvcConfigurerAdapter { 17 | 18 | @Bean 19 | public HttpMessageConverters customConverters() { 20 | HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter(); 21 | return new HttpMessageConverters(additional); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /java-spring/docker-compose-common.yml: -------------------------------------------------------------------------------- 1 | apigateway: 2 | build: ./api-gateway-service/ 3 | restart: unless-stopped 4 | ports: 5 | - "8080:8080" 6 | environment: 7 | SPRING_DATA_MONGODB_URI: mongodb://mongodb/mydb 8 | ACCOUNTS_COMMANDSIDE_SERVICE_HOST: accountsservice 9 | TRANSFERS_COMMANDSIDE_SERVICE_HOST: transactionsservice 10 | ACCOUNTS_QUERYSIDE_SERVICE_HOST: accountsviewservice 11 | CUSTOMERS_COMMANDSIDE_SERVICE_HOST: customersservice 12 | CUSTOMERS_QUERYSIDE_SERVICE_HOST: customersviewservice 13 | 14 | 15 | accountsservice: 16 | build: ./accounts-service/ 17 | restart: unless-stopped 18 | ports: 19 | - "8085:8080" 20 | 21 | transactionsservice: 22 | build: ./transactions-service/ 23 | restart: unless-stopped 24 | ports: 25 | - "8082:8080" 26 | 27 | accountsviewservice: 28 | build: ./accounts-view-service/ 29 | restart: unless-stopped 30 | environment: 31 | SPRING_DATA_MONGODB_URI: mongodb://mongodb/mydb 32 | ports: 33 | - "8081:8080" 34 | 35 | customersservice: 36 | build: ./customers-service/ 37 | restart: unless-stopped 38 | ports: 39 | - "8083:8080" 40 | 41 | customersviewservice: 42 | build: ./customers-view-service/ 43 | restart: unless-stopped 44 | ports: 45 | - "8084:8080" 46 | environment: 47 | SPRING_DATA_MONGODB_URI: mongodb://mongodb/mydb 48 | 49 | mongodb: 50 | image: mongo:3.0.4 51 | hostname: mongodb 52 | command: mongod --smallfiles 53 | ports: 54 | - "27017:27017" 55 | -------------------------------------------------------------------------------- /java-spring/e2e-test/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: VerifyMongoDBConfigurationPlugin 2 | 3 | dependencies { 4 | testCompile project(":testutil") 5 | testCompile project(":common-auth") 6 | testCompile "junit:junit:4.11" 7 | testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" 8 | testCompile "org.jsoup:jsoup:1.9.2" 9 | } 10 | 11 | test { 12 | ignoreFailures (!project.hasProperty("ignoreE2EFailures") || ignoreE2EFailures.toBoolean()) 13 | } 14 | -------------------------------------------------------------------------------- /java-spring/gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | org.gradle.jvmargs=-XX:MaxPermSize=512m 3 | 4 | eventuateMavenRepoUrl= 5 | 6 | springBootVersion=1.3.5.RELEASE 7 | 8 | eventuateClientVersion=0.14.0.RELEASE 9 | eventuateLocalVersion=0.11.0.RELEASE 10 | -------------------------------------------------------------------------------- /java-spring/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cer/event-sourcing-examples/7dcafe5deabb6cd55c414eaa611e8537ea39df2e/java-spring/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /java-spring/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jan 03 09:40:05 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-2.11-all.zip 7 | -------------------------------------------------------------------------------- /java-spring/mongodb-cli.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | docker run --rm --link javaspring_mongodb_1:mongodb -i -t mongo:3.0.4 /usr/bin/mongo --host mongodb 4 | -------------------------------------------------------------------------------- /java-spring/rest-api-integration-tests/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: VerifyMongoDBConfigurationPlugin 2 | 3 | dependencies { 4 | testCompile project(":common-auth-web") 5 | testCompile project(":transactions-service") 6 | testCompile project(":accounts-service") 7 | testCompile project(":accounts-view-service") 8 | testCompile project(":customers-service") 9 | testCompile project(":customers-view-service") 10 | testCompile project(":testutil") 11 | testCompile "junit:junit:4.11" 12 | testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" 13 | testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" 14 | } -------------------------------------------------------------------------------- /java-spring/set-env.sh: -------------------------------------------------------------------------------- 1 | if [ -z "$DOCKER_HOST_IP" ] ; then 2 | if [ -z "$DOCKER_HOST" ] ; then 3 | export DOCKER_HOST_IP=`hostname` 4 | else 5 | echo using ${DOCKER_HOST?} 6 | XX=${DOCKER_HOST%\:*} 7 | export DOCKER_HOST_IP=${XX#tcp\:\/\/} 8 | fi 9 | fi 10 | 11 | echo DOCKER_HOST_IP is $DOCKER_HOST_IP 12 | 13 | export SPRING_DATASOURCE_URL=jdbc:mysql://${DOCKER_HOST_IP}/eventuate 14 | export SPRING_DATASOURCE_USERNAME=mysqluser 15 | export SPRING_DATASOURCE_PASSWORD=mysqlpw 16 | export SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.jdbc.Driver 17 | export EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS=$DOCKER_HOST_IP:9092 18 | export EVENTUATELOCAL_CDC_DB_USER_NAME=root 19 | export EVENTUATELOCAL_CDC_DB_PASSWORD=rootpassword 20 | export EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING=$DOCKER_HOST_IP:2181 21 | 22 | -------------------------------------------------------------------------------- /java-spring/settings.gradle: -------------------------------------------------------------------------------- 1 | include 'testutil' 2 | include 'common' 3 | include 'common-backend' 4 | include 'common-auth' 5 | include 'common-auth-web' 6 | include 'common-swagger' 7 | include 'customers-query-side-common' 8 | 9 | include 'accounts-service' 10 | include 'accounts-view-service' 11 | include 'transactions-service' 12 | include 'customers-service' 13 | include 'customers-view-service' 14 | 15 | include 'api-gateway-service' 16 | 17 | include 'backend-integration-tests' 18 | include 'rest-api-integration-tests' 19 | include 'e2e-test' 20 | 21 | rootProject.name = 'java-spring-event-sourcing-example' 22 | -------------------------------------------------------------------------------- /java-spring/show-urls.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | IP=$(docker-machine ip default) 4 | 5 | echo Accounts command-side service = http://${IP}:8080/swagger-ui.html 6 | echo Money Transfers command-side service = http://${IP}:8082/swagger-ui.html 7 | echo Accounts query-side service = http://${IP}:8081/swagger-ui.html 8 | -------------------------------------------------------------------------------- /java-spring/testutil/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile project(":common") 5 | compile project(":common-auth") 6 | 7 | compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion" 8 | compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" 9 | 10 | compile "junit:junit:4.11" 11 | compile "io.reactivex:rxjava:1.1.5" 12 | } 13 | -------------------------------------------------------------------------------- /java-spring/testutil/src/main/java/net/chrisrichardson/eventstorestore/javaexamples/testutil/AbstractEntityEventTest.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstorestore.javaexamples.testutil; 2 | 3 | import io.eventuate.Aggregate; 4 | import io.eventuate.ReflectiveMutableCommandProcessingAggregate; 5 | import io.eventuate.javaclient.spring.EventEntityUtil; 6 | import org.junit.Test; 7 | import org.springframework.util.ReflectionUtils; 8 | 9 | import java.lang.reflect.Method; 10 | 11 | public abstract class AbstractEntityEventTest { 12 | 13 | @Test 14 | public void eventDefinitionsShouldBeCorrect() { 15 | 16 | ReflectionUtils.doWithMethods(entityClass(), new ReflectionUtils.MethodCallback() { 17 | @Override 18 | public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { 19 | Class eventClass = method.getParameterTypes()[0]; 20 | EventEntityUtil.toEntityType(eventClass); 21 | } 22 | }, 23 | new ReflectionUtils.MethodFilter() { 24 | @Override 25 | public boolean matches(Method method) { 26 | return method.getName().startsWith("apply") && 27 | method.getDeclaringClass() != Aggregate.class && 28 | method.getDeclaringClass() != ReflectiveMutableCommandProcessingAggregate.class; 29 | } 30 | }); 31 | 32 | } 33 | 34 | protected abstract Class<?> entityClass(); 35 | } 36 | -------------------------------------------------------------------------------- /java-spring/testutil/src/main/java/net/chrisrichardson/eventstorestore/javaexamples/testutil/Producer.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstorestore.javaexamples.testutil; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | public interface Producer<T> { 6 | public CompletableFuture<T> produce(); 7 | } 8 | -------------------------------------------------------------------------------- /java-spring/testutil/src/main/java/net/chrisrichardson/eventstorestore/javaexamples/testutil/RestTemplateErrorHandler.java: -------------------------------------------------------------------------------- 1 | 2 | package net.chrisrichardson.eventstorestore.javaexamples.testutil; 3 | 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.http.client.ClientHttpResponse; 7 | import org.springframework.web.client.ResponseErrorHandler; 8 | 9 | import java.io.IOException; 10 | 11 | public class RestTemplateErrorHandler implements ResponseErrorHandler { 12 | 13 | private static final Logger log = LoggerFactory.getLogger(RestTemplateErrorHandler.class); 14 | 15 | @Override 16 | public void handleError(ClientHttpResponse response) throws IOException { 17 | log.error("Response error: {} {}", response.getStatusCode(), response.getStatusText()); 18 | } 19 | 20 | @Override 21 | public boolean hasError(ClientHttpResponse response) throws IOException { 22 | return RestUtil.isError(response.getStatusCode()); 23 | } 24 | } -------------------------------------------------------------------------------- /java-spring/testutil/src/main/java/net/chrisrichardson/eventstorestore/javaexamples/testutil/RestUtil.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstorestore.javaexamples.testutil; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class RestUtil { 6 | 7 | public static boolean isError(HttpStatus status) { 8 | HttpStatus.Series series = status.series(); 9 | return (HttpStatus.Series.CLIENT_ERROR.equals(series) 10 | || HttpStatus.Series.SERVER_ERROR.equals(series)); 11 | } 12 | } -------------------------------------------------------------------------------- /java-spring/testutil/src/main/java/net/chrisrichardson/eventstorestore/javaexamples/testutil/Verifier.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstorestore.javaexamples.testutil; 2 | 3 | public interface Verifier<T> { 4 | public void verify(T x); 5 | } 6 | -------------------------------------------------------------------------------- /java-spring/transactions-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:openjdk-8u91-jdk 2 | CMD java ${JAVA_OPTS} -jar transactions-service.jar 3 | EXPOSE 8080 4 | COPY build/libs/transactions-service.jar . -------------------------------------------------------------------------------- /java-spring/transactions-service/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'spring-boot' 2 | apply plugin: EventuateDependencyPlugin 3 | 4 | apply plugin: VerifyEventStoreEnvironmentPlugin 5 | 6 | dependencies { 7 | compile project(":common-backend") 8 | compile project(":common-swagger") 9 | 10 | compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" 11 | compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" 12 | 13 | testCompile project(":testutil") 14 | testCompile "junit:junit:4.11" 15 | testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" 16 | testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" 17 | } 18 | 19 | test { 20 | ignoreFailures System.getenv("EVENTUATE_API_KEY_ID") == null 21 | } 22 | 23 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/TransactionsServiceMain.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice; 2 | 3 | import io.eventuate.javaclient.driver.EventuateDriverConfiguration; 4 | import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration; 5 | import net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.web.MoneyTransferWebConfiguration; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 8 | import org.springframework.context.annotation.ComponentScan; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Import; 11 | 12 | @Configuration 13 | @Import({MoneyTransferWebConfiguration.class, 14 | EventuateDriverConfiguration.class, 15 | CommonSwaggerConfiguration.class}) 16 | @EnableAutoConfiguration 17 | @ComponentScan 18 | public class TransactionsServiceMain { 19 | 20 | public static void main(String[] args) { 21 | SpringApplication.run(TransactionsServiceMain.class, args); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/backend/CreateMoneyTransferCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.TransferDetails; 4 | 5 | public class CreateMoneyTransferCommand implements MoneyTransferCommand { 6 | private TransferDetails details; 7 | 8 | public TransferDetails getDetails() { 9 | return details; 10 | } 11 | 12 | public CreateMoneyTransferCommand(TransferDetails details) { 13 | 14 | this.details = details; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/backend/MoneyTransferBackendConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend; 2 | 3 | import io.eventuate.AggregateRepository; 4 | import io.eventuate.EventuateAggregateStore; 5 | import io.eventuate.javaclient.spring.EnableEventHandlers; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | @EnableEventHandlers 11 | public class MoneyTransferBackendConfiguration { 12 | 13 | @Bean 14 | public MoneyTransferService moneyTransferService(AggregateRepository<MoneyTransfer, MoneyTransferCommand> moneyTransferRepository) { 15 | return new MoneyTransferService(moneyTransferRepository); 16 | } 17 | 18 | @Bean 19 | public MoneyTransferWorkflow moneyTransferWorkflow() { 20 | return new MoneyTransferWorkflow(); 21 | } 22 | 23 | @Bean 24 | public AggregateRepository<MoneyTransfer, MoneyTransferCommand> moneyTransferRepository(EventuateAggregateStore eventStore) { 25 | return new AggregateRepository<MoneyTransfer, MoneyTransferCommand>(MoneyTransfer.class, eventStore); 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/backend/MoneyTransferCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend; 2 | 3 | import io.eventuate.Command; 4 | 5 | interface MoneyTransferCommand extends Command { 6 | } 7 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/backend/MoneyTransferService.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend; 2 | 3 | import io.eventuate.AggregateRepository; 4 | import io.eventuate.EntityWithIdAndVersion; 5 | import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.TransferDetails; 6 | 7 | import java.util.concurrent.CompletableFuture; 8 | 9 | public class MoneyTransferService { 10 | private final AggregateRepository<MoneyTransfer, MoneyTransferCommand> aggregateRepository; 11 | 12 | public MoneyTransferService(AggregateRepository<MoneyTransfer, MoneyTransferCommand> aggregateRepository) { 13 | this.aggregateRepository = aggregateRepository; 14 | } 15 | 16 | public CompletableFuture<EntityWithIdAndVersion<MoneyTransfer>> transferMoney(TransferDetails transferDetails) { 17 | return aggregateRepository.save(new CreateMoneyTransferCommand(transferDetails)); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/backend/RecordCreditCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend; 2 | 3 | public class RecordCreditCommand implements MoneyTransferCommand { 4 | } 5 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/backend/RecordDebitCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend; 2 | 3 | public class RecordDebitCommand implements MoneyTransferCommand { 4 | } 5 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/backend/RecordDebitFailedCommand.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend; 2 | 3 | public class RecordDebitFailedCommand implements MoneyTransferCommand { 4 | } 5 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/web/MoneyTransferWebConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.web; 2 | 3 | import net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend.MoneyTransferBackendConfiguration; 4 | import org.springframework.boot.autoconfigure.web.HttpMessageConverters; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Import; 9 | import org.springframework.http.converter.HttpMessageConverter; 10 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 11 | 12 | @Configuration 13 | @Import({MoneyTransferBackendConfiguration.class}) 14 | @ComponentScan 15 | public class MoneyTransferWebConfiguration { 16 | 17 | @Bean 18 | public HttpMessageConverters customConverters() { 19 | HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter(); 20 | return new HttpMessageConverters(additional); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/TransactionsCommandSideServiceIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.boot.test.IntegrationTest; 8 | import org.springframework.boot.test.SpringApplicationConfiguration; 9 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 | import org.springframework.test.context.web.WebAppConfiguration; 11 | import org.springframework.web.client.RestTemplate; 12 | 13 | @RunWith(SpringJUnit4ClassRunner.class) 14 | @SpringApplicationConfiguration(classes = TransactionsCommandSideServiceTestConfiguration.class) 15 | @WebAppConfiguration 16 | @IntegrationTest({"server.port=0", "management.port=0"}) 17 | public class TransactionsCommandSideServiceIntegrationTest { 18 | 19 | @Value("${local.server.port}") 20 | private int port; 21 | 22 | private String baseUrl(String path) { 23 | return "http://localhost:" + port + "/api" + path; 24 | } 25 | 26 | @Autowired 27 | RestTemplate restTemplate; 28 | 29 | 30 | @Test 31 | public void shouldCreateAccountsAndTransferMoney() { 32 | // TBD 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/backend/MoneyTransferEventTest.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.backend; 2 | 3 | import net.chrisrichardson.eventstorestore.javaexamples.testutil.AbstractEntityEventTest; 4 | 5 | public class MoneyTransferEventTest extends AbstractEntityEventTest { 6 | 7 | @Override 8 | protected Class<?> entityClass() { 9 | return MoneyTransfer.class; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /java-spring/transactions-service/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/transactionsservice/web/MoneyTransferControllerIntegrationTestConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.javaexamples.banking.transactionsservice.web; 2 | 3 | import io.eventuate.javaclient.spring.jdbc.EmbeddedTestAggregateStoreConfiguration; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.Import; 7 | 8 | @Configuration 9 | @Import({MoneyTransferWebConfiguration.class, EmbeddedTestAggregateStoreConfiguration.class}) 10 | @EnableAutoConfiguration 11 | public class MoneyTransferControllerIntegrationTestConfiguration { 12 | } 13 | -------------------------------------------------------------------------------- /js-frontend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0", "react"], 3 | "plugins": ["add-module-exports"] 4 | } 5 | -------------------------------------------------------------------------------- /js-frontend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | !/build 4 | #dist 5 | dist-intermediate 6 | -------------------------------------------------------------------------------- /js-frontend/README.md: -------------------------------------------------------------------------------- 1 | # Money Transfer App - Frontend Client 2 | 3 | This.. 4 | 5 | ## Happiness is six lines away 6 | 7 | *Prerequisites: node.js and git* 8 | 9 | ``` 10 | cd js-frontend 11 | npm install 12 | npm run build 13 | ``` 14 | 15 | Text.. 16 | -------------------------------------------------------------------------------- /js-frontend/build/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cer/event-sourcing-examples/7dcafe5deabb6cd55c414eaa611e8537ea39df2e/js-frontend/build/.gitkeep -------------------------------------------------------------------------------- /js-frontend/build/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /js-frontend/build/style.b588c60da106277d78c8.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"style.b588c60da106277d78c8.css","sourceRoot":""} -------------------------------------------------------------------------------- /js-frontend/build/style.b588c60da106277d78c8.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([1,3],{ 2 | 3 | /***/ 0: 4 | /***/ function(module, exports, __webpack_require__) { 5 | 6 | __webpack_require__(612); 7 | module.exports = __webpack_require__(616); 8 | 9 | 10 | /***/ }, 11 | 12 | /***/ 612: 13 | /***/ function(module, exports) { 14 | 15 | // removed by extract-text-webpack-plugin 16 | 17 | /***/ }, 18 | 19 | /***/ 616: 20 | /***/ function(module, exports) { 21 | 22 | // removed by extract-text-webpack-plugin 23 | 24 | /***/ } 25 | 26 | }); 27 | //# sourceMappingURL=style.b588c60da106277d78c8.js.map -------------------------------------------------------------------------------- /js-frontend/build/style.b588c60da106277d78c8.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///./~/react-select/dist/react-select.css?","webpack:///./src/main.less?"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,0C;;;;;;;ACAA,0C","file":"style.b588c60da106277d78c8.js","sourcesContent":["// removed by extract-text-webpack-plugin\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./~/react-select/dist/react-select.css\n ** module id = 612\n ** module chunks = 1\n **/","// removed by extract-text-webpack-plugin\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./src/main.less\n ** module id = 616\n ** module chunks = 1\n **/"],"sourceRoot":""} -------------------------------------------------------------------------------- /js-frontend/config/environments/development.js: -------------------------------------------------------------------------------- 1 | export default { 2 | identityProperty: 'APP_IDENTITY', 3 | } 4 | -------------------------------------------------------------------------------- /js-frontend/config/environments/production.js: -------------------------------------------------------------------------------- 1 | export default { 2 | identityProperty: 'APP_IDENTITY', 3 | } 4 | -------------------------------------------------------------------------------- /js-frontend/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register'); 2 | 3 | module.exports = require('./nightwatch.json'); -------------------------------------------------------------------------------- /js-frontend/nightwatch.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_folders": ["tests/e2e-tests"], 3 | "output_folder": "reports", 4 | "custom_commands_path": "", 5 | "custom_assertions_path": "", 6 | "page_objects_path": "tests/e2e-pages", 7 | "globals_path": "tests/e2e-globals/globals.js", 8 | 9 | "selenium": { 10 | "start_process": true, 11 | "server_path": "./node_modules/selenium-standalone/.selenium/selenium-server/2.53.1-server.jar", 12 | "log_path": "./reports", 13 | "host": "127.0.0.1", 14 | "port": 4444, 15 | "cli_args": { 16 | "webdriver.chrome.driver": "./node_modules/selenium-standalone/.selenium/chromedriver/2.24-x64-chromedriver", 17 | "webdriver.firefox.driver": "./node_modules/selenium-standalone/.selenium/geckodriver/0.10.0-x64-geckodriver" 18 | } 19 | }, 20 | "test_settings": { 21 | "default": { 22 | "launch_url": "http://localhost:8080", 23 | "selenium_port": 4444, 24 | "selenium_host": "localhost", 25 | "silent": true, 26 | "desiredCapabilities": { 27 | "browserName": "chrome", 28 | "javascriptEnabled": true, 29 | "acceptSslCerts": true 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /js-frontend/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cer/event-sourcing-examples/7dcafe5deabb6cd55c414eaa611e8537ea39df2e/js-frontend/public/.gitkeep -------------------------------------------------------------------------------- /js-frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /js-frontend/reports/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cer/event-sourcing-examples/7dcafe5deabb6cd55c414eaa611e8537ea39df2e/js-frontend/reports/.gitkeep -------------------------------------------------------------------------------- /js-frontend/src/actions/configure.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 26/02/16. 3 | */ 4 | import { authenticate } from "./authenticate"; 5 | import { applyConfig } from "../utils/clientSettings"; 6 | 7 | export const SET_ENDPOINT_KEYS = "SET_ENDPOINT_KEYS"; 8 | 9 | export function setEndpointKeys(endpoints, currentEndpointKey, defaultEndpointKey) { 10 | return { 11 | type: SET_ENDPOINT_KEYS, 12 | endpoints, 13 | currentEndpointKey, 14 | defaultEndpointKey 15 | }; 16 | } 17 | 18 | export function configure(endpoint={}, settings={}) { 19 | 20 | return dispatch => { 21 | 22 | return applyConfig({ dispatch, endpoint, settings }) 23 | .then(() => { 24 | return dispatch(authenticate()); 25 | }); 26 | 27 | }; 28 | } -------------------------------------------------------------------------------- /js-frontend/src/actions/navigate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 26/02/16. 3 | */ 4 | import T from '../constants/ACTION_TYPES'; 5 | import { makeActionCreator } from '../utils/actions'; 6 | 7 | export const visitLocation = makeActionCreator(T.LOCATION.ENTER, 'location'); 8 | -------------------------------------------------------------------------------- /js-frontend/src/actions/signIn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 26/02/16. 3 | */ 4 | import T from '../constants/ACTION_TYPES'; 5 | import { makeActionCreator } from '../utils/actions'; 6 | import { persistUserData } from "../utils/sessionStorage"; 7 | import { entityReceived } from './entities'; 8 | import { apiSignIn } from '../utils/api'; 9 | 10 | export const emailSignInFormUpdate = makeActionCreator(T.AUTH.SIGN_IN_FORM_UPDATE, 'key', 'value'); 11 | export const emailSignInStart = makeActionCreator(T.AUTH.SIGN_IN_START); 12 | export const emailSignInComplete = makeActionCreator(T.AUTH.SIGN_IN_COMPLETE, 'user'); 13 | export const emailSignInError = makeActionCreator(T.AUTH.SIGN_IN_ERROR, 'error'); 14 | 15 | export function emailSignIn(body) { 16 | return dispatch => { 17 | 18 | dispatch(emailSignInStart()); 19 | 20 | return apiSignIn(body) 21 | .then(function(data = {}) { 22 | const { id } = data; 23 | if (id ) { 24 | dispatch(entityReceived(id, data)); 25 | } 26 | return data; 27 | }) 28 | .then((user) => { 29 | persistUserData(user); 30 | dispatch(emailSignInComplete(user)); 31 | }) 32 | .catch((errors) => { 33 | // revert endpoint key to what it was before failed request 34 | //setCurrentEndpointKey(prevEndpointKey); 35 | //dispatch(storeCurrentEndpointKey(prevEndpointKey)); 36 | return dispatch(emailSignInError(errors)); 37 | }); 38 | }; 39 | } -------------------------------------------------------------------------------- /js-frontend/src/actions/signOut.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 11/03/16. 3 | */ 4 | import T from '../constants/ACTION_TYPES'; 5 | import { makeActionCreator } from '../utils/actions'; 6 | import { destroySession } from "../utils/sessionStorage"; 7 | 8 | export const signOutStart = makeActionCreator(T.AUTH.SIGN_OUT_START); 9 | export const signOutComplete = makeActionCreator(T.AUTH.SIGN_OUT_COMPLETE); 10 | 11 | export const signOut = () => 12 | dispatch => { 13 | dispatch(signOutStart()); 14 | 15 | destroySession(); 16 | 17 | dispatch(signOutComplete()); 18 | }; -------------------------------------------------------------------------------- /js-frontend/src/actions/signUp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 11/03/16. 3 | */ 4 | import { push } from 'redux-router'; 5 | import T from '../constants/ACTION_TYPES'; 6 | import { makeActionCreator } from '../utils/actions'; 7 | import { apiSignUp } from "../utils/api"; 8 | import { emailSignInFormUpdate } from './signIn'; 9 | 10 | export const emailSignUpFormUpdate = makeActionCreator(T.AUTH.SIGN_UP_FORM_UPDATE, 'key', 'value'); 11 | export const emailSignUpStart = makeActionCreator(T.AUTH.SIGN_UP_START); 12 | export const emailSignUpComplete = makeActionCreator(T.AUTH.SIGN_UP_COMPLETE, 'user'); 13 | export const emailSignUpError = makeActionCreator(T.AUTH.SIGN_UP_ERROR, 'error'); 14 | 15 | 16 | export function emailSignUp(body) { 17 | return dispatch => { 18 | dispatch(emailSignUpStart()); 19 | 20 | return apiSignUp(body) 21 | .then(({ data }) => { 22 | dispatch(emailSignUpComplete(data)); 23 | const { email } = body; 24 | dispatch(emailSignInFormUpdate('email', email)); 25 | dispatch(push('/signin')); 26 | }) 27 | .catch(({ errors }) => { 28 | dispatch(emailSignUpError({ 29 | errors 30 | })) 31 | }); 32 | 33 | }; 34 | } -------------------------------------------------------------------------------- /js-frontend/src/client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 12/02/16. 3 | */ 4 | import React from "react"; 5 | import ReactDOM from "react-dom"; 6 | import { initialize } from "./app"; 7 | /** 8 | * Fire-up React Router. 9 | */ 10 | initialize().then(({ provider }) => { 11 | const reactRoot = window.document.getElementById("root"); 12 | ReactDOM.render(provider, reactRoot); 13 | }); 14 | 15 | 16 | /** 17 | * Detect whether the server-side render has been discarded due to an invalid checksum. 18 | */ 19 | if (process.env.NODE_ENV !== "production") { 20 | const reactRoot = window.document.getElementById("root"); 21 | if (!reactRoot.firstChild || !reactRoot.firstChild.attributes || 22 | !reactRoot.firstChild.attributes["data-react-checksum"]) { 23 | console.error("Server-side React render was discarded. Make sure that your initial render does not contain any client-side code."); 24 | } 25 | } -------------------------------------------------------------------------------- /js-frontend/src/components/Money.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 3/22/16. 3 | */ 4 | import React from 'react'; 5 | 6 | export const moneyText = (amount) => { 7 | 8 | if (Number.isNaN(Number(amount))) { 9 | return '—'; 10 | } 11 | const absNum = Math.abs(Number(amount) / 100); 12 | if (absNum < 0) { 13 | return `$(${absNum.toFixed(2)})`; 14 | } 15 | return `${absNum.toFixed(2)}`; 16 | }; 17 | 18 | export const Money = ({ amount }) => { 19 | 20 | if (Number.isNaN(Number(amount))) { 21 | return (<span />); 22 | } 23 | const absNum = Math.abs(Number(amount) / 100); 24 | if (absNum < 0) { 25 | return (<span className="text-danger">(${ absNum.toFixed(2) })</span>) 26 | } 27 | return (<span>${ absNum.toFixed(2) }</span>); 28 | }; -------------------------------------------------------------------------------- /js-frontend/src/components/partials/IndexPanel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 17/02/16. 3 | */ 4 | import React, { PropTypes } from "react"; 5 | import { Panel, Col } from "react-bootstrap"; 6 | 7 | export class IndexPanel extends React.Component { 8 | static propTypes = { 9 | bsStyle: PropTypes.string, 10 | header: PropTypes.string, 11 | children: PropTypes.node 12 | }; 13 | 14 | static defaultProps = { 15 | bsStyle: "info", 16 | children: <span /> 17 | }; 18 | 19 | render () { 20 | return ( 21 | <Col sm={6}> 22 | <Panel {...this.props} /> 23 | </Col> 24 | ); 25 | } 26 | } 27 | 28 | export default IndexPanel; -------------------------------------------------------------------------------- /js-frontend/src/controls/bootstrap/AuxErrorLabel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 15/02/16. 3 | */ 4 | import React, { PropTypes } from "react"; 5 | import { Glyphicon } from "react-bootstrap"; 6 | 7 | class AuxErrorLabel extends React.Component { 8 | 9 | static propTypes = { 10 | label: PropTypes.string, 11 | errors: PropTypes.array 12 | }; 13 | 14 | static defaultProps = { 15 | label: '', 16 | errors: [] 17 | }; 18 | 19 | render () { 20 | const { errors } = this.props; 21 | 22 | if (errors.length) { 23 | return ( 24 | <div className='has-error'> 25 | { errors.map((err, i) => { 26 | return ( 27 | <p className="control-label inline-error-item" 28 | style={{paddingLeft: "20px", position: "relative", marginBottom: "28px"}} 29 | key={i}> 30 | 31 | <Glyphicon glyph="exclamation-sign" 32 | style={{ 33 | position: "absolute", 34 | left: 0, 35 | top: 2 36 | }} 37 | /> {this.props.label} {err} 38 | </p> 39 | ); 40 | })} 41 | </div> 42 | ); 43 | } else { 44 | return <span />; 45 | } 46 | } 47 | } 48 | 49 | export default AuxErrorLabel; 50 | -------------------------------------------------------------------------------- /js-frontend/src/entities/formToPayloadMappers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 21/02/16. 3 | */ 4 | export const customerInfoMap = ({ 5 | ssn, 6 | password, 7 | address1, 8 | address2, 9 | city, //: "Moscow" 10 | email, //: "arevinsky@gmail.com" 11 | fname, //: "Andrew" 12 | lname, //: "Revinsky" 13 | phoneNumber, //: "+79031570864" 14 | state, //: "Kentucky" 15 | zip //: "125315" 16 | }) => ({ 17 | "name": { 18 | "firstName": fname, 19 | "lastName": lname 20 | }, 21 | password, 22 | email, 23 | ssn, 24 | "phoneNumber": phoneNumber, 25 | "address": { 26 | "street1": address1, 27 | "street2": address2, 28 | city, 29 | state, 30 | "zipCode": zip 31 | } 32 | }); -------------------------------------------------------------------------------- /js-frontend/src/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <meta charset="utf-8"> 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 | 7 | <title>Money Transfer App</title> 8 | <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous"> 9 | 10 | <link rel="stylesheet" href="/style2.css"> 11 | 12 | <!-- inject:app:css --> 13 | <!-- endinject --> 14 | 15 | </head> 16 | <body> 17 | <div id="react-app"></div> 18 | 19 | <!-- inject:app:js --> 20 | <!-- endinject --> 21 | </body> 22 | </html> 23 | -------------------------------------------------------------------------------- /js-frontend/src/main.less: -------------------------------------------------------------------------------- 1 | /* 2 | * This file contains Global styles. 3 | * 4 | * In general, your styles should *not* be in this file, but in the individual 5 | * component files. For details, see the Pacomo specification: 6 | * 7 | * https://github.com/unicorn-standard/pacomo 8 | */ 9 | 10 | @import url('http://fonts.googleapis.com/css?family=Roboto:300,400,500'); 11 | 12 | * { 13 | box-sizing: border-box; 14 | margin: 0; 15 | } 16 | *:before, 17 | *:after { 18 | box-sizing: border-box; 19 | } 20 | 21 | html, body, main { 22 | position: relative; 23 | height: 100%; 24 | min-height: 100%; 25 | font-family: Roboto; 26 | } 27 | 28 | body { 29 | -webkit-tap-highlight-color: rgba(0,0,0,0); 30 | } 31 | 32 | // Reset fonts for relevant elements 33 | input, 34 | button, 35 | select, 36 | textarea { 37 | font-family: inherit; 38 | font-size: inherit; 39 | line-height: inherit; 40 | } 41 | 42 | #react-app { 43 | position: relative; 44 | height: 100%; 45 | min-height: 100%; 46 | } 47 | 48 | body { 49 | padding-bottom: 50px; 50 | /* height: 100%; */ 51 | /* min-height: 100%; */ 52 | height: auto; 53 | } 54 | .footer-navigation { 55 | height: 1px; 56 | & > .container { 57 | height: 100%; 58 | & > * { 59 | top: 50%; 60 | transform: translateY(-50%); 61 | } 62 | } 63 | } 64 | 65 | .page-header { 66 | padding-bottom: 9px; 67 | margin: 0px 0 20px; 68 | border-bottom: 1px solid #eee; 69 | } 70 | h1 { 71 | margin-top: .5em; 72 | } -------------------------------------------------------------------------------- /js-frontend/src/reducers/auth/authenticate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 25/02/16. 3 | */ 4 | import T from '../../constants/ACTION_TYPES'; 5 | 6 | const initialState = { 7 | loading: false, 8 | valid: false, 9 | errors: null 10 | }; 11 | 12 | export const authReducer = (state = {...initialState}, action) => { 13 | switch(action.type) { 14 | case T.AUTH.AUTHENTICATE_START: 15 | return { 16 | ...state, 17 | loading: true 18 | }; 19 | 20 | case T.AUTH.AUTHENTICATE_COMPLETE: 21 | return { 22 | ...state, 23 | loading: false, 24 | errors: null, 25 | valid: true 26 | }; 27 | 28 | case T.AUTH.AUTHENTICATE_ERROR: 29 | return { 30 | ...state, 31 | loading: false, 32 | errors: "Invalid token", 33 | valid: false 34 | }; 35 | 36 | default: return state; 37 | } 38 | }; -------------------------------------------------------------------------------- /js-frontend/src/reducers/auth/configure.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 25/02/16. 3 | */ 4 | import T from '../../constants/ACTION_TYPES'; 5 | import createDataReducer from '../createDataReducer'; 6 | 7 | export const configReducer = createDataReducer([ 8 | T.AUTH.CONFIGURE_START, 9 | T.AUTH.CONFIGURE_COMPLETE, 10 | T.AUTH.CONFIGURE_ERROR 11 | ], 12 | 'config', 13 | 'config', 14 | (c = {}) => ({ ...c }) 15 | ); 16 | -------------------------------------------------------------------------------- /js-frontend/src/reducers/auth/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 25/02/16. 3 | */ 4 | import { combineReducers } from 'redux'; 5 | 6 | import { configReducer } from './configure'; 7 | import { authReducer } from './authenticate'; 8 | import { signInReducer } from './signin'; 9 | import { signUpReducer } from './signup'; 10 | import { signOutReducer } from './signout'; 11 | import { userReducer } from './user'; 12 | 13 | const authStateReducer = combineReducers({ 14 | configure: configReducer, 15 | signIn: signInReducer, 16 | signUp: signUpReducer, 17 | signOut: signOutReducer, 18 | authentication: authReducer, 19 | user: userReducer 20 | }); 21 | 22 | export default authStateReducer; -------------------------------------------------------------------------------- /js-frontend/src/reducers/auth/signin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 25/02/16. 3 | */ 4 | import T from '../../constants/ACTION_TYPES'; 5 | import createFormReducer from '../createFormReducer'; 6 | 7 | const internalSignInReducer = createFormReducer([ 8 | T.AUTH.SIGN_IN_START, 9 | T.AUTH.SIGN_IN_COMPLETE, 10 | T.AUTH.SIGN_IN_ERROR, 11 | T.AUTH.SIGN_IN_FORM_UPDATE 12 | ]); 13 | 14 | export const signInReducer = (state, action) => { 15 | switch (action.type) { 16 | case T.LOCATION.ENTER: { 17 | const { location } = action; 18 | const { pathname } = location; 19 | if (pathname == '/signin') { 20 | return internalSignInReducer(state, { 21 | type: T.AUTH.SIGN_IN_ERROR, 22 | error: null 23 | }); 24 | } 25 | return state; 26 | } 27 | 28 | default: { 29 | return internalSignInReducer(state, action); 30 | } 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /js-frontend/src/reducers/auth/signout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 25/02/16. 3 | */ 4 | import T from '../../constants/ACTION_TYPES'; 5 | 6 | const signOutInitialState = { 7 | loading: false, 8 | errors: null 9 | }; 10 | 11 | export const signOutReducer = (state = {...signOutInitialState}, action) => { 12 | switch(action.type) { 13 | case T.AUTH.SIGN_OUT_START: 14 | return { 15 | ...state, 16 | loading: true 17 | }; 18 | case T.AUTH.SIGN_OUT_COMPLETE: 19 | return { 20 | ...state, 21 | loading: false, 22 | errors: null 23 | }; 24 | default: return state; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /js-frontend/src/reducers/auth/signup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 25/02/16. 3 | */ 4 | import T from '../../constants/ACTION_TYPES'; 5 | import createFormReducer from '../createFormReducer'; 6 | 7 | export const internalSignUpReducer = createFormReducer([ 8 | T.AUTH.SIGN_UP_START, 9 | T.AUTH.SIGN_UP_COMPLETE, 10 | T.AUTH.SIGN_UP_ERROR, 11 | T.AUTH.SIGN_UP_FORM_UPDATE 12 | ]); 13 | 14 | 15 | export const signUpReducer = (state, action) => { 16 | switch (action.type) { 17 | case T.LOCATION.ENTER: { 18 | const { location } = action; 19 | const { pathname } = location; 20 | if (pathname == '/register') { 21 | return internalSignUpReducer(state, { 22 | type: T.AUTH.SIGN_UP_ERROR, 23 | error: null 24 | }); 25 | } 26 | return state; 27 | } 28 | 29 | default: { 30 | return internalSignUpReducer(state, action); 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /js-frontend/src/reducers/auth/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 25/02/16. 3 | */ 4 | import T from '../../constants/ACTION_TYPES'; 5 | 6 | const initalState = { 7 | attributes: null, 8 | isSignedIn: false 9 | }; 10 | 11 | export const userReducer = (state = {...initalState}, action) => { 12 | switch(action.type) { 13 | case T.AUTH.AUTHENTICATE_COMPLETE: 14 | case T.AUTH.SIGN_IN_COMPLETE: { 15 | const { user } = action; 16 | return {...state, 17 | attributes: user, 18 | isSignedIn: !!user 19 | }; 20 | } 21 | case T.AUTH.SIGN_OUT_COMPLETE: 22 | case T.AUTH.AUTHENTICATE_ERROR: 23 | return { 24 | ...initalState 25 | }; 26 | default: return state; 27 | } 28 | }; -------------------------------------------------------------------------------- /js-frontend/src/reducers/createFormReducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 3/22/16. 3 | */ 4 | 5 | const createFormReducer = ([KEY_REQUEST, KEY_SUCCESS, KEY_ERROR, KEY_UPDATE]) => { 6 | 7 | const initialState = { 8 | loading: false, 9 | form: {}, 10 | errors: {} 11 | }; 12 | 13 | return function formReducer(state = {...initialState}, action) { 14 | switch(action.type) { 15 | case KEY_REQUEST: { 16 | return { 17 | ...state, 18 | loading: true 19 | } 20 | } 21 | case KEY_ERROR: { 22 | const { error } = action; 23 | return { 24 | ...state, 25 | loading: false, 26 | errors: error 27 | } 28 | } 29 | case KEY_SUCCESS: { 30 | return { 31 | ...initialState 32 | } 33 | } 34 | case KEY_UPDATE: { 35 | const { key, value } = action; 36 | return { 37 | ...state, 38 | form: { 39 | ...state.form, 40 | [key]: value 41 | }, 42 | errors: { 43 | ...state.errors, 44 | aggregate: null, 45 | [key]: null 46 | } 47 | } 48 | } 49 | 50 | default: 51 | return state; 52 | } 53 | }; 54 | }; 55 | 56 | export default createFormReducer; -------------------------------------------------------------------------------- /js-frontend/src/reducers/data/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 15/03/16. 3 | */ 4 | import { combineReducers } from 'redux'; 5 | 6 | import { accounts } from './accounts'; 7 | import { transfers } from './transfers'; 8 | import { entities } from './entities'; 9 | import { bookmarkAccount } from './bookmarkAccount'; 10 | 11 | const dataReducer = combineReducers({ 12 | transfers, 13 | entities, 14 | accounts, 15 | bookmarkAccount 16 | }); 17 | 18 | export default dataReducer; -------------------------------------------------------------------------------- /js-frontend/src/reducers/data/transfers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 15/03/16. 3 | */ 4 | /** 5 | * Created by andrew on 15/03/16. 6 | */ 7 | import T from '../../constants/ACTION_TYPES'; 8 | import createListReducer from '../createDataReducer'; 9 | import {createByIdDataReducer } from '../createDataReducer'; 10 | 11 | const selectedEvents = [ 12 | T.TRANSFERS.LIST_START, 13 | T.TRANSFERS.LIST_COMPLETE, 14 | T.TRANSFERS.LIST_ERROR 15 | ]; 16 | 17 | export const transfers = createByIdDataReducer(selectedEvents, createListReducer(selectedEvents)); 18 | -------------------------------------------------------------------------------- /js-frontend/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 18/03/16. 3 | */ 4 | import { combineReducers } from 'redux'; 5 | 6 | import authStateReducer from './auth'; 7 | import appStateReducer from './data' 8 | import uiReducer from './ui' 9 | 10 | const mainReducer = combineReducers({ 11 | auth: authStateReducer, 12 | data: appStateReducer, 13 | ui: uiReducer 14 | }); 15 | 16 | export default mainReducer; -------------------------------------------------------------------------------- /js-frontend/src/reducers/ui/account.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 15/03/16. 3 | */ 4 | /** 5 | * Created by andrew on 15/03/16. 6 | */ 7 | import T from '../../constants/ACTION_TYPES'; 8 | import { combineReducers } from 'redux'; 9 | 10 | 11 | const initialState = { 12 | loading: false, 13 | errors: [] 14 | }; 15 | 16 | export const account = (state = { ...initialState }, action ) => { 17 | switch(action.type) { 18 | case T.ACCOUNT.SINGLE_START: { 19 | return { 20 | ...state, 21 | loading: true 22 | }; 23 | } 24 | case T.ACCOUNT.SINGLE_COMPLETE: { 25 | return { 26 | ...initialState 27 | }; 28 | } 29 | case T.ACCOUNT.SINGLE_ERROR: { 30 | const { error } = action; 31 | return { 32 | ...state, 33 | loading: false, 34 | errors: [ error ] 35 | }; 36 | 37 | } 38 | 39 | default: 40 | return state; 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /js-frontend/src/reducers/ui/bookmarkAccount.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 18/03/16. 3 | */ 4 | export const bookmarkAccount = (state = {}, action) => { 5 | switch (action.type) { 6 | default: 7 | return state; 8 | } 9 | }; -------------------------------------------------------------------------------- /js-frontend/src/reducers/ui/errors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 18/03/16. 3 | */ 4 | 5 | import T from '../../constants/ACTION_TYPES'; 6 | 7 | export const error = (state = null, action ) => { 8 | switch (action.type) { 9 | case T.ERROR.STOP: { 10 | return null; 11 | } 12 | case T.ERROR.START: 13 | return action.payload; 14 | 15 | default: 16 | return state; 17 | } 18 | }; -------------------------------------------------------------------------------- /js-frontend/src/reducers/ui/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 15/03/16. 3 | */ 4 | /** 5 | * Created by andrew on 25/02/16. 6 | */ 7 | import { combineReducers } from 'redux'; 8 | 9 | import { account } from './account'; 10 | import { error } from './errors'; 11 | import { bookmarkAccount } from './bookmarkAccount'; 12 | import { transfersMake } from './transfersMake'; 13 | 14 | 15 | const uiReducer = combineReducers({ 16 | account, 17 | error, 18 | bookmarkAccount, 19 | transfersMake 20 | }); 21 | 22 | export default uiReducer; -------------------------------------------------------------------------------- /js-frontend/src/reducers/ui/transfersMake.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 15/03/16. 3 | */ 4 | /** 5 | * Created by andrew on 15/03/16. 6 | */ 7 | import T from '../../constants/ACTION_TYPES'; 8 | import createFormReducer from '../createFormReducer'; 9 | 10 | export const transfersMake = createFormReducer([ 11 | T.TRANSFERS.MAKE_START, 12 | T.TRANSFERS.MAKE_COMPLETE, 13 | T.TRANSFERS.MAKE_ERROR, 14 | T.TRANSFERS.MAKE_FORM_UPDATE 15 | ]); 16 | -------------------------------------------------------------------------------- /js-frontend/src/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cer/event-sourcing-examples/7dcafe5deabb6cd55c414eaa611e8537ea39df2e/js-frontend/src/static/.gitkeep -------------------------------------------------------------------------------- /js-frontend/src/theme/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cer/event-sourcing-examples/7dcafe5deabb6cd55c414eaa611e8537ea39df2e/js-frontend/src/theme/.gitkeep -------------------------------------------------------------------------------- /js-frontend/src/utils/actions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 15/03/16. 3 | */ 4 | export function makeActionCreator(type, ...argNames) { 5 | return function(...args) { 6 | return argNames.reduce((action, arg, index) => { 7 | action[arg] = args[index]; 8 | return action; 9 | }, { type }); 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /js-frontend/src/utils/apiEndpoints.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 9/16/16. 3 | */ 4 | const API_ROOT = '/api'; 5 | 6 | export const emailSignIn = () => API_ROOT + '/login'; 7 | export const emailSignUp = () => API_ROOT + '/customers'; 8 | export const currentUser = () => API_ROOT + '/user'; 9 | export const accountsPath = () => API_ROOT + '/accounts'; 10 | export const customersLookup = (lookup) => `${API_ROOT}/customers?${ makeQuery(lookup) }`; 11 | export const customersAccounts = (customerId) => `${API_ROOT}/customers/${customerId}/accounts`; 12 | export const refAccounts = (customerId) => `${API_ROOT}/customers/${customerId}/toaccounts`; 13 | export const refAccount = (customerId, accountId) => `${API_ROOT}/customers/${customerId}/toaccounts/${accountId}`; 14 | export const account = (accountId) => `${API_ROOT}/accounts/${accountId}`; 15 | export const history = (accountId) => `${API_ROOT}/accounts/${accountId}/history`; 16 | export const transfers = () => API_ROOT + '/transfers'; 17 | 18 | function makeQuery(params) { 19 | return Object.keys(params) 20 | .map(key => [ encodeURIComponent(key), encodeURIComponent(params[key]) ].join('=')) 21 | .join('&'); 22 | } 23 | -------------------------------------------------------------------------------- /js-frontend/src/utils/blockedExecution.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 8/17/16. 3 | */ 4 | 5 | export const blocked = (fn, useCb) => { 6 | let isBlocked = false; 7 | return (...args) => { 8 | if (isBlocked) { 9 | return; 10 | } 11 | //noinspection JSUnusedAssignment 12 | isBlocked = true; 13 | const cb = () => { 14 | isBlocked = false; 15 | }; 16 | const result = useCb ? fn(...(args.concat([cb]))) : fn(...args); 17 | if (!useCb) { 18 | cb(); 19 | } 20 | return result; 21 | }; 22 | }; -------------------------------------------------------------------------------- /js-frontend/src/utils/compact.js: -------------------------------------------------------------------------------- 1 | export default function compact(obj) { 2 | let entries = Object.entries(obj) 3 | let result = Object.assign({}, obj) 4 | let count = entries.length 5 | for (let [key, value] of entries) { 6 | if (!value) { 7 | count -= 1 8 | delete result[key] 9 | } 10 | } 11 | return count === 0 ? null : result 12 | } 13 | -------------------------------------------------------------------------------- /js-frontend/src/utils/compose.js: -------------------------------------------------------------------------------- 1 | export default function compose(...funcs) { 2 | const innerFunc = funcs.pop(); 3 | return (...args) => funcs.reduceRight((composed, f) => f(composed), innerFunc(...args)) 4 | } 5 | -------------------------------------------------------------------------------- /js-frontend/src/utils/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 26/02/16. 3 | */ 4 | export const INITIAL_CONFIG_KEY = "default"; 5 | export const DEFAULT_CONFIG_KEY = "defaultConfigKey"; 6 | export const SAVED_CONFIG_KEY = "currentConfigName"; 7 | export const SAVED_CREDS_KEY = "authHeaders"; 8 | export const SAVED_USER_INFO = "user-info"; -------------------------------------------------------------------------------- /js-frontend/src/utils/partial.js: -------------------------------------------------------------------------------- 1 | export default function partial(fn, ...firstArgs) { 2 | return (...args) => fn(...firstArgs, ...args) 3 | } 4 | -------------------------------------------------------------------------------- /js-frontend/src/utils/readProp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 11/03/16. 3 | */ 4 | export default function read(src, path = '', defaultVal = null) { 5 | const [pathItem = null, ...rest] = path.split('.'); 6 | 7 | if (pathItem === null ) { 8 | return src || defaultVal; 9 | } else if (rest.length === 0) { 10 | if (!src) { return defaultVal; } 11 | return src[pathItem] || defaultVal; 12 | } 13 | 14 | if (!src) { return defaultVal; } 15 | return read(src[pathItem], rest.join('.'), defaultVal); 16 | } -------------------------------------------------------------------------------- /js-frontend/src/utils/root.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 27/02/16. 3 | */ 4 | // even though this code shouldn't be used server-side, node will throw 5 | // errors if "window" is used 6 | export default Function("return this")() || (42, eval)("this"); -------------------------------------------------------------------------------- /js-frontend/src/utils/typeReducers.js: -------------------------------------------------------------------------------- 1 | export default function typeReducers(actionTypes, defaultState, reducers) { 2 | const inverseActionTypes = 3 | new Map(Object.entries(actionTypes).map(([x, y]) => [y, x])) 4 | 5 | return (state = defaultState, action) => { 6 | const reducer = reducers[inverseActionTypes.get(action.type)] 7 | return reducer ? reducer(state, action) : state 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /js-frontend/src/utils/uuid.js: -------------------------------------------------------------------------------- 1 | function uuidReplacer(c) { 2 | const r = Math.random()*16|0 3 | const v = c == 'x' ? r : (r&0x3|0x8) 4 | return v.toString(16) 5 | } 6 | 7 | 8 | export default function uuid() { 9 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, uuidReplacer) 10 | } 11 | -------------------------------------------------------------------------------- /js-frontend/src/views/SignUp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 12/02/16. 3 | */ 4 | import React from "react"; 5 | //import { PageHeader } from "react-bootstrap"; 6 | import { connect } from "react-redux"; 7 | import { pushState } from 'redux-router'; 8 | import read from '../utils/readProp'; 9 | 10 | import { PageHeader, OverlayTrigger, Tooltip, Row, ButtonGroup, Table } from "react-bootstrap"; 11 | import * as BS from "react-bootstrap"; 12 | import { Link, IndexLink} from "react-router"; 13 | 14 | //import { EmailSignUpForm } from "redux-auth/bootstrap-theme" 15 | import EmailSignUpForm from "../controls/bootstrap/EmailSignUpForm"; 16 | 17 | export class SignUp extends React.Component { 18 | 19 | checkRedirect(props) { 20 | if (props.isAuthenticated) { 21 | // redirect to login and add next param so we can redirect again after login 22 | // const redirectAfterLogin = props.location.pathname; 23 | props.dispatch(pushState(null, `/`)); 24 | } 25 | } 26 | 27 | componentWillMount() { 28 | this.checkRedirect(this.props); 29 | } 30 | 31 | componentWillReceiveProps(nextProps) { 32 | this.checkRedirect(nextProps); 33 | } 34 | 35 | render () { 36 | return ( 37 | <BS.Well> 38 | <BS.PageHeader>Register</BS.PageHeader> 39 | <EmailSignUpForm /> 40 | </BS.Well> 41 | ); 42 | 43 | } 44 | } 45 | export default connect(({ 46 | routes, 47 | app 48 | }) => ({routes, 49 | isAuthenticated: read(app, 'auth.user.isSignedIn', false) 50 | }))(SignUp); -------------------------------------------------------------------------------- /js-frontend/src/views/modals/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by andrew on 20/02/16. 3 | */ 4 | export { default as Add3rdPartyAccountModal } from './Add3rdPartyAccountModal'; 5 | export { default as NewAccountModal } from './NewAccountModal'; 6 | export { default as RemoveAccountBookmarkModal } from './RemoveAccountModal'; 7 | -------------------------------------------------------------------------------- /js-frontend/tests/e2e-globals/globals.js: -------------------------------------------------------------------------------- 1 | const salt = Math.random().toString().substr(2, 6); 2 | 3 | const userData = (() => { 4 | 5 | const [ fName, lName, email, pass, passConf, ssn ] = 'John|Doe|jd@em.com|12345|12345|12345'.split('|').map(k => `${k}_${salt}`); 6 | 7 | return { 8 | fName, lName, email, pass, passConf, ssn 9 | } 10 | 11 | })(); 12 | 13 | const otherUserData = (() => { 14 | 15 | const [ fName, lName, email, pass, passConf, ssn ] = 'Jane|Dole|janed@ail.com|56789|56789|56789'.split('|').map(k => `${k}_${salt}`); 16 | 17 | return { 18 | fName, lName, email, pass, passConf, ssn 19 | } 20 | 21 | })(); 22 | 23 | const accountOne = (() => { 24 | 25 | const [ title, amount, description ] = 'InitialAccount|100|One hundred'.split('|'); 26 | 27 | return { 28 | title, 29 | amount, 30 | description 31 | } 32 | 33 | })(); 34 | 35 | const accountTwo = (() => { 36 | 37 | const [ title, amount, description ] = 'SecondaryAccount|200|Two hundred'.split('|'); 38 | 39 | return { 40 | title, 41 | amount, 42 | description 43 | } 44 | 45 | })(); 46 | 47 | 48 | 49 | export default { 50 | waitForConditionTimeout: 10000, 51 | userData, 52 | otherUserData, 53 | accountOne, 54 | accountTwo 55 | }; -------------------------------------------------------------------------------- /js-frontend/tests/e2e-pages/loginPage.js: -------------------------------------------------------------------------------- 1 | const loginCommands = { 2 | login({ email, pass }) { 3 | 4 | this 5 | .waitForElementVisible('@emailInput', 500); 6 | 7 | this 8 | .clearValue('@emailInput') 9 | .setValue('@emailInput', email) 10 | .clearValue('@passInput') 11 | .setValue('@passInput', pass); 12 | 13 | this.getValue('@emailInput', (result) => { 14 | this.assert.equal(result.value, email); 15 | }); 16 | 17 | return this.waitForElementVisible('@loginButton') 18 | .click('@loginButton') 19 | .submitForm('@loginButton'); 20 | 21 | } 22 | }; 23 | 24 | export default { 25 | url: 'http://localhost:8080/#/signin', 26 | commands: [loginCommands], 27 | elements: { 28 | emailInput: { 29 | selector: 'input[type=text]' 30 | }, 31 | emailLoginPageInput: { 32 | selector: 'input.email-sign-in-email.form-control' 33 | }, 34 | passInput: { 35 | selector: 'input[name=password]' 36 | }, 37 | loginButton: { 38 | selector: 'button[type=submit]' 39 | }, 40 | formError: { 41 | selector: '.control-label.inline-error-item' 42 | } 43 | } 44 | }; -------------------------------------------------------------------------------- /js-frontend/tests/e2e-tests/test010_Signup.js: -------------------------------------------------------------------------------- 1 | import globals from '../e2e-globals/globals'; 2 | 3 | export default { 4 | '@tags': ['register', 'sanity'], 5 | 6 | 'User signs up': (client) => { 7 | const signupPage = client.page.signupPage(); 8 | const loginPage = client.page.loginPage(); 9 | 10 | const [ fName, lName, email, pass, passConf, ssn ] = '|||||'.split('|'); 11 | signupPage 12 | .navigate() 13 | .signup({ 14 | fName, lName, email, pass, passConf, ssn 15 | }, false); 16 | 17 | signupPage.expect.element('@formError').to.be.visible; 18 | 19 | signupPage 20 | .navigate() 21 | .signup(globals.userData, true); 22 | 23 | loginPage.expect.element('@emailLoginPageInput').to.be.visible; 24 | 25 | client.end(); 26 | } 27 | }; -------------------------------------------------------------------------------- /js-frontend/tests/e2e-tests/test020_Login.js: -------------------------------------------------------------------------------- 1 | import globals from '../e2e-globals/globals'; 2 | 3 | export default { 4 | '@tags': ['login', 'sanity'], 5 | 6 | 'User Logs in': (client) => { 7 | const loginPage = client.page.loginPage(); 8 | const instancesPage = client.page.instancesPage(); 9 | 10 | const [email, pass] = '|'.split('|'); 11 | 12 | loginPage 13 | .navigate() 14 | .login({email, pass}); 15 | 16 | loginPage.expect.element('@formError').to.be.visible; 17 | 18 | loginPage 19 | .navigate() 20 | .login(globals.userData); 21 | 22 | instancesPage.expect.element('@signOutLink').to.be.visible; 23 | 24 | instancesPage 25 | .navigate() 26 | .signOut(); 27 | 28 | client.assert.urlContains('/#/signin'); 29 | 30 | client.end(); 31 | } 32 | }; -------------------------------------------------------------------------------- /prebuilt-web-client/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <meta charset="utf-8"> 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 | 7 | <title>Money Transfer App</title> 8 | <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous"> 9 | 10 | <link rel="stylesheet" href="/style2.css"> 11 | 12 | <!-- inject:app:css --> 13 | <link rel="stylesheet" href="/style.css"> 14 | <!-- endinject --> 15 | 16 | </head> 17 | <body> 18 | <div id="react-app"></div> 19 | 20 | <!-- inject:app:js --> 21 | <script src="/main-3c1aebc49347fa338f9c.js"></script> 22 | <!-- endinject --> 23 | </body> 24 | </html> 25 | -------------------------------------------------------------------------------- /prebuilt-web-client/style.css: -------------------------------------------------------------------------------- 1 | @import url(http://fonts.googleapis.com/css?family=Roboto:300,400,500);*,:after,:before{box-sizing:border-box}#react-app,body,html,main{position:relative;height:100%;min-height:100%}*{margin:0}body,html,main{font-family:Roboto}body{-webkit-tap-highlight-color:transparent;padding-bottom:50px;height:auto}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}.footer-navigation{height:1px}.footer-navigation>.container{height:100%}.footer-navigation>.container>*{top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.page-header{padding-bottom:9px;margin:0 0 20px;border-bottom:1px solid #eee}h1{margin-top:.5em} -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-backend/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile "org.scala-lang:scala-library:2.10.2" 3 | compile project(":common-backend") 4 | compile "net.chrisrichardson.eventstore.client:eventstore-client-event-handling_2.10:$eventStoreClientVersion" 5 | } 6 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/accounts/AccountCommands.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.accounts 2 | 3 | import net.chrisrichardson.eventstore.{Command, EntityId} 4 | 5 | object AccountCommands { 6 | 7 | sealed trait AccountCommand extends Command 8 | 9 | case class OpenAccountCommand(initialBalance : BigDecimal) extends AccountCommand 10 | case class DebitAccountCommand(amount : BigDecimal, transactionId : EntityId) extends AccountCommand 11 | case class CreditAccountCommand(amount : BigDecimal, transactionId : EntityId) extends AccountCommand 12 | 13 | } 14 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/accounts/AccountConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.accounts 2 | 3 | import net.chrisrichardson.eventstore.EventStore 4 | import net.chrisrichardson.eventstore.subscriptions.EnableEventHandlers 5 | import net.chrisrichardson.utils.config.MetricRegistryConfiguration 6 | import org.springframework.context.annotation.{Bean, Configuration, Import} 7 | 8 | @Configuration 9 | @Import(Array(classOf[MetricRegistryConfiguration])) 10 | @EnableEventHandlers 11 | class AccountConfiguration { 12 | 13 | @Bean 14 | def accountService(eventStore : EventStore) = new AccountService()(eventStore) 15 | 16 | 17 | @Bean 18 | def transferWorkflow(eventStore: EventStore): TransferWorkflowAccountHandlers = { 19 | new TransferWorkflowAccountHandlers(eventStore) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/accounts/AccountService.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.accounts 2 | 3 | import net.chrisrichardson.eventstore.EventStore 4 | import net.chrisrichardson.eventstore.examples.bank.accounts.AccountCommands.OpenAccountCommand 5 | import net.chrisrichardson.eventstore.util.ServiceUtil._ 6 | 7 | class AccountService(implicit eventStore : EventStore) { 8 | 9 | def openAccount(initialBalance : BigDecimal) = 10 | newEntity[Account] <== OpenAccountCommand(initialBalance) 11 | 12 | } 13 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/accounts/TransferWorkflowAccountHandlers.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.accounts 2 | 3 | import net.chrisrichardson.eventstore.EventStore 4 | import net.chrisrichardson.eventstore.examples.bank.accounts.AccountCommands.{CreditAccountCommand, DebitAccountCommand} 5 | import net.chrisrichardson.eventstore.examples.bank.backend.common.transactions.{DebitRecordedEvent, MoneyTransferCreatedEvent} 6 | import net.chrisrichardson.eventstore.subscriptions.{EventHandlerMethod, CompoundEventHandler, EventSubscriber} 7 | import net.chrisrichardson.eventstore.util.EventHandlingUtil._ 8 | 9 | @EventSubscriber(id = "accountEventHandlers") 10 | class TransferWorkflowAccountHandlers(eventStore: EventStore) extends CompoundEventHandler { 11 | 12 | implicit val es = eventStore 13 | 14 | @EventHandlerMethod 15 | val performDebit = 16 | handlerForEvent[MoneyTransferCreatedEvent] { de => 17 | existingEntity[Account](de.event.details.fromAccountId) <== 18 | DebitAccountCommand(de.event.details.amount, de.entityId) 19 | } 20 | 21 | @EventHandlerMethod 22 | val performCredit = handlerForEvent[DebitRecordedEvent] { de => 23 | existingEntity[Account](de.event.details.toAccountId) <== 24 | CreditAccountCommand(de.event.details.amount, de.entityId) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-service/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | apply plugin: 'spring-boot' 3 | apply plugin: VerifyEventStoreEnvironmentPlugin 4 | 5 | dependencies { 6 | compile "org.scala-lang:scala-library:2.10.2" 7 | compile project(":accounts-command-side-web") 8 | 9 | compile "org.springframework.boot:spring-boot-starter-web" 10 | compile "org.springframework.boot:spring-boot-starter-actuator" 11 | 12 | compile "net.chrisrichardson.eventstore.client:eventstore-http-stomp-client_2.10:$eventStoreClientVersion" 13 | 14 | testCompile "org.springframework.boot:spring-boot-starter-test" 15 | testCompile scalaTestDependency 16 | 17 | } 18 | 19 | test { 20 | ignoreFailures true 21 | } 22 | 23 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-service/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/AccountsCommandSideServiceConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web 2 | 3 | import net.chrisrichardson.eventstore.client.config.EventStoreHttpClientConfiguration 4 | import net.chrisrichardson.eventstore.examples.bank.web.accounts.CommandSideWebAccountsConfiguration 5 | import net.chrisrichardson.eventstore.json.EventStoreCommonObjectMapping 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration 7 | import org.springframework.boot.autoconfigure.web.HttpMessageConverters 8 | import org.springframework.context.annotation._ 9 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter 10 | 11 | @Configuration 12 | @EnableAutoConfiguration 13 | @Import(Array(classOf[CommandSideWebAccountsConfiguration], classOf[EventStoreHttpClientConfiguration])) 14 | @ComponentScan 15 | class AccountsCommandSideServiceConfiguration { 16 | 17 | @Bean 18 | def scalaJSonConverter: HttpMessageConverters = { 19 | val additional = new MappingJackson2HttpMessageConverter 20 | additional.setObjectMapper(EventStoreCommonObjectMapping.getObjectMapper) 21 | new HttpMessageConverters(additional) 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-service/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/main/AccountsCommandSideServiceMain.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.main 2 | 3 | import net.chrisrichardson.eventstore.examples.bank.web.AccountsCommandSideServiceConfiguration 4 | import org.springframework.boot.SpringApplication 5 | 6 | object AccountsCommandSideServiceMain { 7 | 8 | def main(args: Array[String]) : Unit = SpringApplication.run(classOf[AccountsCommandSideServiceConfiguration], args :_ *) 9 | 10 | } 11 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-service/src/test/scala/net/chrisrichardson/eventstore/examples/bank/web/AccountsCommandSideServiceIntegrationTest.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web 2 | 3 | import net.chrisrichardson.eventstore.examples.bank.web.accounts.controllers.{CreateAccountRequest, CreateAccountResponse} 4 | import org.junit.Assert 5 | import org.junit.runner.RunWith 6 | import org.scalatest.FlatSpec 7 | import org.scalatest.junit.JUnitRunner 8 | import org.springframework.boot.SpringApplication 9 | import org.springframework.web.client.RestTemplate 10 | 11 | @RunWith(classOf[JUnitRunner]) 12 | class AccountsCommandSideServiceIntegrationTest extends FlatSpec { 13 | 14 | val sa = new SpringApplication(classOf[AccountsCommandSideServiceTestConfiguration]) 15 | val ctx = sa.run() 16 | 17 | // var server = ctx.getBean(classOf[EmbeddedServletContainer]) 18 | 19 | val port = 8080 20 | 21 | val baseUrl = s"http://localhost:$port/" 22 | 23 | val restTemplate = ctx.getBean(classOf[RestTemplate]) 24 | 25 | it should "create account" in { 26 | 27 | val CreateAccountResponse(accountId) = restTemplate.postForEntity(s"$baseUrl/accounts", CreateAccountRequest(BigDecimal(500)), classOf[CreateAccountResponse]).getBody 28 | Assert.assertNotNull(accountId) 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-service/src/test/scala/net/chrisrichardson/eventstore/examples/bank/web/AccountsCommandSideServiceTestConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web 2 | 3 | 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import net.chrisrichardson.eventstore.json.EventStoreCommonObjectMapping 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration 7 | import org.springframework.context.annotation.{Bean, Import, Configuration} 8 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter 9 | import org.springframework.web.client.RestTemplate 10 | import scala.collection.JavaConversions._ 11 | 12 | @Configuration 13 | @Import(Array(classOf[AccountsCommandSideServiceConfiguration])) 14 | class AccountsCommandSideServiceTestConfiguration { 15 | 16 | @Bean 17 | def restTemplate() = { 18 | val restTemplate = new RestTemplate() 19 | restTemplate.getMessageConverters foreach { 20 | case mc: MappingJackson2HttpMessageConverter => 21 | mc.setObjectMapper(EventStoreCommonObjectMapping.getObjectMapper) 22 | case _ => 23 | } 24 | restTemplate 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-web/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | 3 | dependencies { 4 | compile "org.scala-lang:scala-library:2.10.2" 5 | compile project(":accounts-command-side-backend") 6 | compile project(":common-web") 7 | 8 | testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" 9 | testCompile scalaTestDependency 10 | } 11 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-web/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/accounts/CommandSideWebAccountsConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.accounts 2 | 3 | import net.chrisrichardson.eventstore.examples.bank.accounts.AccountConfiguration 4 | import org.springframework.context.annotation.{ComponentScan, Configuration, Import} 5 | 6 | @Configuration 7 | @Import(Array(classOf[AccountConfiguration])) 8 | @ComponentScan 9 | class CommandSideWebAccountsConfiguration { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-web/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/accounts/controllers/AccountController.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.accounts.controllers 2 | 3 | import net.chrisrichardson.eventstore.EventStore 4 | import net.chrisrichardson.eventstore.examples.bank.accounts.AccountService 5 | import net.chrisrichardson.eventstore.examples.bank.web.util.WebUtil 6 | import org.springframework.beans.factory.annotation.Autowired 7 | import org.springframework.web.bind.annotation._ 8 | import scala.concurrent.ExecutionContext.Implicits.global 9 | 10 | 11 | @RestController 12 | class AccountController @Autowired() (accountService : AccountService, eventStore : EventStore) { 13 | 14 | @RequestMapping(value=Array("/accounts"), method = Array(RequestMethod.POST)) 15 | def create(@RequestBody request : CreateAccountRequest) = { 16 | val f = accountService.openAccount(request.initialBalance) 17 | WebUtil.toDeferredResult(f map(account => CreateAccountResponse(account.entityId.id))) 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-web/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/accounts/controllers/CreateAccountRequest.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.accounts.controllers 2 | 3 | case class CreateAccountRequest(initialBalance : BigDecimal) 4 | -------------------------------------------------------------------------------- /scala-spring/accounts-command-side-web/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/accounts/controllers/CreateAccountResponse.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.accounts.controllers 2 | 3 | /** 4 | * Created by cer on 7/16/14. 5 | */ 6 | case class CreateAccountResponse(accountId : String) 7 | case class GetAccountResponse(accountId : String, balance : String) 8 | -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-backend/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | 3 | dependencies { 4 | 5 | compile project(":common-backend") 6 | 7 | compile "org.scala-lang:scala-library:2.10.2" 8 | compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion" 9 | 10 | compile "net.chrisrichardson.eventstore.client:eventstore-java-client_2.10:$eventStoreClientVersion" 11 | 12 | testCompile scalaTestDependency 13 | 14 | testCompile "junit:junit:4.11" 15 | testCompile "net.chrisrichardson.eventstore.client:eventstore-jdbc_2.10:$eventStoreClientVersion" 16 | } 17 | -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/queryside/AccountInfo.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.queryside 2 | 3 | import org.springframework.data.mongodb.repository.MongoRepository 4 | 5 | case class AccountInfo(id : String, balance : Long, 6 | changes : java.util.List[AccountChangeInfo], 7 | transactions : java.util.List[AccountTransactionInfo], 8 | version : String) 9 | 10 | case class AccountChangeInfo(changeId : String, transactionId : String, transactionType : String, amount : Long, balanceDelta: Long) 11 | 12 | case class AccountTransactionInfo(transactionId : String, fromAccountId: String, toAccountId: String, amount : Long) 13 | 14 | trait AccountInfoRepository extends MongoRepository[AccountInfo, String] -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/queryside/AccountInfoQueryService.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.queryside 2 | 3 | import net.chrisrichardson.eventstore.EntityId 4 | 5 | class AccountInfoQueryService(accountInfoRepository : AccountInfoRepository) { 6 | 7 | def findByAccountId(accountId : EntityId) : AccountInfo = { 8 | val account = accountInfoRepository.findOne(accountId.id) 9 | if (account == null) 10 | throw new AccountNotFoundException(accountId) 11 | else 12 | account 13 | } 14 | 15 | } 16 | 17 | class AccountNotFoundException(accountId : EntityId) extends RuntimeException("Account not found " + accountId) -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/queryside/QuerySideConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.queryside 2 | 3 | import org.springframework.context.annotation.{Bean, Configuration} 4 | import org.springframework.data.mongodb.core.MongoTemplate 5 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories 6 | 7 | @Configuration 8 | @EnableMongoRepositories 9 | class QuerySideConfiguration { 10 | 11 | @Bean 12 | def accountUpdateService(accountInfoRepository: AccountInfoRepository, mongoTemplate: MongoTemplate): AccountInfoUpdateService = 13 | new AccountInfoUpdateService(accountInfoRepository, mongoTemplate) 14 | 15 | @Bean 16 | def accountInfoQueryService(accountInfoRepository : AccountInfoRepository) = new AccountInfoQueryService(accountInfoRepository) 17 | 18 | @Bean 19 | def querysideDependencyChecker(mongoTemplate : MongoTemplate) = new QuerysideDependencyChecker(mongoTemplate) 20 | } 21 | -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/queryside/QuerysideDependencyChecker.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.queryside 2 | 3 | import javax.annotation.PostConstruct 4 | 5 | import net.chrisrichardson.utils.logging.Logging 6 | import org.springframework.beans.factory.annotation.Autowired 7 | import org.springframework.data.mongodb.core.MongoTemplate 8 | 9 | import scala.concurrent.{TimeoutException, Await, Future} 10 | import scala.concurrent.duration._ 11 | import scala.concurrent.ExecutionContext.Implicits.global 12 | 13 | class QuerysideDependencyChecker (mongoTemplate : MongoTemplate) extends Logging { 14 | 15 | @PostConstruct 16 | def checkDependencies(): Unit = { 17 | try { 18 | Await.result(Future { mongoTemplate.getDb.getCollectionNames}, 5 seconds) 19 | } catch { 20 | case e : Throwable => 21 | logger.error("Error connecting to Mongo - have you set SPRING_DATA_MONGODB_URI or --spring.data.mongodb_uri?", e) 22 | throw e 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-service/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | apply plugin: 'spring-boot' 3 | apply plugin: VerifyMongoDBConfigurationPlugin 4 | apply plugin: VerifyEventStoreEnvironmentPlugin 5 | 6 | dependencies { 7 | compile "org.scala-lang:scala-library:2.10.2" 8 | compile project(":accounts-query-side-web") 9 | 10 | compile "org.springframework.boot:spring-boot-starter-web" 11 | compile "org.springframework.boot:spring-boot-starter-actuator" 12 | 13 | compile "net.chrisrichardson.eventstore.client:eventstore-http-stomp-client_2.10:$eventStoreClientVersion" 14 | 15 | testCompile "org.springframework.boot:spring-boot-starter-test" 16 | testCompile scalaTestDependency 17 | 18 | } 19 | 20 | test { 21 | ignoreFailures true 22 | } 23 | 24 | -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-service/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/AccountsQuerySideServiceConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web 2 | 3 | import net.chrisrichardson.eventstore.client.config.EventStoreHttpClientConfiguration 4 | import net.chrisrichardson.eventstore.examples.bank.web.queryside.QuerySideWebConfiguration 5 | import net.chrisrichardson.eventstore.json.EventStoreCommonObjectMapping 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration 7 | import org.springframework.boot.autoconfigure.web.HttpMessageConverters 8 | import org.springframework.context.annotation._ 9 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter 10 | 11 | @Configuration 12 | @EnableAutoConfiguration 13 | @Import(Array(classOf[QuerySideWebConfiguration], classOf[EventStoreHttpClientConfiguration])) 14 | @ComponentScan 15 | class AccountsQuerySideServiceConfiguration { 16 | 17 | @Bean 18 | def scalaJSonConverter: HttpMessageConverters = { 19 | val additional = new MappingJackson2HttpMessageConverter 20 | additional.setObjectMapper(EventStoreCommonObjectMapping.getObjectMapper) 21 | new HttpMessageConverters(additional) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-service/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/main/AccountsQuerySideServiceMain.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.main 2 | 3 | import net.chrisrichardson.eventstore.examples.bank.web.AccountsQuerySideServiceConfiguration 4 | import org.springframework.boot.SpringApplication 5 | 6 | object AccountsQuerySideServiceMain { 7 | 8 | def main(args: Array[String]) : Unit = SpringApplication.run(classOf[AccountsQuerySideServiceConfiguration], args :_ *) 9 | 10 | } 11 | -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-service/src/test/scala/net/chrisrichardson/eventstore/examples/bank/web/AccountsQuerySideServiceIntegrationTest.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FlatSpec 5 | import org.scalatest.concurrent.Eventually._ 6 | import org.scalatest.junit.JUnitRunner 7 | import org.scalatest.time.{Millis, Span} 8 | import org.springframework.boot.SpringApplication 9 | import org.springframework.web.client.RestTemplate 10 | 11 | @RunWith(classOf[JUnitRunner]) 12 | class AccountsQuerySideServiceIntegrationTest extends FlatSpec { 13 | 14 | val sa = new SpringApplication(classOf[AccountsQuerySideServiceTestConfiguration]) 15 | val ctx = sa.run() 16 | 17 | // var server = ctx.getBean(classOf[EmbeddedServletContainer]) 18 | 19 | val port = 8080 20 | 21 | val baseUrl = s"http://localhost:$port/" 22 | 23 | val restTemplate = ctx.getBean(classOf[RestTemplate]) 24 | 25 | implicit val reallyLongPatienceConfig = PatienceConfig(timeout = Span(10 * 1000, Millis), interval = Span(1 * 1000, Millis)) 26 | 27 | it should "create accounts and transfer money" in { 28 | // FIXME 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-service/src/test/scala/net/chrisrichardson/eventstore/examples/bank/web/AccountsQuerySideServiceTestConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web 2 | 3 | 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import net.chrisrichardson.eventstore.json.EventStoreCommonObjectMapping 6 | import org.springframework.context.annotation.{Bean, Configuration, Import} 7 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter 8 | import org.springframework.web.client.RestTemplate 9 | 10 | import scala.collection.JavaConversions._ 11 | 12 | @Configuration 13 | @Import(Array(classOf[AccountsQuerySideServiceConfiguration])) 14 | class AccountsQuerySideServiceTestConfiguration { 15 | 16 | @Bean 17 | def restTemplate() = { 18 | val restTemplate = new RestTemplate() 19 | restTemplate.getMessageConverters foreach { 20 | case mc: MappingJackson2HttpMessageConverter => 21 | mc.setObjectMapper(EventStoreCommonObjectMapping.getObjectMapper) 22 | case _ => 23 | } 24 | restTemplate 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-web/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | 3 | dependencies { 4 | compile "org.scala-lang:scala-library:2.10.2" 5 | compile project(":accounts-query-side-backend") 6 | compile project(":common-web") 7 | 8 | compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" 9 | 10 | testCompile "org.springframework.boot:spring-boot-starter-test" 11 | testCompile scalaTestDependency 12 | 13 | } 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-web/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/queryside/QuerySideWebConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.queryside 2 | 3 | import net.chrisrichardson.eventstore.examples.bank.queryside.QuerySideConfiguration 4 | import net.chrisrichardson.eventstore.subscriptions.EnableEventHandlers 5 | import org.springframework.context.annotation._ 6 | 7 | @Configuration 8 | @Import(Array(classOf[QuerySideConfiguration])) 9 | @ComponentScan 10 | @EnableEventHandlers 11 | class QuerySideWebConfiguration { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /scala-spring/accounts-query-side-web/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/queryside/controllers/AccountQuerySideController.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.queryside.controllers 2 | 3 | import net.chrisrichardson.eventstore.EntityId 4 | import net.chrisrichardson.eventstore.examples.bank.queryside.{AccountNotFoundException, AccountInfoQueryService} 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.http.HttpStatus 7 | import org.springframework.web.bind.annotation._ 8 | 9 | @RestController 10 | class AccountQuerySideController @Autowired() (accountInfoQueryService : AccountInfoQueryService) { 11 | 12 | @RequestMapping(value=Array("/accounts/{accountId}"), method = Array(RequestMethod.GET)) 13 | def get(@PathVariable accountId : String) = accountInfoQueryService.findByAccountId(EntityId(accountId)) 14 | 15 | @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "account not found") 16 | @ExceptionHandler(Array(classOf[AccountNotFoundException])) 17 | def accountNotFound() {} 18 | } 19 | -------------------------------------------------------------------------------- /scala-spring/backend-integration-tests/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | apply plugin: VerifyMongoDBConfigurationPlugin 3 | 4 | dependencies { 5 | 6 | compile "org.scala-lang:scala-library:2.10.2" 7 | 8 | compile project(":transactions-command-side-backend") 9 | compile project(":accounts-command-side-backend") 10 | compile project(":accounts-query-side-backend") 11 | 12 | testCompile scalaTestDependency 13 | 14 | testCompile "junit:junit:4.11" 15 | testCompile "net.chrisrichardson.eventstore.client:eventstore-jdbc_2.10:$eventStoreClientVersion" 16 | } 17 | 18 | 19 | -------------------------------------------------------------------------------- /scala-spring/backend-integration-tests/src/test/scala/net/chrisrichardson/eventstore/examples/bank/config/BankingTestConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.config 2 | 3 | import net.chrisrichardson.eventstore.examples.bank.accounts.AccountConfiguration 4 | import net.chrisrichardson.eventstore.examples.bank.queryside.QuerySideConfiguration 5 | import net.chrisrichardson.eventstore.examples.bank.transactions.TransactionConfiguration 6 | import net.chrisrichardson.eventstore.jdbc.config.JdbcEventStoreConfiguration 7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration 8 | import org.springframework.context.annotation.{Import, Configuration} 9 | 10 | @Configuration 11 | @EnableAutoConfiguration 12 | @Import(Array(classOf[JdbcEventStoreConfiguration], classOf[AccountConfiguration], classOf[TransactionConfiguration], classOf[QuerySideConfiguration])) 13 | class BankingTestConfiguration { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /scala-spring/build-and-test-all.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | ../_build-and-test-all.sh $* 4 | -------------------------------------------------------------------------------- /scala-spring/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | dependencies { 6 | classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion") 7 | } 8 | } 9 | 10 | allprojects { 11 | group = "net.chrisrichardson.eventstore" 12 | } 13 | 14 | 15 | task wrapper(type: Wrapper) { 16 | gradleVersion = '2.0' 17 | } 18 | 19 | subprojects { 20 | apply plugin: 'java' 21 | apply plugin: 'scala' 22 | sourceCompatibility = 1.7 23 | targetCompatibility = 1.7 24 | 25 | repositories { 26 | mavenCentral() 27 | eventuateMavenRepoUrl.split(',').each { repoUrl -> maven { url repoUrl } } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /scala-spring/buildSrc/src/main/groovy/VerifyEventStoreEnvironmentPlugin.groovy: -------------------------------------------------------------------------------- 1 | import org.gradle.api.* 2 | 3 | 4 | class VerifyEventStoreEnvironmentPlugin implements Plugin<Project> { 5 | void apply(Project project) { 6 | project.test { 7 | beforeSuite { x -> 8 | if (x.parent == null) { 9 | if (System.getenv("EVENTUATE_API_KEY_ID") == null && System.getenv("EVENTUATE_API_KEY_SECRET") == null) 10 | logger.warn("\nPLEASE make sure that Eventuate-related environment variables EVENTUATE_API_KEY_ID and EVENTUATE_API_KEY_SECRET are set, see sample-set-remote-env.sh !!!!\n") 11 | } 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /scala-spring/buildSrc/src/main/groovy/VerifyMongoDBConfigurationPlugin.groovy: -------------------------------------------------------------------------------- 1 | import org.gradle.api.* 2 | 3 | 4 | class VerifyMongoDBConfigurationPlugin implements Plugin<Project> { 5 | void apply(Project project) { 6 | project.test { 7 | beforeSuite { x -> 8 | if (x.parent == null) { 9 | if (System.getenv("SPRING_DATA_MONGODB_URI") == null) 10 | throw new RuntimeException("Please make sure that the environment variable SPRING_DATA_MONGODB_URI is set, e.g. export SPRING_DATA_MONGODB_URI=mongodb://192.168.59.103/mydb") 11 | } 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /scala-spring/common-backend/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | 3 | dependencies { 4 | compile "org.scala-lang:scala-library:2.10.2" 5 | compile "net.chrisrichardson.eventstore.client:eventstore-java-client_2.10:$eventStoreClientVersion" 6 | 7 | testCompile scalaTestDependency 8 | 9 | testCompile "junit:junit:4.11" 10 | } 11 | -------------------------------------------------------------------------------- /scala-spring/common-backend/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <configuration> 3 | 4 | <!-- [%thread] --> 5 | 6 | <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 7 | <layout class="ch.qos.logback.classic.PatternLayout"> 8 | <Pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</Pattern> 9 | </layout> 10 | </appender> 11 | 12 | <root level="error"> 13 | <appender-ref ref="STDOUT" /> 14 | </root> 15 | 16 | <logger name="org.springframework" level='info'> 17 | </logger> 18 | 19 | <logger name="net.chrisrichardson.eventstore.client" level='info'> 20 | </logger> 21 | 22 | </configuration> 23 | -------------------------------------------------------------------------------- /scala-spring/common-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/backend/common/accounts/AccountEvents.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.backend.common.accounts 2 | 3 | import net.chrisrichardson.eventstore.{EntityId, Event} 4 | 5 | trait AccountChangedEvent extends Event { 6 | val amount : BigDecimal 7 | val transactionId : EntityId 8 | } 9 | 10 | case class AccountOpenedEvent(initialBalance : BigDecimal) extends Event 11 | 12 | case class AccountCreditedEvent(amount : BigDecimal, transactionId : EntityId) extends AccountChangedEvent 13 | 14 | case class AccountDebitedEvent(amount : BigDecimal, transactionId : EntityId) extends AccountChangedEvent 15 | 16 | case class AccountDebitFailedDueToInsufficientFundsEvent(amount : BigDecimal, transactionId : EntityId) extends AccountChangedEvent 17 | 18 | 19 | -------------------------------------------------------------------------------- /scala-spring/common-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/backend/common/accounts/package-info.java: -------------------------------------------------------------------------------- 1 | @net.chrisrichardson.eventstore.EventEntity(entity="net.chrisrichardson.eventstore.examples.bank.accounts.Account") 2 | package net.chrisrichardson.eventstore.examples.bank.backend.common.accounts; -------------------------------------------------------------------------------- /scala-spring/common-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/backend/common/transactions/TransactionEvents.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.backend.common.transactions 2 | 3 | import net.chrisrichardson.eventstore.Event 4 | 5 | case class MoneyTransferCreatedEvent(details : TransferDetails) extends Event 6 | case class DebitRecordedEvent(details : TransferDetails) extends Event 7 | case class CreditRecordedEvent(details : TransferDetails) extends Event 8 | case class TransferFailedDueToInsufficientFundsEvent() extends Event 9 | -------------------------------------------------------------------------------- /scala-spring/common-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/backend/common/transactions/TransferDetails.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.backend.common.transactions 2 | 3 | import net.chrisrichardson.eventstore.EntityId 4 | 5 | 6 | case class TransferDetails(fromAccountId : EntityId, toAccountId : EntityId, amount : BigDecimal) 7 | -------------------------------------------------------------------------------- /scala-spring/common-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/backend/common/transactions/package-info.java: -------------------------------------------------------------------------------- 1 | @net.chrisrichardson.eventstore.EventEntity(entity="net.chrisrichardson.eventstore.examples.bank.transactions.MoneyTransfer") 2 | package net.chrisrichardson.eventstore.examples.bank.backend.common.transactions; -------------------------------------------------------------------------------- /scala-spring/common-web/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | 3 | dependencies { 4 | compile "org.scala-lang:scala-library:2.10.2" 5 | compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" 6 | 7 | } 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /scala-spring/common-web/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/util/WebUtil.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.util 2 | 3 | import org.springframework.web.context.request.async.DeferredResult 4 | 5 | import scala.concurrent.ExecutionContext.Implicits.global 6 | import scala.concurrent.Future 7 | 8 | object WebUtil { 9 | def toDeferredResult[T](future: Future[T]): DeferredResult[T] = { 10 | val result = new DeferredResult[T] 11 | future onSuccess { 12 | case r => result.setResult(r) 13 | } 14 | future onFailure { 15 | case t => result.setErrorResult(t) 16 | } 17 | result 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /scala-spring/docker-compose.yml: -------------------------------------------------------------------------------- 1 | accountscommandside: 2 | image: java:8 3 | working_dir: /app 4 | volumes: 5 | - ./accounts-command-side-service/build/libs:/app 6 | command: java -jar /app/accounts-command-side-service.jar 7 | ports: 8 | - "8080:8080" 9 | environment: 10 | EVENTUATE_API_KEY_ID: 11 | EVENTUATE_API_KEY_SECRET: 12 | 13 | transactionscommandside: 14 | image: java:8 15 | working_dir: /app 16 | volumes: 17 | - ./transactions-command-side-service/build/libs:/app 18 | command: java -jar /app/transactions-command-side-service.jar 19 | ports: 20 | - "8082:8080" 21 | environment: 22 | EVENTUATE_API_KEY_ID: 23 | EVENTUATE_API_KEY_SECRET: 24 | 25 | 26 | accountsqueryside: 27 | image: java:8 28 | working_dir: /app 29 | volumes: 30 | - ./accounts-query-side-service/build/libs:/app 31 | command: java -jar /app/accounts-query-side-service.jar 32 | ports: 33 | - "8081:8080" 34 | links: 35 | - mongodb 36 | environment: 37 | EVENTUATE_API_KEY_ID: 38 | EVENTUATE_API_KEY_SECRET: 39 | SPRING_DATA_MONGODB_URI: mongodb://mongodb/mydb 40 | 41 | mongodb: 42 | image: mongo:3.0.4 43 | hostname: mongodb 44 | command: mongod --smallfiles 45 | ports: 46 | - "27017:27017" 47 | -------------------------------------------------------------------------------- /scala-spring/e2e-test/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | apply plugin: VerifyMongoDBConfigurationPlugin 3 | 4 | dependencies { 5 | compile "org.scala-lang:scala-library:2.10.2" 6 | 7 | testCompile project(":accounts-command-side-web") 8 | testCompile project(":transactions-command-side-web") 9 | testCompile project(":accounts-query-side-web") 10 | 11 | testCompile "junit:junit:4.11" 12 | testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" 13 | testCompile scalaTestDependency 14 | 15 | } 16 | 17 | test { 18 | ignoreFailures (!project.hasProperty("ignoreE2EFailures") || ignoreE2EFailures.toBoolean()) 19 | } 20 | -------------------------------------------------------------------------------- /scala-spring/gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | org.gradle.jvmargs=-XX:MaxPermSize=512m 3 | 4 | eventuateMavenRepoUrl=http://mavenrepo.eventuate.io/release 5 | 6 | scalaTestDependency=org.scalatest:scalatest_2.10:2.0 7 | 8 | springBootVersion=1.2.8.RELEASE 9 | 10 | eventStoreClientVersion=0.12 11 | -------------------------------------------------------------------------------- /scala-spring/gradle/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cer/event-sourcing-examples/7dcafe5deabb6cd55c414eaa611e8537ea39df2e/scala-spring/gradle/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /scala-spring/gradle/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jan 03 13:06:18 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.0-bin.zip 7 | -------------------------------------------------------------------------------- /scala-spring/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cer/event-sourcing-examples/7dcafe5deabb6cd55c414eaa611e8537ea39df2e/scala-spring/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /scala-spring/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jan 03 13:36:23 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-2.0-all.zip 7 | -------------------------------------------------------------------------------- /scala-spring/handy-curl-commands.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -e 2 | 3 | 4 | # Create account 1 5 | 6 | account1=$(curl -v --data '{"initialBalance" : 500}' -H "content-type: application/json" http://localhost:8080/accounts) 7 | 8 | # {"accountId":"0000014ae4caf314-ae7453bbb71e0000"} 9 | 10 | curl -v http://localhost:8081/accounts/0000014ae4caf314-ae7453bbb71e0000 11 | 12 | # {"accountId":"0000014ae4caf314-ae7453bbb71e0000","balance":50000} 13 | 14 | # Create account 2 15 | 16 | account2=$(curl -v --data '{"initialBalance" : 300}' -H "content-type: application/json" http://localhost:8080/accounts) 17 | 18 | # {"accountId":"0000014ae4cc8415-ae7453bbb71e0000"} 19 | 20 | curl -v http://localhost:8081/accounts/0000014ae4cc8415-ae7453bbb71e0000 21 | 22 | # 23 | 24 | 25 | transfer=$(curl -v --data '{"amount" : 150, "fromAccountId" : "0000014ae4caf314-ae7453bbb71e0000", "toAccountId" : "0000014ae4cc8415-ae7453bbb71e0000"}' -H "content-type: application/json" http://localhost:8082/transfers) 26 | 27 | # {"moneyTransferId":"0000014ae4cef030-ae7453bbb71e0000"} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /scala-spring/monolithic-service/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | apply plugin: 'spring-boot' 3 | apply plugin: VerifyMongoDBConfigurationPlugin 4 | 5 | dependencies { 6 | compile "org.scala-lang:scala-library:2.10.2" 7 | compile project(":accounts-command-side-web") 8 | compile project(":transactions-command-side-web") 9 | compile project(":accounts-query-side-web") 10 | 11 | compile "org.springframework.boot:spring-boot-starter-web" 12 | compile "org.springframework.boot:spring-boot-starter-actuator" 13 | 14 | compile "net.chrisrichardson.eventstore.client:eventstore-jdbc_2.10:$eventStoreClientVersion" 15 | 16 | testCompile "org.springframework.boot:spring-boot-starter-test" 17 | testCompile scalaTestDependency 18 | 19 | } 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /scala-spring/monolithic-service/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/main/BankingMain.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.main 2 | 3 | import net.chrisrichardson.eventstore.examples.bank.web.BankingWebAppConfiguration 4 | import org.springframework.boot.SpringApplication 5 | 6 | object BankingMain { 7 | 8 | def main(args: Array[String]) : Unit = SpringApplication.run(classOf[BankingWebAppConfiguration], args :_ *) 9 | 10 | } 11 | -------------------------------------------------------------------------------- /scala-spring/monolithic-service/src/test/scala/net/chrisrichardson/eventstore/examples/bank/web/BankingWebAppTestConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web 2 | 3 | 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import net.chrisrichardson.eventstore.json.EventStoreCommonObjectMapping 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration 7 | import org.springframework.context.annotation.{Bean, Import, Configuration} 8 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter 9 | import org.springframework.web.client.RestTemplate 10 | import scala.collection.JavaConversions._ 11 | 12 | @Configuration 13 | @Import(Array(classOf[BankingWebAppConfiguration])) 14 | class BankingWebAppTestConfiguration { 15 | 16 | @Bean 17 | def restTemplate() = { 18 | val restTemplate = new RestTemplate() 19 | restTemplate.getMessageConverters foreach { 20 | case mc: MappingJackson2HttpMessageConverter => 21 | mc.setObjectMapper(EventStoreCommonObjectMapping.getObjectMapper) 22 | case _ => 23 | } 24 | restTemplate 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /scala-spring/settings.gradle: -------------------------------------------------------------------------------- 1 | include 'common-web' 2 | 3 | include 'common-backend' 4 | 5 | include 'accounts-command-side-backend' 6 | include 'transactions-command-side-backend' 7 | include 'accounts-command-side-web' 8 | include 'transactions-command-side-web' 9 | 10 | 11 | include 'accounts-query-side-backend' 12 | include 'accounts-query-side-web' 13 | 14 | include 'backend-integration-tests' 15 | 16 | include 'monolithic-service' 17 | include 'accounts-command-side-service' 18 | include 'accounts-query-side-service' 19 | include 'transactions-command-side-service' 20 | 21 | include 'e2e-test' 22 | 23 | rootProject.name = 'scala-spring-event-sourcing-example' 24 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-backend/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile "org.scala-lang:scala-library:2.10.2" 3 | compile project(":common-backend") 4 | compile "net.chrisrichardson.eventstore.client:eventstore-client-event-handling_2.10:$eventStoreClientVersion" 5 | } 6 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/transactions/MoneyTransferCommands.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.transactions 2 | 3 | import net.chrisrichardson.eventstore.{Command, EntityId} 4 | import net.chrisrichardson.eventstore.examples.bank.backend.common.transactions.TransferDetails 5 | 6 | object MoneyTransferCommands { 7 | 8 | sealed trait MoneyTransferCommand extends Command 9 | 10 | case class CreateMoneyTransferCommand(details : TransferDetails) extends MoneyTransferCommand 11 | case class RecordDebitCommand(accountId : EntityId) extends MoneyTransferCommand 12 | case class RecordDebitFailedDueToInsufficientFundsCommand(accountId : EntityId) extends MoneyTransferCommand 13 | case class RecordCreditCommand(accountId : EntityId) extends MoneyTransferCommand 14 | 15 | } 16 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/transactions/MoneyTransferService.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.transactions 2 | 3 | import net.chrisrichardson.eventstore.EventStore 4 | import net.chrisrichardson.eventstore.examples.bank.backend.common.transactions.TransferDetails 5 | import net.chrisrichardson.eventstore.examples.bank.transactions.MoneyTransferCommands.CreateMoneyTransferCommand 6 | import net.chrisrichardson.eventstore.util.ServiceUtil._ 7 | 8 | class MoneyTransferService(implicit eventStore : EventStore) { 9 | 10 | def transferMoney(transferDetails : TransferDetails) = 11 | newEntity[MoneyTransfer] <== CreateMoneyTransferCommand(transferDetails) 12 | 13 | } 14 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-backend/src/main/scala/net/chrisrichardson/eventstore/examples/bank/transactions/TransactionConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.transactions 2 | 3 | import net.chrisrichardson.eventstore.EventStore 4 | import net.chrisrichardson.eventstore.subscriptions.EnableEventHandlers 5 | import org.springframework.context.annotation.{Bean, Configuration} 6 | 7 | @Configuration 8 | @EnableEventHandlers 9 | class TransactionConfiguration { 10 | 11 | @Bean 12 | def accountTransactionService(eventStore : EventStore) = new MoneyTransferService()(eventStore) 13 | 14 | @Bean 15 | def moneyTransferWorkflow(eventStore: EventStore): MoneyTransferEventHandlers = { 16 | new MoneyTransferEventHandlers()(eventStore) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-service/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | apply plugin: 'spring-boot' 3 | apply plugin: VerifyEventStoreEnvironmentPlugin 4 | 5 | dependencies { 6 | compile "org.scala-lang:scala-library:2.10.2" 7 | compile project(":transactions-command-side-web") 8 | 9 | compile "org.springframework.boot:spring-boot-starter-web" 10 | compile "org.springframework.boot:spring-boot-starter-actuator" 11 | 12 | compile "net.chrisrichardson.eventstore.client:eventstore-http-stomp-client_2.10:$eventStoreClientVersion" 13 | 14 | testCompile "org.springframework.boot:spring-boot-starter-test" 15 | testCompile scalaTestDependency 16 | 17 | } 18 | 19 | test { 20 | ignoreFailures true 21 | } 22 | 23 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-service/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/TransactionsCommandSideServiceConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web 2 | 3 | import net.chrisrichardson.eventstore.client.config.EventStoreHttpClientConfiguration 4 | import net.chrisrichardson.eventstore.examples.bank.web.transactions.CommandSideWebTransactionsConfiguration 5 | import net.chrisrichardson.eventstore.json.EventStoreCommonObjectMapping 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration 7 | import org.springframework.boot.autoconfigure.web.HttpMessageConverters 8 | import org.springframework.context.annotation._ 9 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter 10 | 11 | @Configuration 12 | @EnableAutoConfiguration 13 | @Import(Array(classOf[CommandSideWebTransactionsConfiguration], classOf[EventStoreHttpClientConfiguration])) 14 | @ComponentScan 15 | class TransactionsCommandSideServiceConfiguration { 16 | 17 | @Bean 18 | def scalaJSonConverter: HttpMessageConverters = { 19 | val additional = new MappingJackson2HttpMessageConverter 20 | additional.setObjectMapper(EventStoreCommonObjectMapping.getObjectMapper) 21 | new HttpMessageConverters(additional) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-service/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/main/TransactionsCommandSideServiceMain.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.main 2 | 3 | import net.chrisrichardson.eventstore.examples.bank.web.TransactionsCommandSideServiceConfiguration 4 | import org.springframework.boot.SpringApplication 5 | 6 | object TransactionsCommandSideServiceMain { 7 | 8 | def main(args: Array[String]) : Unit = SpringApplication.run(classOf[TransactionsCommandSideServiceConfiguration], args :_ *) 9 | 10 | } 11 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-service/src/test/scala/net/chrisrichardson/eventstore/examples/bank/web/TransactionsCommandSideServiceIntegrationTest.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.FlatSpec 5 | import org.scalatest.concurrent.Eventually._ 6 | import org.scalatest.junit.JUnitRunner 7 | import org.scalatest.time.{Millis, Span} 8 | import org.springframework.boot.SpringApplication 9 | import org.springframework.web.client.RestTemplate 10 | 11 | @RunWith(classOf[JUnitRunner]) 12 | class TransactionsCommandSideServiceIntegrationTest extends FlatSpec { 13 | 14 | val sa = new SpringApplication(classOf[TransactionsCommandSideServiceTestConfiguration]) 15 | val ctx = sa.run() 16 | 17 | // var server = ctx.getBean(classOf[EmbeddedServletContainer]) 18 | 19 | val port = 8080 20 | 21 | val baseUrl = s"http://localhost:$port/" 22 | 23 | val restTemplate = ctx.getBean(classOf[RestTemplate]) 24 | 25 | implicit val reallyLongPatienceConfig = PatienceConfig(timeout = Span(10 * 1000, Millis), interval = Span(1 * 1000, Millis)) 26 | 27 | it should "create accounts and transfer money" in { 28 | // FIXME 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-service/src/test/scala/net/chrisrichardson/eventstore/examples/bank/web/TransactionsCommandSideServiceTestConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web 2 | 3 | 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import net.chrisrichardson.eventstore.json.EventStoreCommonObjectMapping 6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration 7 | import org.springframework.context.annotation.{Bean, Import, Configuration} 8 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter 9 | import org.springframework.web.client.RestTemplate 10 | import scala.collection.JavaConversions._ 11 | 12 | @Configuration 13 | @Import(Array(classOf[TransactionsCommandSideServiceConfiguration])) 14 | class TransactionsCommandSideServiceTestConfiguration { 15 | 16 | @Bean 17 | def restTemplate() = { 18 | val restTemplate = new RestTemplate() 19 | restTemplate.getMessageConverters foreach { 20 | case mc: MappingJackson2HttpMessageConverter => 21 | mc.setObjectMapper(EventStoreCommonObjectMapping.getObjectMapper) 22 | case _ => 23 | } 24 | restTemplate 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-web/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'scala' 2 | 3 | dependencies { 4 | compile "org.scala-lang:scala-library:2.10.2" 5 | compile project(":transactions-command-side-backend") 6 | compile project(":common-web") 7 | 8 | testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" 9 | testCompile scalaTestDependency 10 | } 11 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-web/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/transactions/CommandSideWebTransactionsConfiguration.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.transactions 2 | 3 | import net.chrisrichardson.eventstore.examples.bank.transactions.TransactionConfiguration 4 | import org.springframework.context.annotation.{ComponentScan, Configuration, Import} 5 | 6 | @Configuration 7 | @Import(Array(classOf[TransactionConfiguration])) 8 | @ComponentScan 9 | class CommandSideWebTransactionsConfiguration { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /scala-spring/transactions-command-side-web/src/main/scala/net/chrisrichardson/eventstore/examples/bank/web/transactions/controllers/CreateMoneyTransferResponse.scala: -------------------------------------------------------------------------------- 1 | package net.chrisrichardson.eventstore.examples.bank.web.transactions.controllers 2 | 3 | import net.chrisrichardson.eventstore.EntityId 4 | import net.chrisrichardson.eventstore.examples.bank.transactions.TransferStates 5 | 6 | case class CreateMoneyTransferResponse(transactionId : String) 7 | 8 | case class MoneyTransferRequest(fromAccountId : EntityId, toAccountId : EntityId, amount: BigDecimal) 9 | 10 | case class GetMoneyTransferResponse(transactionId : String, status : String) 11 | 12 | -------------------------------------------------------------------------------- /wait-for-docker-services.sh: -------------------------------------------------------------------------------- 1 | while [[ true ]]; do 2 | nc -z -w 4 ${SERVICE_HOST?} 8080 && nc -z -w 4 ${SERVICE_HOST?} 8081 && nc -z -w 4 ${SERVICE_HOST?} 8082 3 | if [[ "$?" -eq "0" ]]; then 4 | echo connected 5 | break 6 | fi 7 | echo -n . 8 | sleep 1 9 | done -------------------------------------------------------------------------------- /wait-for-services.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | done=false 4 | 5 | host=$1 6 | shift 7 | ports=$* 8 | 9 | while [[ "$done" = false ]]; do 10 | for port in $ports; do 11 | curl -q http://${host}:${port}/health >& /dev/null 12 | if [[ "$?" -eq "0" ]]; then 13 | done=true 14 | else 15 | done=false 16 | break 17 | fi 18 | done 19 | if [[ "$done" = true ]]; then 20 | echo connected 21 | break; 22 | fi 23 | #curl -q http://${1?}:8080/health >& /dev/null && curl -q http://${1?}:8081/health >& /dev/null && curl -q http://${1?}:8082/health >& /dev/null 24 | echo -n . 25 | sleep 1 26 | done 27 | --------------------------------------------------------------------------------