├── .gitignore
├── .mvn
└── wrapper
│ ├── MavenWrapperDownloader.java
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── AUTHORS
├── CHANGELOG.md
├── CONTRIBUTING.md
├── ESArchSmokeTest.postman_collection.json
├── LICENSE
├── NOTICE
├── README.md
├── bigpave-pcf.sh
├── bigpave-pws.sh
├── ci
├── pipeline.yml
├── run-tests.sh
├── tasks
│ ├── archive
│ │ ├── package-command-side.sh
│ │ ├── package-command-side.yml
│ │ ├── package-query-side.sh
│ │ ├── package-query-side.yml
│ │ ├── unit-test-command-side.sh
│ │ ├── unit-test-command-side.yml
│ │ ├── unit-test-common.sh
│ │ ├── unit-test-common.yml
│ │ ├── unit-test-query-side.sh
│ │ └── unit-test-query-side.yml
│ ├── build-trader-app.sh
│ ├── build-trader-app.yml
│ ├── build-trader-ui.sh
│ ├── build-trader-ui.yml
│ ├── build-trading-engine.sh
│ ├── build-trading-engine.yml
│ ├── e2e-test-both-sides.sh
│ ├── e2e-test-both-sides.yml
│ ├── smoke-test-trader-app.sh
│ ├── smoke-test-trader-app.yml
│ ├── smoke-test-trading-engine.sh
│ └── smoke-test-trading-engine.yml
└── tips.txt
├── config-server-setup.json
├── deploy-backend.sh
├── deploy-frontend.sh
├── discovery-server
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── discoveryserver
│ │ │ └── DiscoveryServerApplication.java
│ └── resources
│ │ └── application.properties
│ └── test
│ └── java
│ └── com
│ └── example
│ └── discoveryserver
│ └── DiscoveryServerApplicationTests.java
├── http-api.http
├── images
├── AxonTrader-UI-001.png
└── CQRS+EventSourcing-on-CloudFoundry.jpg
├── mvnw
├── mvnw.cmd
├── pave.sh
├── pom.xml
├── teardown.sh
├── trader-app-ui
├── .eslintignore
├── .eslintrc
├── .gitignore
├── README.md
├── Staticfile
├── manifest.yml
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
└── src
│ ├── actions
│ ├── company.js
│ ├── company.test.js
│ ├── home.js
│ ├── home.test.js
│ ├── portfolio.js
│ └── portfolio.test.js
│ ├── app.scss
│ ├── assets
│ ├── home-background.png
│ └── home-foreground-image.png
│ ├── components
│ ├── Company
│ │ ├── BuyOrder.js
│ │ ├── Company.js
│ │ ├── CompanyList.js
│ │ ├── DataTable.js
│ │ ├── SellOrder.js
│ │ └── styles.css
│ ├── Dashboard
│ │ ├── Portfolio.js
│ │ ├── TradeItemsContainer.js
│ │ ├── TradeItemsTable.js
│ │ ├── Transactions.js
│ │ ├── index.js
│ │ └── styles.css
│ ├── Home
│ │ ├── Banner.js
│ │ ├── CredentialsTable.js
│ │ ├── Header.js
│ │ ├── SideBar.js
│ │ ├── SideBarItem.js
│ │ ├── credentials.js
│ │ ├── img
│ │ │ ├── fondo.jpg
│ │ │ ├── fondo@2x.jpg
│ │ │ └── fondo@3x.jpg
│ │ ├── index.js
│ │ └── styles.css
│ └── Loader
│ │ ├── index.js
│ │ └── styles.css
│ ├── constants.js
│ ├── constants
│ ├── companyActions.js
│ ├── homeActions.js
│ └── portfolioActions.js
│ ├── containers
│ ├── App
│ │ ├── App.test.js
│ │ ├── index.js
│ │ └── sessionStorage.js
│ ├── CompanyContainer.js
│ ├── CompanyListContainer
│ │ ├── CompanyListContainer.js
│ │ └── styles.css
│ ├── DashboardContainer.js
│ ├── LoginContainer
│ │ ├── index.js
│ │ └── styles.css
│ ├── NavbarContainer
│ │ ├── index.js
│ │ └── styles.css
│ └── SecureRoute.js
│ ├── index.css
│ ├── index.js
│ ├── reducers
│ ├── company.js
│ ├── company.test.js
│ ├── home.js
│ ├── home.test.js
│ ├── index.js
│ ├── portfolio.js
│ └── portfolio.test.js
│ ├── registerServiceWorker.js
│ └── utils
│ ├── animation.js
│ ├── config.js
│ ├── fetch.js
│ └── transactions.js
├── trader-app
├── manifest.yml
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── io
│ │ │ └── pivotal
│ │ │ └── refarch
│ │ │ └── cqrs
│ │ │ └── trader
│ │ │ ├── app
│ │ │ ├── AmqpConfiguration.java
│ │ │ ├── AppConfig.java
│ │ │ ├── SecurityConfig.java
│ │ │ ├── TraderAggregateDataInitializer.java
│ │ │ ├── TraderApplication.java
│ │ │ ├── command
│ │ │ │ ├── company
│ │ │ │ │ ├── Company.java
│ │ │ │ │ └── CompanyOrderBookListener.java
│ │ │ │ └── user
│ │ │ │ │ ├── DigestUtils.java
│ │ │ │ │ ├── PortfolioManagementUserListener.java
│ │ │ │ │ └── User.java
│ │ │ ├── controller
│ │ │ │ ├── CommandController.java
│ │ │ │ └── QueryController.java
│ │ │ └── query
│ │ │ │ ├── company
│ │ │ │ ├── CompanyEventHandler.java
│ │ │ │ ├── CompanyView.java
│ │ │ │ └── CompanyViewRepository.java
│ │ │ │ ├── orderbook
│ │ │ │ ├── OrderBookEventHandler.java
│ │ │ │ └── OrderBookViewRepository.java
│ │ │ │ ├── orders
│ │ │ │ ├── trades
│ │ │ │ │ ├── OrderBookView.java
│ │ │ │ │ └── OrderView.java
│ │ │ │ └── transaction
│ │ │ │ │ ├── TradeExecutedQueryRepository.java
│ │ │ │ │ ├── TradeExecutedView.java
│ │ │ │ │ └── TransactionView.java
│ │ │ │ ├── portfolio
│ │ │ │ ├── ItemEntry.java
│ │ │ │ ├── PortfolioEventHandler.java
│ │ │ │ ├── PortfolioView.java
│ │ │ │ └── PortfolioViewRepository.java
│ │ │ │ ├── transaction
│ │ │ │ ├── TransactionEventHandler.java
│ │ │ │ └── TransactionViewRepository.java
│ │ │ │ └── users
│ │ │ │ ├── UserEventHandler.java
│ │ │ │ ├── UserView.java
│ │ │ │ └── UserViewRepository.java
│ │ │ └── coreapi
│ │ │ ├── company
│ │ │ ├── CompanyId.java
│ │ │ ├── commands.kt
│ │ │ ├── events.kt
│ │ │ └── queries.kt
│ │ │ ├── orders
│ │ │ ├── OrderBookId.java
│ │ │ ├── trades
│ │ │ │ ├── OrderId.java
│ │ │ │ ├── commands.kt
│ │ │ │ ├── events.kt
│ │ │ │ └── queries.kt
│ │ │ ├── transaction
│ │ │ │ ├── TransactionId.java
│ │ │ │ ├── commands.kt
│ │ │ │ ├── events.kt
│ │ │ │ ├── queries.kt
│ │ │ │ └── valueObjects.kt
│ │ │ └── valueObjects.kt
│ │ │ ├── portfolio
│ │ │ ├── PortfolioId.java
│ │ │ ├── cash
│ │ │ │ ├── commands.kt
│ │ │ │ └── events.kt
│ │ │ ├── commands.kt
│ │ │ ├── events.kt
│ │ │ ├── queries.kt
│ │ │ └── stock
│ │ │ │ ├── commands.kt
│ │ │ │ └── events.kt
│ │ │ └── users
│ │ │ ├── UserId.java
│ │ │ ├── commands.kt
│ │ │ ├── events.kt
│ │ │ └── queries.kt
│ └── resources
│ │ ├── application.properties
│ │ └── bootstrap.properties
│ └── test
│ ├── java
│ └── io
│ │ └── pivotal
│ │ └── refarch
│ │ └── cqrs
│ │ └── trader
│ │ └── app
│ │ ├── command
│ │ ├── company
│ │ │ ├── CompanyOrderBookListenerTest.java
│ │ │ └── CompanyTest.java
│ │ └── user
│ │ │ ├── PortfolioManagementUserListenerTest.java
│ │ │ └── UserTest.java
│ │ ├── contracts
│ │ ├── CommandContractTest.java
│ │ └── QueryContractTest.java
│ │ ├── controller
│ │ ├── CommandControllerTest.java
│ │ ├── CreateCompanyApiIntegrationTest.java
│ │ └── QueryControllerTest.java
│ │ └── query
│ │ ├── company
│ │ └── CompanyEventHandlerTest.java
│ │ ├── orderbook
│ │ └── OrderBookEventHandlerIntegrationTest.java
│ │ ├── portfolio
│ │ ├── PortfolioEventHandlerTest.java
│ │ └── PortfolioViewTest.java
│ │ ├── transaction
│ │ └── TransactionEventHandlerTest.java
│ │ └── users
│ │ └── UserEventHandlerTest.java
│ └── resources
│ └── contracts
│ └── trader
│ ├── command
│ ├── postAddItemsToPortfolioCommandShouldReturnOk.groovy
│ ├── postAddOrderBookToCompanyCommandShouldReturnOk.groovy
│ ├── postAuthenticateUserCommandShouldReturnOk.groovy
│ ├── postCancelCashReservationCommandShouldReturnOk.groovy
│ ├── postCancelItemReservationForPortfolioCommandShouldReturnOk.groovy
│ ├── postCancelTransactionCommandShouldReturnOk.groovy
│ ├── postConfirmCashReservationCommandShouldReturnOk.groovy
│ ├── postConfirmItemReservationForPortfolioCommandShouldReturnOk.groovy
│ ├── postConfirmTransactionCommandShouldReturnOk.groovy
│ ├── postCreateCompanyCommandShouldReturnOkAndUuid.groovy
│ ├── postCreateOrderBookCommandShouldReturnOkAndUuid.groovy
│ ├── postCreatePortfolioCommandShouldReturnOkAndUuid.groovy
│ ├── postCreateUserCommandShouldReturnOkAndUuid.groovy
│ ├── postDepositCashCommandShouldReturnOk.groovy
│ ├── postExecutedTransactionCommandShouldReturnOk.groovy
│ ├── postOfIncorrectCommandPayloadReturnsNotFound.groovy
│ ├── postOfNoneExistingCommandReturnsNotFound.groovy
│ ├── postReserveCashCommandShouldReturnOk.groovy
│ ├── postReserveItemsCommandShouldReturnOk.groovy
│ ├── postStartBuyTransactionCommandShouldReturnOkAndUuid.groovy
│ ├── postStartSellTransactionCommandShouldReturnOkAndUuid.groovy
│ └── postWithdrawCashCommandShouldReturnOk.groovy
│ └── query
│ ├── findAllCompaniesShouldReturnAllCompanies.groovy
│ ├── findAllUsersQueryShouldReturnAllUsers.groovy
│ ├── getCompanyByIdShouldReturnCompany.groovy
│ ├── getCompanyByIdShouldReturnNotFoundForNonExistingId.groovy
│ ├── getExecutedTradesByOrderBookIdShouldNotFoundForNonExistingId.groovy
│ ├── getExecutedTradesByOrderBookIdShouldReturnExecutedTrades.groovy
│ ├── getOrderBookByIdShouldReturnNotFoundForNonExistingId.groovy
│ ├── getOrderBookByIdShouldReturnOrderBook.groovy
│ ├── getOrderBooksByCompanyIdShouldReturnNotFoundForNonExistingId.groovy
│ ├── getOrderBooksByCompanyIdShouldReturnOrderBook.groovy
│ ├── getPortfolioByIdShouldReturnNotFoundForNonExistingId.groovy
│ ├── getPortfolioByIdShouldReturnPortfolio.groovy
│ ├── getPortfolioByUserIdShouldReturnNotFoundForNonExistingId.groovy
│ ├── getPortfolioByUserIdShouldReturnPortfolio.groovy
│ ├── getTransactionByIdShouldReturnNotFoundForNonExistingId.groovy
│ ├── getTransactionByIdShouldReturnTransaction.groovy
│ ├── getTransactionsByPortfolioIdShouldReturnNotFoundForNonExistingId.groovy
│ ├── getTransactionsByPortfolioIdShouldReturnTransactions.groovy
│ ├── getUserByIdShouldReturnNotFoundForNonExistingId.groovy
│ └── getUserByIdShouldReturnUser.groovy
└── trading-engine
├── manifest.yml
├── pom.xml
└── src
├── main
├── java
│ └── io
│ │ └── pivotal
│ │ └── refarch
│ │ └── cqrs
│ │ └── trader
│ │ ├── coreapi
│ │ ├── orders
│ │ │ ├── OrderBookId.java
│ │ │ ├── trades
│ │ │ │ ├── OrderId.java
│ │ │ │ ├── commands.kt
│ │ │ │ └── events.kt
│ │ │ ├── transaction
│ │ │ │ ├── TransactionId.java
│ │ │ │ ├── commands.kt
│ │ │ │ └── events.kt
│ │ │ └── valueObjects.kt
│ │ ├── portfolio
│ │ │ ├── PortfolioId.java
│ │ │ ├── cash
│ │ │ │ ├── commands.kt
│ │ │ │ └── events.kt
│ │ │ ├── commands.kt
│ │ │ ├── events.kt
│ │ │ └── stock
│ │ │ │ ├── commands.kt
│ │ │ │ └── events.kt
│ │ └── users
│ │ │ └── UserId.java
│ │ └── tradingengine
│ │ ├── AmqpConfiguration.java
│ │ ├── SecurityConfig.java
│ │ ├── TradingEngineApplication.java
│ │ ├── order
│ │ ├── BuyTradeManagerSaga.java
│ │ ├── Portfolio.java
│ │ ├── SellTradeManagerSaga.java
│ │ └── Transaction.java
│ │ └── trade
│ │ ├── Order.java
│ │ └── OrderBook.java
└── resources
│ ├── application.properties
│ └── bootstrap.properties
└── test
├── java
└── io
│ └── pivotal
│ └── refarch
│ └── cqrs
│ └── trader
│ └── tradingengine
│ ├── order
│ ├── BuyTradeManagerSagaTest.java
│ ├── PortfolioTest.java
│ ├── SellTradeManagerSagaTest.java
│ └── TransactionTest.java
│ └── trade
│ └── OrderBookTest.java
└── resources
└── bootstrap.properties
/.gitignore:
--------------------------------------------------------------------------------
1 | .cf/
2 | .idea/
3 | .cf/
4 | .DS_Store
5 | *.class
6 | **/private*
7 | **/.classpath
8 | **/.project
9 | **/.settings
10 | **/out
11 |
12 | # Node
13 | **/node_modules
14 |
15 | # Package Files #
16 | **/target/
17 | **/bin
18 | **/*.iml
19 | axoniq-conference-demo.md
20 |
21 | **/build
22 |
23 | secret*
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pivotalsoftware/ESarch/c6e2f272b3cb99b414bb1e1b0456c9d0f3efe359/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 |
2 | # This is the list of authors for copyright purposes.
3 | #
4 | # This does not necessarily list everyone who has contributed code, since in
5 | # some cases, their employer may be the copyright holder. To see the full list
6 | # of contributors, see the revision history in source control.
7 |
8 | ## Pivotal Software, Inc.
9 |
10 | * Ben Wilcock
11 | * David Caron
12 | * Jakub Pilmon
13 | * Kenny Bastani
14 | * Pieter Humphrey
15 |
16 | ## AxonIQ
17 |
18 | * Allard Buijze
19 | * Steven van Beelen
20 |
21 | ## Solstice
22 |
23 | * Sampath Kunta
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ### 10-08-2018 Ben W.
4 |
5 | Added in the scaffolding necessary to get "producer" side Spring Cloud Contracts in the Trader-App. This means that there are some changes to the Trader-App `POM.xml` and new API contract specifications written in Groovy. These specs are in the `/test/resources/contracts` folder and sre organised into folders by consumer. Each file contains a single spec of a request-response interaction. These contracts are used in two ways: firstly they are used by the SCC plugin to generate JUnit tests for the API specified (find them in `target/generated-test-sources`), and secondly a "stubs" jar is created which contains the specifications needed for wiremock to be able to fake the same API. To generate and run these contract-based tests use `mvn test` to add the stubs jar to your local `.m2` folder, use `mvn install`.
6 |
7 | > Side note: notice that the JUnit tests generated by SCC extend the same base class (`BaseContractTest`). This base class must be provided and is configured via the plugin XML configuration in the `POM.xml`.
8 |
9 | Separated the Unit level tests from the Integration level tests (test that run SpringBoot, like `@SpringBootTest`, `@DataJpaTest`, `@WebMvcTest` etc.). This involved changes to the `POM.xml`. To benefit from this separation...
10 |
11 | * Run unit tests > `mvn test`
12 | * Run unit and integration tests > `mvn verify`
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Pivotal Projects
2 |
3 | We’d love to accept your patches and contributions to this project. Please review the following guidelines you'll need to follow in order to make a contribution.
4 |
5 | ## Contributor License Agreement
6 |
7 | All contributors to this project must have a signed Contributor License Agreement (**"CLA"**) on file with us. The CLA grants us the permissions we need to use and redistribute your contributions as part of the project; you or your employer retain the copyright to your contribution. Head over to https://cla.pivotal.io/ to see your current agreement(s) on file or to sign a new one.
8 |
9 | We generally only need you (or your employer) to sign our CLA once and once signed, you should be able to submit contributions to any Pivotal project.
10 |
11 | Note: if you would like to submit an "_obvious fix_" for something like a typo, formatting issue or spelling mistake, you may not need to sign the CLA. Please see our information on [obvious fixes](https://cla.pivotal.io/about#obvious-fix) for more details.
12 |
13 | ## Code reviews
14 |
15 | All submissions, including submissions by project members, require review and we use GitHub's pull requests for this purpose. Please consult [GitHub Help](https://help.github.com/articles/about-pull-requests/) if you need more information about using pull requests.
16 |
17 |
18 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | [Event Sourcing Reference Architecture]
2 |
3 | Copyright (c) 2018-Present Pivotal Software, Inc. All Rights Reserved.
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 |
--------------------------------------------------------------------------------
/bigpave-pcf.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Use this script to pave the required services on a PCF environment other than PWS (such as an enterprise-deployed cluster)
3 |
4 | cf create-service p.mysql db-small enginedb
5 | cf create-service p.mysql db-small appdb
6 | cf create-service p.rabbitmq single-node-3.7 rabbit
7 | cf create-service p-service-registry standard registry
8 | cf create-service p-config-server standard config -c config-server-setup.json
9 |
10 |
--------------------------------------------------------------------------------
/bigpave-pws.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Use this script to pave your PWS instance with the correct services
3 |
4 | set -eu
5 |
6 | if [ -z ${1+x} ] && [ -z ${2+x} ] && [ -z ${3+x} ] && [ -z ${4+x} ]; then
7 | echo "Usage: USERNAME PASSWORD ORG SPACE"
8 | exit 1
9 | fi
10 |
11 | export CF='command -v cf'
12 | if [[ $? != 0 ]]; then
13 | echo "I don't think the CF command line client (cf-cli) is installed."
14 | echo "Please see the instructions here: https://docs.cloudfoundry.org/cf-cli/install-go-cli.html"
15 | exit 1
16 | else
17 | echo $'\nYou have the CF command line client already; that\'s awesome!\n'
18 | fi
19 |
20 | export API="https://api.run.pivotal.io"
21 | echo "API: ${API}"
22 |
23 | if [ -z ${1+x} ]; then
24 | echo "I need your PWS username (email address) to continue."
25 | exit 1
26 | else
27 | echo "Username: $1"
28 | fi
29 |
30 | if [ -z ${2+x} ]; then
31 | echo "I need your PWS password to continue."
32 | exit 1
33 | else
34 | echo "Password is {SECRET}."
35 | fi
36 |
37 | if [ -z ${3+x} ]; then
38 | echo "I need your PWS ORG name to continue."
39 | exit 1
40 | else
41 | echo "Org: $3"
42 | fi
43 |
44 | if [ -z ${4+x} ]; then
45 | echo "I need your PWS SPACE name to continue."
46 | exit 1
47 | else
48 | echo "Space: $4"
49 | fi
50 |
51 | echo $'Logging in to PWS using the details provided...\n'
52 |
53 | cf login -a ${API} -u $1 -p $2 -o $3 -s $4
54 |
55 | if [[ $? != 0 ]]; then
56 | echo "Abort. There was a problem logging in to PWS with the details given. Please resolve the problem and try again."
57 | exit 1
58 | else
59 | echo $'\nWe\'re logged in. Super!\n'
60 | fi
61 |
62 | echo $'Now we\'ll create some services using the Cloud Foundry marketplace [#Awesome #NoOps]...\n'
63 |
64 | cf create-service cleardb amp enginedb
65 | cf create-service cleardb amp appdb
66 | cf create-service cloudamqp tiger rabbit
67 | cf create-service p-service-registry standard registry
68 | cf create-service p-config-server standard config -c config-server-setup.json
69 |
70 | echo $'\nNow we need to wait a few minutes until all five
71 | services show \"create succeeded\" as their status.'
72 | echo "This may take while, so I'll start a watch. Press CTRL+C any time to quit (the services will still be created)."
73 | read -p "Press [Enter] to start watching..."
74 |
75 | watch -n 1 "cf services"
76 |
77 | echo $'\nWe\'re done. You can now use the push.sh script provided to push your apps.'
--------------------------------------------------------------------------------
/ci/run-tests.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | if [ -n $engineURL ]
6 | then
7 | export engineURL=https://esrefarch-demo-trading-engine.cfapps.io
8 | echo -e "Setting URL for the Trading Engine to ${engineURL}"
9 | fi
10 |
11 | if [ -n $appURL ]
12 | then
13 | export appURL=https://esrefarch-demo-trader-app.cfapps.io
14 | echo -e "Setting URL for the Trading App to ${appURL}"
15 | fi
16 |
17 | if [ -n $uiURL ]
18 | then
19 | export uiURL=https://esrefarch-demo-trader-ui.cfapps.io
20 | echo -e "Setting URL for the Trader UI to ${uiURL}"
21 | fi
22 |
23 | env URL="$engineURL" ./tasks/smoke-test-trading-engine.sh
24 | env URL="$appURL" ./tasks/smoke-test-trader-app.sh
25 | ./tasks/e2e-test-both-sides.sh
--------------------------------------------------------------------------------
/ci/tasks/archive/package-command-side.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e +x
4 |
5 | pushd source-code
6 | echo "Packaging JAR"
7 | ./gradlew command-side:assemble
8 | popd
9 |
10 | jar_count=`find source-code/command-side/build/libs -type f -name *.jar | wc -l`
11 |
12 | if [ $jar_count -gt 1 ]; then
13 | echo "More than one jar found, don't know which one to deploy. Exiting :("
14 | exit 1
15 | fi
16 |
17 | find source-code/command-side/build/libs -type f -name *.jar -exec cp "{}" package-output/pcf-command-side.jar \;
18 |
19 | echo "Done packaging"
20 | exit 0
--------------------------------------------------------------------------------
/ci/tasks/archive/package-command-side.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: docker-image
6 | source:
7 | repository: java
8 | tag: "8"
9 |
10 | inputs:
11 | - name: source-code
12 |
13 | outputs:
14 | - name: package-output
15 |
16 | run:
17 | path: source-code/ci/tasks/package-command-side.sh
18 |
--------------------------------------------------------------------------------
/ci/tasks/archive/package-query-side.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e +x
4 |
5 | pushd source-code
6 | echo "Packaging JAR"
7 | ./gradlew query-side:assemble
8 | popd
9 |
10 | jar_count=`find source-code/query-side/build/libs -type f -name *.jar | wc -l`
11 |
12 | if [ $jar_count -gt 1 ]; then
13 | echo "More than one jar found, don't know which one to deploy. Exiting :("
14 | exit 1
15 | fi
16 |
17 | find source-code/query-side/build/libs -type f -name *.jar -exec cp "{}" package-output/pcf-query-side.jar \;
18 |
19 | echo "Done packaging"
20 | exit 0
--------------------------------------------------------------------------------
/ci/tasks/archive/package-query-side.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: docker-image
6 | source:
7 | repository: java
8 | tag: "8"
9 |
10 | inputs:
11 | - name: source-code
12 |
13 | outputs:
14 | - name: package-output
15 |
16 | run:
17 | path: source-code/ci/tasks/package-command-side.sh
18 |
--------------------------------------------------------------------------------
/ci/tasks/archive/unit-test-command-side.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -ex
4 |
5 | pushd source-code
6 | echo "Fetching Dependencies & Building Code..."
7 | ./gradlew command-side:assemble > /dev/null
8 |
9 | echo "Running Tests..."
10 | ./gradlew command-side:test
11 | popd
12 |
13 | exit 0
14 |
--------------------------------------------------------------------------------
/ci/tasks/archive/unit-test-command-side.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: docker-image
6 | source:
7 | repository: java
8 | tag: "8"
9 |
10 | inputs:
11 | - name: source-code
12 |
13 | run:
14 | path: source-code/ci/tasks/unit-test-command-side.sh
15 |
--------------------------------------------------------------------------------
/ci/tasks/archive/unit-test-common.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -ex
4 |
5 | pushd source-code
6 | echo "Fetching Dependencies & Building Code..."
7 | ./gradlew common-api:assemble > /dev/null
8 |
9 | echo "Running Tests..."
10 | ./gradlew common-api:test
11 | popd
12 |
13 | exit 0
14 |
--------------------------------------------------------------------------------
/ci/tasks/archive/unit-test-common.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: docker-image
6 | source:
7 | repository: java
8 | tag: "8"
9 |
10 | inputs:
11 | - name: source-code
12 |
13 | run:
14 | path: source-code/ci/tasks/unit-test-common.sh
15 |
--------------------------------------------------------------------------------
/ci/tasks/archive/unit-test-query-side.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -ex
4 |
5 | pushd source-code
6 | echo "Fetching Dependencies & Building Code..."
7 | ./gradlew query-side:assemble > /dev/null
8 |
9 | echo "Running Tests..."
10 | ./gradlew query-side:test
11 | popd
12 |
13 | exit 0
14 |
--------------------------------------------------------------------------------
/ci/tasks/archive/unit-test-query-side.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: docker-image
6 | source:
7 | repository: java
8 | tag: "8"
9 |
10 | inputs:
11 | - name: source-code
12 |
13 | run:
14 | path: source-code/ci/tasks/unit-test-query-side.sh
15 |
--------------------------------------------------------------------------------
/ci/tasks/build-trader-app.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | set -e +x
4 |
5 | export FOLDER=`pwd`
6 | echo "The path is ${OLDPATH}"
7 |
8 | echo "Testing and Packaging the trader App JAR..."
9 | cd source-code/trader-app
10 | mvn verify
11 | cd $FOLDER
12 |
13 | # jar_count=`find source-code/trader-app/target -type f -name *.jar | wc -l`
14 |
15 | # if [ $jar_count -gt 1 ]; then
16 | # echo "More than one jar found, don't know which one to deploy. Exiting :("
17 | # exit 1
18 | # fi
19 |
20 | # find source-code/trader-app/target -type f -name *.jar -exec cp "{}" package-output/trader-app.jar \;
21 |
22 | find source-code/trader-app/target -type f -name trader-app.jar -exec cp "{}" package-output/trader-app.jar \;
23 |
24 | echo "Done packaging"
25 | exit 0
--------------------------------------------------------------------------------
/ci/tasks/build-trader-app.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: docker-image
6 | source:
7 | repository: maven
8 | tag: "3.5-jdk-8-alpine"
9 |
10 | inputs:
11 | - name: source-code
12 |
13 | outputs:
14 | - name: package-output
15 |
16 | run:
17 | path: source-code/ci/tasks/build-trader-app.sh
18 |
--------------------------------------------------------------------------------
/ci/tasks/build-trader-ui.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | set -eux
4 |
5 | export FOLDER=`pwd`
6 | echo "The path is ${FOLDER}"
7 |
8 | echo "Packaging the Trader UI App..."
9 |
10 | cd source-code/trader-app-ui
11 | npm install
12 | npm run build
13 | cp manifest.yml ${FOLDER}/package-output/manifest.yml
14 | cp Staticfile build/Staticfile
15 | cp -R build ${FOLDER}/package-output
16 | cd $FOLDER
17 |
18 | ls -la package-output
19 | ls -la package-output/build
20 | ls -la package-output/build/static
21 |
22 | echo "Done building trader ui"
23 | exit 0
--------------------------------------------------------------------------------
/ci/tasks/build-trader-ui.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: docker-image
6 | source:
7 | repository: node
8 |
9 | inputs:
10 | - name: source-code
11 |
12 | outputs:
13 | - name: package-output
14 |
15 | run:
16 | path: source-code/ci/tasks/build-trader-ui.sh
17 |
--------------------------------------------------------------------------------
/ci/tasks/build-trading-engine.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e +x
4 |
5 | export FOLDER=`pwd`
6 | echo "The path is ${OLDPATH}"
7 |
8 | echo "Testing and Packaging the Trading Engine JAR..."
9 | cd source-code/trading-engine
10 | mvn verify
11 | cd $FOLDER
12 |
13 | jar_count=`find source-code/trading-engine/target -type f -name *.jar | wc -l`
14 |
15 | if [ $jar_count -gt 1 ]; then
16 | echo "More than one jar found, don't know which one to deploy. Exiting :("
17 | exit 1
18 | fi
19 |
20 | find source-code/trading-engine/target -type f -name *.jar -exec cp "{}" package-output/trading-engine.jar \;
21 |
22 | echo "Done packaging"
23 | exit 0
--------------------------------------------------------------------------------
/ci/tasks/build-trading-engine.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: docker-image
6 | source:
7 | repository: maven
8 | tag: "3.5-jdk-8-alpine"
9 |
10 | inputs:
11 | - name: source-code
12 |
13 | outputs:
14 | - name: package-output
15 |
16 | run:
17 | path: source-code/ci/tasks/build-trading-engine.sh
18 |
--------------------------------------------------------------------------------
/ci/tasks/e2e-test-both-sides.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: docker-image
6 | source:
7 | repository: ubuntu
8 | tag: "latest"
9 |
10 | params:
11 | URL:
12 |
13 | inputs:
14 | - name: source-code
15 |
16 | run:
17 | path: source-code/ci/tasks/e2e-test-both-sides.sh
--------------------------------------------------------------------------------
/ci/tasks/smoke-test-trader-app.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | apt-get update && apt-get install -y curl uuid-runtime jq --allow-unauthenticated
4 |
5 | set -eu
6 |
7 | echo "Smoke Testing the Trader App using the URL: ${URL}"
8 |
9 | # Begin the Smoke-testing...
10 |
11 | export HEALTH_STATUS=`curl -sL -X GET ${URL}/actuator/health | jq -r .status`
12 | if [ -z ${HEALTH_STATUS} ] || [ "$HEALTH_STATUS" != "UP" ]
13 | then
14 | echo -e "\e[31mError. The smoke test has failed, the application health check didn't work!"
15 | exit 1
16 | else
17 | echo "The health check status is reporting that ${URL} is ${HEALTH_STATUS}"
18 | fi
19 |
20 | export API=`curl -sL -X GET ${URL}/command/api | jq '. | length'`
21 | if [ -z ${API} ] || [[ ${API} < 1 ]]
22 | then
23 | echo -e "\e[31mError. The smoke test has failed, the application API command count was too low!"
24 | exit 1
25 | else
26 | echo "The API check status is reporting that there are ${API} commands available."
27 | fi
28 |
29 | echo -e "\e[32mTRADER-APP SMOKE TEST FINISHED - ZERO ERRORS ;D "
30 | exit 0
--------------------------------------------------------------------------------
/ci/tasks/smoke-test-trader-app.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: docker-image
6 | source:
7 | repository: ubuntu
8 | tag: "latest"
9 |
10 | params:
11 | URL:
12 |
13 | inputs:
14 | - name: source-code
15 |
16 | run:
17 | path: source-code/ci/tasks/smoke-test-trader-app.sh
--------------------------------------------------------------------------------
/ci/tasks/smoke-test-trading-engine.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | apt-get update && apt-get install -y curl uuid-runtime jq --allow-unauthenticated
4 |
5 | set -eu
6 |
7 | echo "Smoke Testing the Trading Engine using the URL: ${URL}"
8 |
9 | # Begin the Smoke-testing...
10 |
11 | export HEALTH_STATUS=`curl -sL -X GET ${URL}/actuator/health | jq -r .status`
12 | if [ -z $HEALTH_STATUS ] || [ "$HEALTH_STATUS" != "UP" ]
13 | then
14 | echo -e "\e[31mError. The smoke test has failed, the application health check didn't work!"
15 | exit 1
16 | else
17 | echo "The health check status is reporting that ${URL} is ${HEALTH_STATUS}"
18 | fi
19 | echo -e "\e[32mTRADING-ENGINE SMOKE TEST FINISHED - ZERO ERRORS ;D "
20 | exit 0
--------------------------------------------------------------------------------
/ci/tasks/smoke-test-trading-engine.yml:
--------------------------------------------------------------------------------
1 | ---
2 | platform: linux
3 |
4 | image_resource:
5 | type: docker-image
6 | source:
7 | repository: ubuntu
8 | tag: "latest"
9 |
10 | params:
11 | URL:
12 |
13 | inputs:
14 | - name: source-code
15 |
16 | run:
17 | path: source-code/ci/tasks/smoke-test-trading-engine.sh
--------------------------------------------------------------------------------
/ci/tips.txt:
--------------------------------------------------------------------------------
1 | fly -t local login -c http://localhost:8000
2 | fly -t local set-pipeline -p pcf-cqrs-demo -c ci/pipeline.yml -l ci/private.yml
3 | fly -t local expose-pipeline -p pcf-cqrs-demo
4 | fly -t local unpause-pipeline -p pcf-cqrs-demo
5 | fly -t local trigger-job -j pcf-cqrs-demo/build-command-side
6 | fly -t local execute --config ci/tasks/build-command-side.yml --input source-code=.
7 |
8 |
9 |
10 | fly -t wings set-pipeline -p bw-esrefarch-demo -c ci/pipeline.yml -l ci/private.yml
11 | fly -t wings unpause-pipeline -p bw-esrefarch-demo
12 | fly -t wings expose-pipeline -p bw-esrefarch-demo
13 | fly -t wings containers
14 | fly -t wings trigger-job -j bw-esrefarch-demo/build-command-side
15 |
16 | ssh-keygen -t rsa -b 4096 -C "myemail@server.com"
17 | sh-agent -s
18 | ssh-add -K ~/.ssh/id_rsa
19 | pbcopy < ~/.ssh/id_rsa
20 | pbcopy < ~/.ssh/id_rsa.pub
21 | ssh -T git@github.com
22 |
23 | git stash
24 | git stash list
25 | git stash apply
26 | git merge redis-repo
27 | git push origin --delete redis-repo
28 | git fetch --all --prune
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/config-server-setup.json:
--------------------------------------------------------------------------------
1 | {
2 | "git": {
3 | "uri": "https://github.com/pivotalsoftware/ESarch-conf.git",
4 | "searchPaths": "{application}"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/deploy-backend.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | # Use this script to package up your code
3 |
4 | set -eu
5 |
6 | pushd () {
7 | command pushd "$@" > /dev/null
8 | }
9 |
10 | popd () {
11 | command popd "$@" > /dev/null
12 | }
13 |
14 | if [ -z ${1+x} ] && [ -z ${2+x} ] && [ -z ${3+x} ] && [ -z ${4+x} ]; then
15 | echo "Usage: USERNAME PASSWORD ORG SPACE"
16 | exit 1
17 | fi
18 |
19 | export CF='command -v cf'
20 | if [[ $? != 0 ]]; then
21 | echo "I don't think the CF command line client (cf-cli) is installed."
22 | echo "Please see the instructions here: https://docs.cloudfoundry.org/cf-cli/install-go-cli.html"
23 | exit 1
24 | else
25 | echo $'\nYou have the CF command line client already; that\'s awesome!\n'
26 | fi
27 |
28 | export API="https://api.run.pivotal.io"
29 | echo "API: ${API}"
30 |
31 |
32 | if [ -z ${1+x} ]; then
33 | echo "I need your PWS username (email address) to continue."
34 | exit 1
35 | else
36 | echo "Username: $1"
37 | fi
38 |
39 | if [ -z ${2+x} ]; then
40 | echo "I need your PWS password to continue."
41 | exit 1
42 | else
43 | echo "Password is {SECRET}."
44 | fi
45 |
46 | if [ -z ${3+x} ]; then
47 | echo "I need your PWS ORG name to continue."
48 | exit 1
49 | else
50 | echo "Org: $3"
51 | fi
52 |
53 | if [ -z ${4+x} ]; then
54 | echo "I need your PWS SPACE name to continue."
55 | exit 1
56 | else
57 | echo "Space: $4"
58 | fi
59 |
60 | echo $'Logging in to PWS using the details provided...\n'
61 |
62 | cf login -a ${API} -u $1 -p $2 -o $3 -s $4
63 |
64 | if [[ $? != 0 ]]; then
65 | echo "Abort. There was a problem logging in to PWS with the details given. Please resolve the problem and try again."
66 | exit 1
67 | else
68 | echo $'\nWe\'re logged in. Let\'s deploy some code!\n'
69 | fi
70 |
71 | echo "Building the Java Packages..."
72 | mvn clean package -DskipTests=true
73 |
74 | echo "Pushing the Trading Engine to PWS..."
75 | pushd .
76 | cd trading-engine
77 | cf push -f manifest.yml --random-route
78 | popd
79 |
80 | echo "Pushing the Trader App to PWS..."
81 | pushd .
82 | cd trader-app
83 | cf push -f manifest.yml --random-route
84 | popd
85 |
86 | echo "Adding a network policy so that the Trader App and the Tradng Engine can communicate directly..."
87 | cf add-network-policy esrefarch-demo-trader-app --destination-app esrefarch-demo-trading-engine
88 | cf add-network-policy esrefarch-demo-trading-engine --destination-app esrefarch-demo-trader-app
89 |
90 | echo "All done. The Axon Trader application is now ready to test."
91 |
--------------------------------------------------------------------------------
/deploy-frontend.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | # Use this script to package up your code
3 |
4 | set -eu
5 |
6 | pushd () {
7 | command pushd "$@" > /dev/null
8 | }
9 |
10 | popd () {
11 | command popd "$@" > /dev/null
12 | }
13 |
14 | if [ -z ${1+x} ] && [ -z ${2+x} ] && [ -z ${3+x} ] && [ -z ${4+x} ]; then
15 | echo "Usage: USERNAME PASSWORD ORG SPACE"
16 | exit 1
17 | fi
18 |
19 | export CF='command -v cf'
20 | if [[ $? != 0 ]]; then
21 | echo "I don't think the CF command line client (cf-cli) is installed."
22 | echo "Please see the instructions here: https://docs.cloudfoundry.org/cf-cli/install-go-cli.html"
23 | exit 1
24 | else
25 | echo $'\nYou have the CF command line client already; that\'s awesome!\n'
26 | fi
27 |
28 | export API="https://api.run.pivotal.io"
29 | echo "API: ${API}"
30 |
31 |
32 | if [ -z ${1+x} ]; then
33 | echo "I need your PWS username (email address) to continue."
34 | exit 1
35 | else
36 | echo "Username: $1"
37 | fi
38 |
39 | if [ -z ${2+x} ]; then
40 | echo "I need your PWS password to continue."
41 | exit 1
42 | else
43 | echo "Password is {SECRET}."
44 | fi
45 |
46 | if [ -z ${3+x} ]; then
47 | echo "I need your PWS ORG name to continue."
48 | exit 1
49 | else
50 | echo "Org: $3"
51 | fi
52 |
53 | if [ -z ${4+x} ]; then
54 | echo "I need your PWS SPACE name to continue."
55 | exit 1
56 | else
57 | echo "Space: $4"
58 | fi
59 |
60 | echo $'Logging in to PWS using the details provided...\n'
61 |
62 | cf login -a ${API} -u $1 -p $2 -o $3 -s $4
63 |
64 | if [[ $? != 0 ]]; then
65 | echo "Abort. There was a problem logging in to PWS with the details given. Please resolve the problem and try again."
66 | exit 1
67 | else
68 | echo $'\nWe\'re logged in. Let\'s deploy some code!\n'
69 | fi
70 |
71 | echo "Building the Trader UI..."
72 | pushd .
73 | cd trader-app-ui
74 | npm install
75 | npm run build
76 | echo "Pushing the Trader UI to PWS..."
77 | cf push -f manifest.yml --random-route
78 | popd
79 |
80 | echo "All done. The Axon Trader application is now ready to test."
81 |
--------------------------------------------------------------------------------
/discovery-server/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.example
7 | discovery-server
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | discovery-server
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 2.0.3.RELEASE
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | 1.8
25 | Finchley.RELEASE
26 |
27 |
28 |
29 |
30 | org.springframework.cloud
31 | spring-cloud-starter-netflix-eureka-server
32 |
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-test
37 | test
38 |
39 |
40 |
41 |
42 |
43 |
44 | org.springframework.cloud
45 | spring-cloud-dependencies
46 | ${spring-cloud.version}
47 | pom
48 | import
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | org.springframework.boot
57 | spring-boot-maven-plugin
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/discovery-server/src/main/java/com/example/discoveryserver/DiscoveryServerApplication.java:
--------------------------------------------------------------------------------
1 | package com.example.discoveryserver;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
6 |
7 | @EnableEurekaServer
8 | @SpringBootApplication
9 | public class DiscoveryServerApplication {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(DiscoveryServerApplication.class, args);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/discovery-server/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=8761
2 |
--------------------------------------------------------------------------------
/discovery-server/src/test/java/com/example/discoveryserver/DiscoveryServerApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.example.discoveryserver;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.test.context.junit4.SpringRunner;
7 |
8 | @RunWith(SpringRunner.class)
9 | @SpringBootTest
10 | public class DiscoveryServerApplicationTests {
11 |
12 | @Test
13 | public void contextLoads() {
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/http-api.http:
--------------------------------------------------------------------------------
1 | # For a quick start check out our HTTP Requests collection (Tools|HTTP Client|Open HTTP Requests Collection).
2 | #
3 | # Following HTTP Request Live Templates are available:
4 | # * 'gtrp' and 'gtr' create a GET request with or without query parameters;
5 | # * 'ptr' and 'ptrp' create a POST request with a simple or parameter-like body;
6 | # * 'mptr' and 'fptr' create a POST request to submit a form with a text or file field (multipart/form-data);
7 |
8 |
9 | GET http://localhost:8080/command/api
10 |
11 | ###
12 |
13 | POST https://esrefarch-demo-trader-app.cfapps.io/command/CreateCompanyCommand
14 | Content-Type: application/json
15 |
16 | {
17 | "companyId": "fa8e9411-9e3a-46a3-9751-14ff68824fe7",
18 | "userId": "1bae0365-949c-467d-a6f5-4a32ddd96dbb",
19 | "companyName": "AxonIQ",
20 | "companyValue": "1337",
21 | "amountOfShares": "42"
22 | }
23 |
24 | ###
25 |
26 | POST https://esrefarch-demo-trader-app.cfapps.io/command/CreatePortfolioCommand
27 | Content-Type: application/json
28 |
29 | {
30 | "portfolioId":"abae5981-9eea-4b0a-b2cd-54002a837409",
31 | "userId":"1bae0365-949c-467d-a6f5-4a32ddd96dbb"
32 | }
33 |
34 | ###
35 |
36 | GET http://localhost:8080/status
37 | Accept: application/json
38 |
39 | ###
40 |
41 | GET http://localhost:8761/
42 | Accept: application/json
43 |
44 | ###
45 |
--------------------------------------------------------------------------------
/images/AxonTrader-UI-001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pivotalsoftware/ESarch/c6e2f272b3cb99b414bb1e1b0456c9d0f3efe359/images/AxonTrader-UI-001.png
--------------------------------------------------------------------------------
/images/CQRS+EventSourcing-on-CloudFoundry.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pivotalsoftware/ESarch/c6e2f272b3cb99b414bb1e1b0456c9d0f3efe359/images/CQRS+EventSourcing-on-CloudFoundry.jpg
--------------------------------------------------------------------------------
/pave.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Use this script to pave your PWS instance with the correct services
3 |
4 | set -eu
5 |
6 | if [ -z ${1+x} ] && [ -z ${2+x} ] && [ -z ${3+x} ] && [ -z ${4+x} ]; then
7 | echo "Usage: USERNAME PASSWORD ORG SPACE"
8 | exit 1
9 | fi
10 |
11 | export CF='command -v cf'
12 | if [[ $? != 0 ]]; then
13 | echo "I don't think the CF command line client (cf-cli) is installed."
14 | echo "Please see the instructions here: https://docs.cloudfoundry.org/cf-cli/install-go-cli.html"
15 | exit 1
16 | else
17 | echo $'\nYou have the CF command line client already; that\'s awesome!\n'
18 | fi
19 |
20 | export API="https://api.run.pivotal.io"
21 | echo "API: ${API}"
22 |
23 | if [ -z ${1+x} ]; then
24 | echo "I need your PWS username (email address) to continue."
25 | exit 1
26 | else
27 | echo "Username: $1"
28 | fi
29 |
30 | if [ -z ${2+x} ]; then
31 | echo "I need your PWS password to continue."
32 | exit 1
33 | else
34 | echo "Password is {SECRET}."
35 | fi
36 |
37 | if [ -z ${3+x} ]; then
38 | echo "I need your PWS ORG name to continue."
39 | exit 1
40 | else
41 | echo "Org: $3"
42 | fi
43 |
44 | if [ -z ${4+x} ]; then
45 | echo "I need your PWS SPACE name to continue."
46 | exit 1
47 | else
48 | echo "Space: $4"
49 | fi
50 |
51 | echo $'Logging in to PWS using the details provided...\n'
52 |
53 | cf login -a ${API} -u $1 -p $2 -o $3 -s $4
54 |
55 | if [[ $? != 0 ]]; then
56 | echo "Abort. There was a problem logging in to PWS with the details given. Please resolve the problem and try again."
57 | exit 1
58 | else
59 | echo $'\nWe\'re logged in. Super!\n'
60 | fi
61 |
62 | echo $'Now we\'ll create some services using the Cloud Foundry marketplace [#AwesomePCF #NoOps :D ]...\n'
63 |
64 | cf create-service cleardb spark enginedb
65 | cf create-service cleardb spark appdb
66 | cf create-service cloudamqp lemur rabbit
67 | cf create-service p-service-registry trial registry
68 | cf create-service p-config-server trial config -c config-server-setup.json
69 |
70 | echo $'\nNow we need to wait a few minutes until all five
71 | services show \"create succeeded\" as their status.'
72 | echo "Press [CTRL+C] any time to quit (the services will still be created)."
73 | read -p "Press the [Enter] key to watch while the services get created..."
74 |
75 | watch -n 1 "cf services"
76 |
77 | echo $'\nWe\'re done. You can now deploy the Axom Trader applications to Pivotal Web Services.'
--------------------------------------------------------------------------------
/teardown.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | Use this script to unpave your PWS space
3 |
4 | set -eu
5 |
6 | if [ -z ${1+x} ] && [ -z ${2+x} ] && [ -z ${3+x} ] && [ -z ${4+x} ]; then
7 | echo "Usage: USERNAME PASSWORD ORG SPACE"
8 | exit 1
9 | fi
10 |
11 | export CF='command -v cf'
12 | if [[ $? != 0 ]]; then
13 | echo "I don't think the CF command line client (cf-cli) is installed."
14 | echo "Please see the instructions here: https://docs.cloudfoundry.org/cf-cli/install-go-cli.html"
15 | exit 1
16 | else
17 | echo $'\nYou have the CF command line client already; that\'s awesome!\n'
18 | fi
19 |
20 | export API="https://api.run.pivotal.io"
21 | echo "API: ${API}"
22 |
23 | if [ -z ${1+x} ]; then
24 | echo "I need your PWS username (email address) to continue."
25 | exit 1
26 | else
27 | echo "Username: $1"
28 | fi
29 |
30 | if [ -z ${2+x} ]; then
31 | echo "I need your PWS password to continue."
32 | exit 1
33 | else
34 | echo "Password is {SECRET}."
35 | fi
36 |
37 | if [ -z ${3+x} ]; then
38 | echo "I need your PWS ORG name to continue."
39 | exit 1
40 | else
41 | echo "Org: $3"
42 | fi
43 |
44 | if [ -z ${4+x} ]; then
45 | echo "I need your PWS SPACE name to continue."
46 | exit 1
47 | else
48 | echo "Space: $4"
49 | fi
50 |
51 | echo $'Logging in to PWS using the details provided...\n'
52 |
53 | cf login -a ${API} -u $1 -p $2 -o $3 -s $4
54 |
55 | if [[ $? != 0 ]]; then
56 | echo "Abort. There was a problem logging in to PWS with the details given. Please resolve the problem and try again."
57 | exit 1
58 | else
59 | echo $'\nWe\'re logged in. Super!\n'
60 | fi
61 |
62 |
63 | cf stop esrefarch-demo-trader-app
64 | cf stop esrefarch-demo-trader-ui
65 | cf stop esrefarch-demo-trading-engine
66 |
67 | cf us esrefarch-demo-trader-app registry
68 | cf us esrefarch-demo-trading-engine registry
69 | cf us esrefarch-demo-trader-app config
70 | cf us esrefarch-demo-trading-engine config
71 | cf us esrefarch-demo-trader-ui config
72 | cf us esrefarch-demo-trader-app rabbit
73 | cf us esrefarch-demo-trading-engine rabbit
74 | cf us esrefarch-demo-trading-engine enginedb
75 | cf us esrefarch-demo-trader-app appdb
76 |
77 | cf ds rabbit -f
78 | cf ds config -f
79 | cf ds registry -f
80 | cf ds appdb -f
81 | cf ds enginedb -f
82 |
83 | cf delete esrefarch-demo-trader-app -f
84 | cf delete esrefarch-demo-trader-ui -f
85 | cf delete esrefarch-demo-trading-engine -f
86 |
87 | exit 0
--------------------------------------------------------------------------------
/trader-app-ui/.eslintignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /node_modules
3 | /build
4 | /src/registerServiceWorker.js
--------------------------------------------------------------------------------
/trader-app-ui/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "jasmine": true
5 | },
6 | "extends": ["airbnb"]
7 | }
8 |
--------------------------------------------------------------------------------
/trader-app-ui/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # dependencies
3 | /node_modules
4 |
5 | # testing
6 | /coverage
7 |
8 | # production
9 | /build
10 |
11 | # misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
22 | # dotenv environment variables file
23 | .env
--------------------------------------------------------------------------------
/trader-app-ui/README.md:
--------------------------------------------------------------------------------
1 | # Axon Trader Application UI Built on ReactJS and Redux
2 |
3 | ## Development
4 | **Prerequisites:** `node` v8 and `npm` v5
5 |
6 | - Add `.env` file to the root directory and include the following environmental variables:
7 | - `REACT_APP_API_ROOT="http://localhost:8081"` (replace the text with your API url)
8 |
9 | ### Run the application:
10 |
11 | - install dependencies - `npm install`
12 | - run the app (dev server) - `npm start` and visit http://localhost:3000/ to view the application
13 |
14 |
15 | Run tests - `npm run test`
16 |
17 | Production build - `npm run build`
18 |
19 | *This application is configured using [create-react-app](https://github.com/facebook/create-react-app "create-react-app github repo"). Please read it's documentation for further details of the application structure*
20 |
--------------------------------------------------------------------------------
/trader-app-ui/Staticfile:
--------------------------------------------------------------------------------
1 | directory: visible
2 | pushstate: enabled
--------------------------------------------------------------------------------
/trader-app-ui/manifest.yml:
--------------------------------------------------------------------------------
1 | ---
2 | applications:
3 | - name: esrefarch-demo-trader-ui
4 | timeout: 120
5 | instances: 1
6 | buildpacks:
7 | - staticfile_buildpack
8 | path: build
9 | env:
10 | TRUST_CERTS: api.run.pivotal.io
--------------------------------------------------------------------------------
/trader-app-ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "trader-app-ui",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "history": "^4.7.2",
7 | "react": "^16.4.2",
8 | "react-bootstrap": "^0.32.3",
9 | "react-dom": "^16.4.2",
10 | "react-redux": "^5.0.7",
11 | "react-router-dom": "^4.3.1",
12 | "react-router-redux": "^5.0.0-alpha.9",
13 | "react-scripts": "1.1.5",
14 | "redux": "^4.0.0",
15 | "redux-thunk": "^2.3.0"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "test": "react-scripts test --env=jsdom",
21 | "eject": "react-scripts eject"
22 | },
23 | "devDependencies": {
24 | "dotenv": "^6.0.0",
25 | "eslint": "^5.6.1",
26 | "eslint-config-airbnb": "^17.1.0",
27 | "eslint-plugin-import": "^2.14.0",
28 | "eslint-plugin-jsx-a11y": "^6.1.1",
29 | "eslint-plugin-react": "^7.11.1",
30 | "fetch-mock": "^7.0.2",
31 | "node-fetch": "^2.2.0",
32 | "redux-mock-store": "^1.5.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/trader-app-ui/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pivotalsoftware/ESarch/c6e2f272b3cb99b414bb1e1b0456c9d0f3efe359/trader-app-ui/public/favicon.ico
--------------------------------------------------------------------------------
/trader-app-ui/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
25 | Axon Trader
26 |
27 |
28 |
29 | You need to enable JavaScript to run this app.
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/trader-app-ui/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/trader-app-ui/src/actions/home.js:
--------------------------------------------------------------------------------
1 | import {
2 | FETCH_USERS_REQUEST,
3 | FETCH_USERS_SUCCESS,
4 | FETCH_USERS_FAILURE,
5 | SET_IMPERSONATED_USER,
6 | LOGOUT_USER,
7 | } from '../constants/homeActions';
8 | import { status, json } from '../utils/fetch';
9 | import { ApiConfig } from '../utils/config';
10 |
11 | const API_ROOT = ApiConfig();
12 |
13 | export const fetchUsersRequest = () => (
14 | {
15 | type: FETCH_USERS_REQUEST,
16 | payload: {
17 | isFetching: true,
18 | },
19 | }
20 | );
21 |
22 | export const fetchUsersSuccess = data => (
23 | {
24 | type: FETCH_USERS_SUCCESS,
25 | payload: {
26 | isFetching: false,
27 | data,
28 | },
29 | }
30 | );
31 |
32 | export const fetchUsersFailure = error => (
33 | {
34 | type: FETCH_USERS_FAILURE,
35 | payload: {
36 | isFetching: false,
37 | error,
38 | },
39 | }
40 | );
41 |
42 | export const validImpersonatedUser = (userList, currentImpersonatedUserId) => {
43 | for (const user of userList) {
44 | if (user.identifier === currentImpersonatedUserId) {
45 | return true;
46 | }
47 | }
48 | return false;
49 | };
50 |
51 | export const getUsers = () => (dispatch, getState) => {
52 | const appState = getState();
53 |
54 | dispatch(fetchUsersRequest());
55 |
56 | const options = {
57 | method: 'GET',
58 | headers: {
59 | 'Content-Type': 'application/json',
60 | },
61 | };
62 |
63 | return fetch(`${API_ROOT}/query/user`, options)
64 | .then(status)
65 | .then(json)
66 | .then((data) => {
67 | // got a successfull response from the server
68 |
69 | // verify the currently impersonated user is valid
70 | if (appState.home.impersonatedUser && appState.home.impersonatedUser.identifier) {
71 | const impersonatedUserId = appState.home.impersonatedUser.identifier;
72 | if (!validImpersonatedUser(data, impersonatedUserId)) {
73 | console.log('invalid impersonated user. logging out..');
74 | dispatch({
75 | type: LOGOUT_USER,
76 | });
77 | }
78 | }
79 | dispatch(fetchUsersSuccess(data));
80 | })
81 | .catch((error) => {
82 | // bad response
83 | dispatch(fetchUsersFailure(error));
84 | });
85 | };
86 |
87 | export function setImpersonatedUser(user) {
88 | // todo redirect to logout page
89 | return {
90 | type: SET_IMPERSONATED_USER,
91 | payload: {
92 | impersonatedUser: user,
93 | },
94 | };
95 | }
96 |
--------------------------------------------------------------------------------
/trader-app-ui/src/actions/portfolio.test.js:
--------------------------------------------------------------------------------
1 | import {
2 | fetchPortfolioRequest,
3 | fetchPortfolioSuccess,
4 | fetchPortfolioFailure,
5 | } from './portfolio';
6 | import * as actions from '../constants/portfolioActions';
7 |
8 | describe('portfolioActions: fetch portfolio request', () => {
9 | it('should create an action to request a portfolio', () => {
10 | const expectedAction = {
11 | type: actions.FETCH_PORTFOLIO_REQUEST,
12 | payload: {
13 | isFetching: true,
14 | },
15 | };
16 | expect(fetchPortfolioRequest()).toEqual(expectedAction);
17 | });
18 | });
19 |
20 | describe('portfolioActions: fetch portfolio success', () => {
21 | it('should create an action to handle when a portfolio is fetched successfully', () => {
22 | const response = [
23 | {
24 | identifier: 'safh43fh1234',
25 | userId: '456456757dfgd43',
26 | amountOfMoney: '2000',
27 | },
28 | ];
29 | const expectedAction = {
30 | type: actions.FETCH_PORTFOLIO_SUCCESS,
31 | payload: {
32 | isFetching: false,
33 | data: response,
34 | },
35 | };
36 | expect(fetchPortfolioSuccess(response)).toEqual(expectedAction);
37 | });
38 | });
39 |
40 | describe('portfolioActions: fetch portfolio failure', () => {
41 | it('should create an action to handle errors when fetching a portfolio', () => {
42 | const error = {
43 | message: 'Not Found',
44 | status: 404,
45 | };
46 | const expectedAction = {
47 | type: actions.FETCH_PORTFOLIO_FAILURE,
48 | payload: {
49 | isFetching: false,
50 | error,
51 | },
52 | };
53 | expect(fetchPortfolioFailure(error)).toEqual(expectedAction);
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/trader-app-ui/src/app.scss:
--------------------------------------------------------------------------------
1 | .appBody {
2 | margin: 100px;
3 | background: white;
4 | overflow: auto;
5 | }
--------------------------------------------------------------------------------
/trader-app-ui/src/assets/home-background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pivotalsoftware/ESarch/c6e2f272b3cb99b414bb1e1b0456c9d0f3efe359/trader-app-ui/src/assets/home-background.png
--------------------------------------------------------------------------------
/trader-app-ui/src/assets/home-foreground-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pivotalsoftware/ESarch/c6e2f272b3cb99b414bb1e1b0456c9d0f3efe359/trader-app-ui/src/assets/home-foreground-image.png
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Company/DataTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const dataTable = props => (
4 |
5 |
6 |
7 |
8 | Count
9 | Price
10 |
11 |
12 |
13 | {
14 | props.data && props.data.map((item, index) => {
15 | const className = index % 2 === 0 ? 'list-row-white' : 'list-row-gray';
16 | const price = item.itemPrice !== undefined ? item.itemPrice : item.tradePrice;
17 |
18 | return (
19 |
20 | {item.itemsRemaining.toLocaleString('en')}
21 | {price.toLocaleString('en', { style: 'currency', currency: 'USD' })}
22 |
23 | );
24 | })
25 | }
26 |
27 |
28 |
29 | );
30 |
31 | export default dataTable;
32 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Dashboard/Portfolio.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Portfolio = (props) => {
4 | const moneyFormatOptions = { style: 'currency', currency: 'USD' };
5 |
6 | return (
7 |
8 |
{props.title}
9 |
10 |
{props.description}
11 |
12 |
13 |
14 | Money
15 |
16 |
17 |
18 |
19 |
20 | Available
21 | {(props.moneyAvailable).toLocaleString('en', moneyFormatOptions)}
22 |
23 |
24 | Reserved
25 | {(props.reserved).toLocaleString('en', moneyFormatOptions)}
26 |
27 |
28 |
29 |
30 | );
31 | };
32 |
33 | export default Portfolio;
34 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Dashboard/TradeItemsContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const TradeItemsContainer = (props) => {
4 | let className = 'container-trade-items';
5 |
6 | if (props.className) {
7 | className += ` ${props.className}`;
8 | }
9 |
10 | return (
11 |
12 | {props.children}
13 |
14 | );
15 | };
16 |
17 | export default TradeItemsContainer;
18 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Dashboard/TradeItemsTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const TradeItemsTable = ({ items, tableName }) => (
4 |
5 |
{tableName}
6 |
7 |
8 |
9 | Name
10 | Amount
11 |
12 |
13 |
14 | {
15 | Object.keys(items).map((key, index) => (
16 |
17 |
18 | {' '}
19 | {items[key].companyName}
20 |
21 |
22 | {' '}
23 | {items[key].amount.toLocaleString('en')}
24 |
25 |
26 | ))
27 | }
28 |
29 |
30 |
31 | );
32 |
33 | export default TradeItemsTable;
34 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Dashboard/Transactions.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import formatTransactionState from '../../utils/transactions';
3 |
4 | const Transactions = ({ transactions, title, description }) => (
5 |
6 |
{title}
7 |
8 |
{description}
9 |
10 |
11 |
12 |
13 | Name
14 | Type
15 | # Shares
16 | Price
17 | Total Price
18 | Shares Executed
19 | State
20 |
21 |
22 |
23 | {
24 | transactions.map(transaction => (
25 |
26 | {transaction.companyName}
27 | {transaction.type}
28 | {transaction.amountOfItems.toLocaleString('en')}
29 | {(transaction.pricePerItem).toLocaleString('en', { style: 'currency', currency: 'USD' })}
30 | {(transaction.amountOfItems * transaction.pricePerItem).toLocaleString('en', { style: 'currency', currency: 'USD' })}
31 | {transaction.amountOfExecutedItems.toLocaleString('en')}
32 | {formatTransactionState(transaction.state)}
33 |
34 | ))
35 | }
36 |
37 |
38 |
39 |
40 | );
41 |
42 | export default Transactions;
43 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Dashboard/styles.css:
--------------------------------------------------------------------------------
1 | .container-trade-items {
2 | padding: 40px 76px;
3 | background-color: #00ACE7;
4 | }
5 |
6 | .dashboard-header {
7 | font-family: Roboto, Arial, Helvetica, sans-serif;
8 | font-size: 64px;
9 | font-weight: bold;
10 | font-style: normal;
11 | font-stretch: normal;
12 | line-height: normal;
13 | letter-spacing: 0px;
14 | color: #01ade5;
15 | }
16 |
17 | .dashboard-subheader {
18 | font-family: Montserrat, Arial, Helvetica, sans-serif;
19 | font-size: 24px;
20 | font-weight: 600;
21 | font-style: normal;
22 | font-stretch: normal;
23 | line-height: 1.38;
24 | letter-spacing: normal;
25 | color: #212121;
26 | }
27 |
28 | .portfolio-title {
29 | font-family: Roboto, Arial, Helvetica, sans-serif;
30 | font-size: 24px;
31 | font-weight: bold;
32 | font-style: normal;
33 | font-stretch: normal;
34 | line-height: normal;
35 | letter-spacing: 0px;
36 | color: #00253e;
37 | }
38 |
39 | .portfolio-subtitle {
40 | font-family: Montserrat, Arial, Helvetica, sans-serif;
41 | font-size: 16px;
42 | font-weight: normal;
43 | font-style: normal;
44 | font-stretch: normal;
45 | line-height: 1.63;
46 | letter-spacing: normal;
47 | color: #212121;
48 | }
49 |
50 | .trade-items-title {
51 | font-family: Roboto, Arial, Helvetica, sans-serif;
52 | font-size: 24px;
53 | font-weight: bold;
54 | font-style: normal;
55 | font-stretch: normal;
56 | line-height: normal;
57 | letter-spacing: 0px;
58 | color: #ffffff;
59 | }
60 |
61 | .dashboard-divider {
62 | background-color: #212121;
63 | height: 3px;
64 | width: 300px;
65 | margin-bottom: 18px;
66 | }
67 |
68 | .dashboard-table {
69 | border-collapse: collapse;
70 | font-family: Montserrat, Arial, Helvetica, sans-serif;
71 | font-size: 16px;
72 | font-style: normal;
73 | font-stretch: normal;
74 | line-height: normal;
75 | letter-spacing: 0px;
76 | color: rgba(0, 37, 62, 0.95);
77 | }
78 |
79 | .dashboard-table th {
80 | background-color: #f6f6f6;
81 | font-weight: 600;
82 | }
83 |
84 | .dashboard-trades-table {
85 | border-collapse: collapse;
86 | font-family: Montserrat;
87 | font-size: 16px;
88 | font-style: normal;
89 | font-stretch: normal;
90 | line-height: normal;
91 | letter-spacing: 0px;
92 | color: #ffffff;
93 | opacity: 0.8;
94 | }
95 |
96 | .dashboard-trades-table th {
97 | background-color: #7FD5F0;
98 | font-weight: 600;
99 | }
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Home/Banner.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | const Banner = () => (
5 |
6 |
The Trader
7 |
8 | Welcome to the Axon Trader application.
9 | Axon is the leading open source CQRS and Event Sourcing framework for
10 |
11 | Spring Boot
12 | .
13 | Axon Trader showcases various
14 | {' '}
15 | Axon Framework
16 | capabilities and it is built for
17 | {' '}
18 | Pivotal Application Service
19 | (provided by
20 | {' '}
21 | Pivotal Web Services
22 | ).
23 | For more information see
24 | {' '}
25 | code
26 | {' '}
27 | and
28 | {' '}
29 | wiki
30 | {' '}
31 | on GitHub.
32 |
33 |
34 | Choose a user to impersonate below, then go to your account dashboard.
35 |
36 |
DASHBOARD
37 |
38 | );
39 |
40 | export default Banner;
41 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Home/CredentialsTable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const CredentialsTable = ({ credentials, onSetImpersonatedUser }) =>
4 |
5 |
Available Credentials
6 |
7 |
8 |
9 |
10 | Username
11 | Full Name
12 |
13 |
14 |
15 |
16 | {credentials &&
17 | credentials.sort((a, b) => {
18 | return a.userName > b.userName;
19 | }).map(user => {
20 | return (
21 |
22 | {user.userName}
23 | {user.fullName}
24 |
25 | onSetImpersonatedUser(user)}>Impersonate User
28 |
29 |
30 | );
31 | })
32 | }
33 |
34 |
35 |
36 |
;
37 |
38 | export default CredentialsTable;
39 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Home/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Header = () => (
4 |
5 |
Welcome
6 |
Have fun playing with the trader
7 |
8 |
9 | There are a few things implemented.
10 | You can choose the company to trade stock shares for.
11 | Before you can use them you need to login.
12 |
13 |
14 | );
15 |
16 | export default Header;
17 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Home/SideBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import SideBarItem from './SideBarItem';
3 |
4 | const data = [
5 | {
6 | header: 'Check the stocks',
7 | description: 'If you have logged in, you can go to the companies',
8 | linkTo: '/orderbooks',
9 | linkName: 'To the stocks',
10 | },
11 | ];
12 |
13 | const SideBar = () => (
14 |
15 | {
16 | data.map(item => (
17 |
24 | ))
25 | }
26 |
27 | );
28 |
29 | export default SideBar;
30 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Home/SideBarItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | const SideBarItem = props => (
5 |
6 |
{props.header}
7 |
{props.description}
8 |
{props.linkName}
9 |
10 | );
11 |
12 | export default SideBarItem;
13 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Home/credentials.js:
--------------------------------------------------------------------------------
1 | const credentials = [
2 | { userName: 'buyer1', password: 'buyer1' },
3 | { userName: 'buyer2', password: 'buyer2' },
4 | { userName: 'buyer3', password: 'buyer3' },
5 | { userName: 'buyer4', password: 'buyer4' },
6 | { userName: 'buyer5', password: 'buyer5' },
7 | { userName: 'buyer6', password: 'buyer6' },
8 | ];
9 |
10 | export default credentials;
11 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Home/img/fondo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pivotalsoftware/ESarch/c6e2f272b3cb99b414bb1e1b0456c9d0f3efe359/trader-app-ui/src/components/Home/img/fondo.jpg
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Home/img/fondo@2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pivotalsoftware/ESarch/c6e2f272b3cb99b414bb1e1b0456c9d0f3efe359/trader-app-ui/src/components/Home/img/fondo@2x.jpg
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Home/img/fondo@3x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pivotalsoftware/ESarch/c6e2f272b3cb99b414bb1e1b0456c9d0f3efe359/trader-app-ui/src/components/Home/img/fondo@3x.jpg
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Home/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import CredentialsTable from './CredentialsTable';
3 | import './styles.css';
4 | import { bindActionCreators } from 'redux'
5 | import { connect } from 'react-redux'
6 | import SideBar from './SideBar';
7 | import Banner from './Banner';
8 | import Header from './Header';
9 | import * as homeActionCreators from '../../actions/home'
10 |
11 | class Home extends Component {
12 |
13 | componentDidMount() {
14 | this.props.homeActions.getUsers();
15 | }
16 |
17 | render() {
18 | const { users } = this.props;
19 |
20 | return (
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
46 |
47 |
48 | )
49 | }
50 |
51 | setImpersonatedUser = (user) => {
52 | this.props.homeActions.setImpersonatedUser(user);
53 | this.props.history.push('/dashboard');
54 | }
55 | }
56 |
57 | const mapDispatchToProps = (dispatch) => {
58 | return {
59 | homeActions: bindActionCreators(homeActionCreators, dispatch)
60 | }
61 | }
62 |
63 | const mapStateToProps = state => {
64 | return {
65 | users: state.home.users
66 | }
67 | }
68 |
69 | export default connect(mapStateToProps, mapDispatchToProps)(Home);
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Loader/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './styles.css';
3 |
4 | const Loader = (props) => {
5 | let className = 'axon-loader text-center';
6 |
7 | if (props.className) {
8 | className += ` ${props.className}`;
9 | }
10 |
11 | return (
12 |
13 | );
14 | };
15 |
16 | export default Loader;
17 |
--------------------------------------------------------------------------------
/trader-app-ui/src/components/Loader/styles.css:
--------------------------------------------------------------------------------
1 | .axon-loader {
2 | border: 16px solid transparent;
3 | border-radius: 50%;
4 | border-top: 16px solid #01ade5;
5 | width: 120px;
6 | height: 120px;
7 | -webkit-animation: spin 1s linear infinite; /* Safari */
8 | animation: spin 1s linear infinite;
9 | }
10 |
11 | /* Safari */
12 | @-webkit-keyframes spin {
13 | 0% { -webkit-transform: rotate(0deg); }
14 | 100% { -webkit-transform: rotate(360deg); }
15 | }
16 |
17 | @keyframes spin {
18 | 0% { transform: rotate(0deg); }
19 | 100% { transform: rotate(360deg); }
20 | }
21 |
--------------------------------------------------------------------------------
/trader-app-ui/src/constants.js:
--------------------------------------------------------------------------------
1 | export const Constants = {
2 | DEFAULT: 'Default',
3 | RESERVED: 'Reserved',
4 | };
5 |
--------------------------------------------------------------------------------
/trader-app-ui/src/constants/companyActions.js:
--------------------------------------------------------------------------------
1 | export const FETCH_COMPANY_REQUEST = 'FETCH_COMPANY_REQUEST';
2 | export const FETCH_COMPANY_SUCCESS = 'FETCH_COMPANY_SUCCESS';
3 | export const FETCH_COMPANY_FAILURE = 'FETCH_COMPANY_FAILURE';
4 | export const FETCH_COMPANY_LIST_REQUEST = 'FETCH_COMPANY_LIST_REQUEST';
5 | export const FETCH_COMPANY_LIST_SUCCESS = 'FETCH_COMPANY_LIST_SUCCESS';
6 | export const FETCH_COMPANY_LIST_FAILURE = 'FETCH_COMPANY_LIST_FAILURE';
7 | export const FETCH_ORDERBOOKS_BY_COMPANYID_REQUEST = 'FETCH_ORDERBOOKS_BY_COMPANYID_REQUEST';
8 | export const FETCH_ORDERBOOKS_BY_COMPANYID_SUCCESS = 'FETCH_ORDERBOOKS_BY_COMPANYID_SUCCESS';
9 | export const FETCH_ORDERBOOKS_BY_COMPANYID_FAILURE = 'FETCH_ORDERBOOKS_BY_COMPANYID_FAILURE';
10 | export const PLACE_BUY_ORDER_REQUEST = 'PLACE_BUY_ORDER_REQUEST';
11 | export const PLACE_BUY_ORDER_SUCCESS = 'PLACE_BUY_ORDER_SUCCESS';
12 | export const PLACE_BUY_ORDER_FAILURE = 'PLACE_BUY_ORDER_FAILURE';
13 | export const PLACE_SELL_ORDER_REQUEST = 'PLACE_SELL_ORDER_REQUEST';
14 | export const PLACE_SELL_ORDER_SUCCESS = 'PLACE_SELL_ORDER_SUCCESS';
15 | export const PLACE_SELL_ORDER_FAILURE = 'PLACE_SELL_ORDER_FAILURE';
16 | export const SET_SSE_ORDERBOOK_DATA = 'SET_SSE_ORDERBOOK_DATA';
17 |
--------------------------------------------------------------------------------
/trader-app-ui/src/constants/homeActions.js:
--------------------------------------------------------------------------------
1 | export const FETCH_USERS_REQUEST = 'FETCH_USERS_REQUEST';
2 | export const FETCH_USERS_SUCCESS = 'FETCH_USERS_SUCCESS';
3 | export const FETCH_USERS_FAILURE = 'FETCH_USERS_FAILURE';
4 | export const SET_IMPERSONATED_USER = 'SET_IMPERSONATED_USER';
5 | export const LOGOUT_USER = 'LOGOUT_USER';
6 |
--------------------------------------------------------------------------------
/trader-app-ui/src/constants/portfolioActions.js:
--------------------------------------------------------------------------------
1 | export const FETCH_PORTFOLIO_REQUEST = 'FETCH_PORTFOLIO_REQUEST';
2 | export const FETCH_PORTFOLIO_SUCCESS = 'FETCH_PORTFOLIO_SUCCESS';
3 | export const FETCH_PORTFOLIO_FAILURE = 'FETCH_PORTFOLIO_FAILURE';
4 | export const FETCH_TRANSACTIONS_BY_PORTFOLIOID_REQUEST = 'FETCH_TRANSACTIONS_BY_PORTFOLIOID_REQUEST';
5 | export const FETCH_TRANSACTIONS_BY_PORTFOLIOID_SUCCESS = 'FETCH_TRANSACTIONS_BY_PORTFOLIOID_SUCCESS';
6 | export const FETCH_TRANSACTIONS_BY_PORTFOLIOID_FAILURE = 'FETCH_TRANSACTIONS_BY_PORTFOLIOID_FAILURE';
7 |
--------------------------------------------------------------------------------
/trader-app-ui/src/containers/App/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './index';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render( , div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/trader-app-ui/src/containers/App/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Provider } from 'react-redux';
3 | import { createStore, applyMiddleware, compose } from 'redux';
4 | import thunk from 'redux-thunk';
5 | import createHistory from 'history/createBrowserHistory';
6 | import { ConnectedRouter, routerMiddleware } from 'react-router-redux';
7 | import { Route, Switch } from 'react-router-dom';
8 | import NavbarContainer from '../NavbarContainer';
9 | import LoginContainer from '../LoginContainer';
10 | import Home from '../../components/Home';
11 | import DashboardContainer from '../DashboardContainer';
12 | import Companies from '../CompanyListContainer/CompanyListContainer';
13 | import SecureRoute from '../SecureRoute';
14 | import CompanyContainer from '../CompanyContainer';
15 | import rootReducer from '../../reducers';
16 | import { scrollToAnimated } from '../../utils/animation';
17 | import { loadState, saveState } from './sessionStorage';
18 |
19 | const history = createHistory();
20 | const middleware = routerMiddleware(history);
21 | const persistedState = loadState();
22 |
23 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
24 |
25 | history.listen((location) => {
26 | if (location.pathname === '/' && location.hash === '#credentials') {
27 | setTimeout(() => {
28 | scrollToAnimated(document.body.scrollHeight, 300);
29 | }, 50);
30 | } else {
31 | window.scrollTo(0, 0);
32 | }
33 | });
34 |
35 | const store = createStore(
36 | rootReducer,
37 | persistedState,
38 | composeEnhancers(applyMiddleware(thunk, middleware)),
39 | );
40 |
41 | store.subscribe(() => {
42 | saveState(store.getState());
43 | });
44 |
45 | export default class App extends Component {
46 | render() {
47 | return (
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/trader-app-ui/src/containers/App/sessionStorage.js:
--------------------------------------------------------------------------------
1 | export const loadState = () => {
2 | try {
3 | const serializedHomeState = sessionStorage.getItem('axonHomeState');
4 | const serializedPortfolioState = sessionStorage.getItem('axonPortfolioState');
5 | const initialState = {};
6 |
7 | if (serializedHomeState == null && serializedPortfolioState == null) {
8 | return undefined;
9 | }
10 |
11 | if (serializedHomeState) {
12 | initialState.home = JSON.parse(serializedHomeState);
13 | }
14 |
15 | if (serializedPortfolioState) {
16 | initialState.portfolio = JSON.parse(serializedPortfolioState);
17 | }
18 | return initialState;
19 | } catch (err) {
20 | return undefined;
21 | }
22 | };
23 |
24 | export const saveState = (state) => {
25 | try {
26 | // save state.home (authentication info) in sessionStorage
27 | const serializedHomeState = JSON.stringify(state.home);
28 | sessionStorage.setItem('axonHomeState', serializedHomeState);
29 |
30 | const serializedPortfolioState = JSON.stringify(state.portfolio);
31 | sessionStorage.setItem('axonPortfolioState', serializedPortfolioState);
32 | } catch (err) {
33 | // ignore write errors
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/trader-app-ui/src/containers/CompanyListContainer/CompanyListContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { bindActionCreators } from 'redux';
3 | import { connect } from 'react-redux';
4 | import CompanyList from '../../components/Company/CompanyList';
5 | import * as companyActionCreators from '../../actions/company';
6 | import './styles.css';
7 |
8 | class CompanyListContainer extends Component {
9 | componentDidMount() {
10 | this.props.companyActions.fetchCompanyList();
11 | }
12 |
13 | render() {
14 | return (
15 |
16 |
All stock items
17 |
Choose the stock to start trading with
18 |
19 |
You can sort the table by clicking on the headers
20 |
21 |
22 |
23 |
24 | );
25 | }
26 | }
27 |
28 | const mapDispatchToProps = dispatch => ({
29 | companyActions: bindActionCreators(companyActionCreators, dispatch),
30 | });
31 |
32 | const mapStateToProps = state => ({
33 | companies: state.companies,
34 | });
35 |
36 | export default connect(mapStateToProps, mapDispatchToProps)(CompanyListContainer);
37 |
--------------------------------------------------------------------------------
/trader-app-ui/src/containers/CompanyListContainer/styles.css:
--------------------------------------------------------------------------------
1 | .company-list-container {
2 | padding-top: 20px;
3 | }
4 |
5 | .company-list-title {
6 | margin-top: 66px;
7 | font-size: 50px;
8 | color: #00253E;
9 | font-family: Roboto;
10 | font-weight: bold;
11 | font-style: normal;
12 | }
13 |
14 | .company-list-subtitle {
15 | margin-top: 22px;
16 | font-family: Montserrat;
17 | font-size: 20px;
18 | font-weight: 600;
19 | line-height: 1.08;
20 | color: #01ADE5;
21 | }
22 |
23 | .small-divider {
24 | background-color: #212121;
25 | height: 3px;
26 | width: 22px;
27 | margin-bottom: 15px;
28 | }
29 |
30 | .company-list-sort-text {
31 | font-family: Montserrat;
32 | font-size: 14px;
33 | font-weight: normal;
34 | font-style: normal;
35 | font-stretch: normal;
36 | line-height: 1.63;
37 | letter-spacing: normal;
38 | color: #00253e;
39 | margin-bottom: 35px;
40 | }
--------------------------------------------------------------------------------
/trader-app-ui/src/containers/DashboardContainer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { bindActionCreators } from 'redux';
3 | import { connect } from 'react-redux';
4 | import Dashboard from '../components/Dashboard';
5 | import * as portfolioActionCreators from '../actions/portfolio';
6 |
7 | class DashboardContainer extends Component {
8 | componentDidMount() {
9 | this.props.portfolioActions.getPortfolioAndItsTransactions(this.props.impersonatedUser.userId);
10 | }
11 |
12 | render() {
13 | const { portfolio } = this.props;
14 | return (
15 |
16 |
17 |
18 | );
19 | }
20 | }
21 |
22 | const mapDispatchToProps = dispatch => ({
23 | portfolioActions: bindActionCreators(portfolioActionCreators, dispatch),
24 | });
25 |
26 | const mapStateToProps = state => ({
27 | portfolio: state.portfolio,
28 | impersonatedUser: state.home.impersonatedUser,
29 | });
30 |
31 | export default connect(mapStateToProps, mapDispatchToProps)(DashboardContainer);
32 |
--------------------------------------------------------------------------------
/trader-app-ui/src/containers/LoginContainer/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { Redirect, Link } from 'react-router-dom';
4 | import './styles.css';
5 |
6 | class LoginContainer extends Component {
7 | render() {
8 | const { impersonatedUser } = this.props;
9 |
10 | if (impersonatedUser) {
11 | return ;
12 | }
13 |
14 | return (
15 |
16 |
17 | Please
18 | {' '}
19 | impersonate a user
20 | {' '}
21 | to access this page
22 |
23 |
24 | );
25 | }
26 | }
27 |
28 | function matchStateToProps(state) {
29 | return {
30 | impersonatedUser: state.home.impersonatedUser,
31 | };
32 | }
33 |
34 | export default connect(matchStateToProps)(LoginContainer);
35 |
--------------------------------------------------------------------------------
/trader-app-ui/src/containers/LoginContainer/styles.css:
--------------------------------------------------------------------------------
1 | .auth-error-container {
2 | height: calc(100vh - 80px);
3 | display: flex;
4 | justify-content: center;
5 | align-items: center;
6 | }
7 |
8 | .auth-error-message {
9 | font-family: Montserrat;
10 | font-size: 30px;
11 | color: #212121;
12 | text-align: center;
13 | }
14 |
15 | .auth-error-message.link {
16 | color: #01ade5;
17 | }
--------------------------------------------------------------------------------
/trader-app-ui/src/containers/NavbarContainer/styles.css:
--------------------------------------------------------------------------------
1 | /* customized bootstrap navbar */
2 |
3 | .bg-axon {
4 | background-color: #340976;
5 | }
6 |
7 | .navbar-item-list {
8 | margin-left: 100px;
9 | }
10 |
11 | .navbar-nav .nav-item {
12 | margin-right: 42px;
13 | }
14 |
15 | .current-username {
16 | font-family: Montserrat;
17 | font-size: 16px;
18 | color: white;
19 | margin: 0 20px;
20 | }
21 |
22 | /* customize the navbar link */
23 | .bg-axon .navbar-nav .nav-link {
24 | font-family: Montserrat;
25 | font-size: 12px;
26 | font-weight: 600;
27 | font-style: normal;
28 | font-stretch: normal;
29 | line-height: normal;
30 | letter-spacing: 0px;
31 | color: #ffffff;
32 | padding-left: 0;
33 | padding-right: 0;
34 | padding-bottom: 3px;
35 | }
36 |
37 | /* customize active or hovered navbar links */
38 | .bg-axon .nav-item .nav-link.active,
39 | .bg-axon .nav-item:hover .nav-link.active {
40 | color: #ffffff;
41 | border-bottom: 1px solid #ffffff;
42 | opacity: 1.0;
43 | }
44 |
45 | .bg-axon .nav-item:hover .nav-link {
46 | opacity: 0.5;
47 | }
48 |
49 | /* customize the brand */
50 | .bg-axon .navbar-brand,
51 | .bg-axon .navbar-text {
52 | font-weight: bold;
53 | font-family: Roboto, Arial, sans-serif;
54 | font-size: 22px;
55 | font-weight: bold;
56 | font-style: normal;
57 | font-stretch: normal;
58 | line-height: normal;
59 | letter-spacing: 0px;
60 | color: #ffffff;
61 | }
62 |
63 | /* Medium devices (tablets, less than 992px) - bootstrap responsive breakpoint */
64 | @media (max-width: 991.98px) {
65 | .navbar-item-list {
66 | margin-left: 0;
67 | }
68 |
69 | .current-username {
70 | margin: 20px 0;
71 | }
72 |
73 | .bg-axon .nav-item .nav-link.active,
74 | .bg-axon .nav-item:hover .nav-link.active {
75 | color: #01ade5;
76 | border-bottom: none;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/trader-app-ui/src/containers/SecureRoute.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { Route, Redirect } from 'react-router-dom';
4 |
5 | const SecureRoute = ({ component: ComposedComponent, ...rest }) => {
6 | class Authentication extends Component {
7 | // redirect if not authenticated; otherwise, return the component passed into
8 | handleRender(props) {
9 | if (!this.props.impersonatedUser) {
10 | return ;
11 | }
12 | return ;
13 | }
14 |
15 | render() {
16 | return (
17 |
18 | );
19 | }
20 | }
21 |
22 | function mapStateToProps(state) {
23 | return {
24 | impersonatedUser: state.home.impersonatedUser,
25 | };
26 | }
27 |
28 | const AuthenticationContainer = connect(mapStateToProps)(Authentication);
29 | return ;
30 | };
31 |
32 | export default SecureRoute;
33 |
--------------------------------------------------------------------------------
/trader-app-ui/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | height: 100vh;
3 | margin: 0;
4 | padding: 0;
5 | font-family: Roboto, Arial, sans-serif;
6 | }
7 |
8 | .text-centering {
9 | text-align: center;
10 | }
11 |
12 | .centered-loader {
13 | position: absolute;
14 | margin: 0 auto;
15 | left: 50%;
16 | top: 50%;
17 | margin-left: -80px;
18 | }
19 |
20 | .small-divider-white {
21 | background-color: #FFFFFF;
22 | height: 3px;
23 | width: 22px;
24 | margin-bottom: 15px;
25 | }
26 |
27 | .btn-primary.axon-button {
28 | background-color: #01ade5;
29 | border-color: #01ade5;
30 | color: white;
31 | font-family: Montserrat;
32 | font-size: 12px;
33 | font-weight: 600;
34 | }
35 |
36 | .btn-light.axon-button {
37 | background-color: transparent;
38 | border-color: white;
39 | color: white;
40 | font-family: Montserrat;
41 | font-size: 12px;
42 | }
43 |
44 | .btn-light.axon-button:hover {
45 | background-color: white;
46 | color: #01ade5;
47 | }
48 |
49 | .axon-error {
50 | font-family: Montserrat, Arial, Helvetica, sans-serif;
51 | font-size: 24px;
52 | font-weight: 600;
53 | font-style: normal;
54 | font-stretch: normal;
55 | line-height: 1.38;
56 | letter-spacing: normal;
57 | color: #e74c3c;
58 | }
59 |
60 | .table-responsive {
61 | max-height: 500px;
62 | overflow-y:auto;
63 | }
64 |
--------------------------------------------------------------------------------
/trader-app-ui/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './containers/App';
5 | import registerServiceWorker from './registerServiceWorker';
6 |
7 | if (process.env.NODE_ENV !== 'production') {
8 | require('dotenv').load();
9 | }
10 |
11 | ReactDOM.render( , document.getElementById('root'));
12 | registerServiceWorker();
13 |
--------------------------------------------------------------------------------
/trader-app-ui/src/reducers/home.js:
--------------------------------------------------------------------------------
1 | import {
2 | FETCH_USERS_REQUEST,
3 | FETCH_USERS_SUCCESS,
4 | FETCH_USERS_FAILURE,
5 | SET_IMPERSONATED_USER,
6 | LOGOUT_USER,
7 | } from '../constants/homeActions';
8 |
9 | const initialState = {
10 | isFetching: false,
11 | error: null,
12 | users: [],
13 | impersonatedUser: null,
14 | };
15 |
16 | export default function homeReducer(state = initialState, action) {
17 | switch (action.type) {
18 | case FETCH_USERS_REQUEST:
19 | return {
20 | ...state,
21 | isFetching: action.payload.isFetching,
22 | error: null,
23 | };
24 | case FETCH_USERS_SUCCESS:
25 | return {
26 | ...state,
27 | isFetching: action.payload.isFetching,
28 | users: action.payload.data,
29 | };
30 | case FETCH_USERS_FAILURE:
31 | return {
32 | ...state,
33 | isFetching: action.payload.isFetching,
34 | error: action.payload.error,
35 | };
36 | case SET_IMPERSONATED_USER:
37 | return {
38 | ...state,
39 | impersonatedUser: action.payload.impersonatedUser,
40 | };
41 | case LOGOUT_USER:
42 | return {
43 | ...state,
44 | impersonatedUser: null,
45 | };
46 | default:
47 | return state;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/trader-app-ui/src/reducers/home.test.js:
--------------------------------------------------------------------------------
1 | import homeReducer from './home';
2 | import * as actions from '../constants/homeActions';
3 |
4 | describe('home reducer', () => {
5 | it('should return the initial state', () => {
6 | expect(homeReducer(undefined, {})).toEqual(
7 | {
8 | isFetching: false,
9 | error: null,
10 | users: [],
11 | impersonatedUser: null,
12 | },
13 | );
14 | });
15 |
16 | it('should handle FETCH_USERS_REQUEST', () => {
17 | expect(homeReducer({}, {
18 | type: actions.FETCH_USERS_REQUEST,
19 | payload: {
20 | isFetching: true,
21 | },
22 | })).toEqual(
23 | {
24 | isFetching: true,
25 | error: null,
26 | },
27 | );
28 | });
29 |
30 | it('should handle FETCH_USERS_SUCCESS', () => {
31 | expect(homeReducer({}, {
32 | type: actions.FETCH_USERS_SUCCESS,
33 | payload: {
34 | isFetching: true,
35 | data: [
36 | { identifier: '9834kdjshg', name: 'Buyer One' },
37 | { identifier: 'dfsdf34563', name: 'Buyer Two' },
38 | ],
39 | },
40 | })).toEqual(
41 | {
42 | isFetching: true,
43 | users: [
44 | { identifier: '9834kdjshg', name: 'Buyer One' },
45 | { identifier: 'dfsdf34563', name: 'Buyer Two' },
46 | ],
47 | },
48 | );
49 | });
50 |
51 | it('should handle FETCH_USERS_FAILURE', () => {
52 | expect(homeReducer({}, {
53 | type: actions.FETCH_USERS_FAILURE,
54 | payload: {
55 | isFetching: false,
56 | error: {
57 | message: 'Not Found',
58 | },
59 |
60 | },
61 | })).toEqual(
62 | {
63 | isFetching: false,
64 | error: {
65 | message: 'Not Found',
66 | },
67 | },
68 | );
69 | });
70 |
71 | it('should handle SET_IMPERSONATED_USER', () => {
72 | expect(homeReducer({}, {
73 | type: actions.SET_IMPERSONATED_USER,
74 | payload: {
75 | impersonatedUser: { identifier: 'kdjs333', name: 'Buyer One' },
76 | },
77 | })).toEqual(
78 | {
79 | impersonatedUser: { identifier: 'kdjs333', name: 'Buyer One' },
80 | },
81 | );
82 | });
83 |
84 | it('should handle LOGOUT_USER', () => {
85 | expect(homeReducer({}, {
86 | type: actions.LOGOUT_USER,
87 | })).toEqual(
88 | {
89 | impersonatedUser: null,
90 | },
91 | );
92 | });
93 | });
94 |
--------------------------------------------------------------------------------
/trader-app-ui/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 | import companyReducer from './company';
4 | import homeReducer from './home';
5 | import portfolioReducer from './portfolio';
6 |
7 | const rootReducer = combineReducers({
8 | router: routerReducer,
9 | home: homeReducer,
10 | companies: companyReducer,
11 | portfolio: portfolioReducer,
12 | });
13 |
14 | export default rootReducer;
15 |
--------------------------------------------------------------------------------
/trader-app-ui/src/reducers/portfolio.js:
--------------------------------------------------------------------------------
1 | import {
2 | FETCH_PORTFOLIO_REQUEST,
3 | FETCH_PORTFOLIO_SUCCESS,
4 | FETCH_PORTFOLIO_FAILURE,
5 | FETCH_TRANSACTIONS_BY_PORTFOLIOID_REQUEST,
6 | FETCH_TRANSACTIONS_BY_PORTFOLIOID_SUCCESS,
7 | FETCH_TRANSACTIONS_BY_PORTFOLIOID_FAILURE,
8 | } from '../constants/portfolioActions';
9 |
10 | // expected format of the portfolio
11 |
12 | // const portfolio = {
13 | // identifier: '392473kjshdfjkh',
14 | // userId: '34935kjehsfjkhsd',
15 | // amountOfMoney: 0,
16 | // reservedAmountOfMoney: 0,
17 | // itemsInPossession: {
18 | // '4798375kjhsdfjksh': {
19 | // generatedId: 23,
20 | // identifier: '347293kjdhfjks',
21 | // companyId: '35794skjdhfjkdshjkf',
22 | // companyName: 'Pivotal',
23 | // amount: 1000
24 | // }
25 | // itemsReserved: {},
26 | // }
27 |
28 | const initialState = {
29 | isFetching: false,
30 | error: null,
31 | data: null,
32 | transactions: {
33 | isFetching: false,
34 | error: null,
35 | data: null,
36 | },
37 | };
38 |
39 | export default function portfolioReducer(state = initialState, action) {
40 | switch (action.type) {
41 | case FETCH_PORTFOLIO_REQUEST:
42 | return {
43 | ...state,
44 | isFetching: action.payload.isFetching,
45 | error: null,
46 | };
47 | case FETCH_PORTFOLIO_SUCCESS:
48 | return {
49 | ...state,
50 | isFetching: action.payload.isFetching,
51 | data: action.payload.data,
52 | };
53 | case FETCH_PORTFOLIO_FAILURE:
54 | return {
55 | ...state,
56 | isFetching: action.payload.isFetching,
57 | error: action.payload.error,
58 | };
59 | case FETCH_TRANSACTIONS_BY_PORTFOLIOID_REQUEST:
60 | return {
61 | ...state,
62 | transactions: {
63 | isFetching: true,
64 | error: null,
65 | data: null,
66 | },
67 | };
68 | case FETCH_TRANSACTIONS_BY_PORTFOLIOID_SUCCESS:
69 | return {
70 | ...state,
71 | transactions: {
72 | isFetching: false,
73 | error: null,
74 | data: action.payload.data,
75 | },
76 | };
77 | case FETCH_TRANSACTIONS_BY_PORTFOLIOID_FAILURE:
78 | return {
79 | ...state,
80 | transactions: {
81 | isFetching: false,
82 | error: action.payload.error,
83 | data: null,
84 | },
85 | };
86 | default:
87 | return state;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/trader-app-ui/src/utils/animation.js:
--------------------------------------------------------------------------------
1 | const scrollToAnimated = (scrollTarget, duration) => {
2 | const initialY = document.body.scrollTop;
3 | const baseY = (initialY + scrollTarget) * 0.5;
4 | const difference = initialY - baseY;
5 | const startTime = performance.now();
6 |
7 | const step = () => {
8 | let normalizedTime = (performance.now() - startTime) / duration;
9 | if (normalizedTime > 1) normalizedTime = 1;
10 |
11 | window.scrollTo(0, baseY + difference * Math.cos(normalizedTime * Math.PI));
12 | if (normalizedTime < 1) window.requestAnimationFrame(step);
13 | };
14 |
15 | window.requestAnimationFrame(step);
16 | };
17 |
18 | export { scrollToAnimated };
19 |
--------------------------------------------------------------------------------
/trader-app-ui/src/utils/config.js:
--------------------------------------------------------------------------------
1 | export const HostURLsMapping = {
2 | 'localhost': 'https://esrefarch-demo-trader-app.cfapps.io',
3 | 'esrefarch-demo-trader-ui.cfapps.io': 'https://esrefarch-demo-trader-app.cfapps.io',
4 | 'axontrader.cfapps.io': 'https://esrefarch-demo-trader-app.cfapps.io',
5 | };
6 | export const ApiConfig = () => {
7 | const hostname = window.location.hostname;
8 | const apiURL = HostURLsMapping[hostname];
9 | return apiURL || '';
10 | };
11 |
--------------------------------------------------------------------------------
/trader-app-ui/src/utils/fetch.js:
--------------------------------------------------------------------------------
1 | const status = (response) => {
2 | // status 200 numbers are successfull http responses
3 | if (response.status >= 200 && response.status < 300) {
4 | return Promise.resolve(response);
5 | }
6 | return Promise.reject(new Error(response.statusText));
7 | };
8 |
9 | const json = response => response.json();
10 |
11 | export { status, json };
12 |
--------------------------------------------------------------------------------
/trader-app-ui/src/utils/transactions.js:
--------------------------------------------------------------------------------
1 | const formatTransactionState = (state) => {
2 | const formattedState = state.toLowerCase()
3 | .split('_')
4 | .map(s => s.charAt(0).toUpperCase() + s.substring(1))
5 | .join(' ');
6 |
7 | return formattedState;
8 | };
9 |
10 | export default formatTransactionState;
11 |
--------------------------------------------------------------------------------
/trader-app/manifest.yml:
--------------------------------------------------------------------------------
1 | ---
2 | applications:
3 | - name: esrefarch-demo-trader-app
4 | path: target/trader-app.jar
5 | timeout: 120
6 | instances: 1
7 | buildpacks:
8 | - java_buildpack
9 | health-check-type: port
10 | services:
11 | - appdb
12 | - rabbit
13 | - config
14 | - registry
15 | env:
16 | TRUST_CERTS: api.run.pivotal.io
17 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/AmqpConfiguration.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.app;
2 |
3 | import com.rabbitmq.client.Channel;
4 | import org.axonframework.amqp.eventhandling.AMQPMessageConverter;
5 | import org.axonframework.amqp.eventhandling.spring.SpringAMQPMessageSource;
6 | import org.axonframework.config.EventProcessingConfiguration;
7 | import org.axonframework.eventhandling.EventMessage;
8 | import org.axonframework.messaging.SubscribableMessageSource;
9 | import org.springframework.amqp.core.*;
10 | import org.springframework.amqp.rabbit.annotation.RabbitListener;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.beans.factory.annotation.Qualifier;
13 | import org.springframework.context.annotation.Bean;
14 | import org.springframework.context.annotation.Configuration;
15 | import org.springframework.transaction.annotation.Transactional;
16 |
17 | @Configuration
18 | public class AmqpConfiguration {
19 |
20 | @Autowired
21 | public void defineExchange(AmqpAdmin admin) {
22 | Queue tradesQueue = QueueBuilder.durable("trades").build();
23 | Exchange tradingExchange = ExchangeBuilder.topicExchange("trading-engine-events").build();
24 |
25 | admin.declareExchange(tradingExchange);
26 | admin.declareQueue(tradesQueue);
27 | admin.declareBinding(BindingBuilder.bind(tradesQueue)
28 | .to(tradingExchange)
29 | .with("#")
30 | .noargs());
31 | }
32 |
33 | @Autowired
34 | public void config(EventProcessingConfiguration epConfig,
35 | @Qualifier("trade-events") SubscribableMessageSource> tradeEvents) {
36 | epConfig.registerSubscribingEventProcessor("trading", c -> tradeEvents);
37 | }
38 |
39 | @Qualifier("trade-events")
40 | @Bean
41 | public SubscribableMessageSource> tradeEvents(AMQPMessageConverter messageConverter) {
42 | return new SpringAMQPMessageSource(messageConverter) {
43 | @Transactional
44 | @RabbitListener(queues = "trades")
45 | @Override
46 | public void onMessage(Message message, Channel channel) {
47 | super.onMessage(message, channel);
48 | }
49 | };
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/AppConfig.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.app;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.fasterxml.jackson.databind.SerializationFeature;
5 | import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator;
6 | import com.fasterxml.jackson.module.kotlin.KotlinModule;
7 | import org.axonframework.commandhandling.CommandBus;
8 | import org.axonframework.commandhandling.distributed.DistributedCommandBus;
9 | import org.axonframework.messaging.interceptors.LoggingInterceptor;
10 | import org.axonframework.serialization.Serializer;
11 | import org.axonframework.serialization.json.JacksonSerializer;
12 | import org.springframework.beans.factory.annotation.Autowired;
13 | import org.springframework.beans.factory.annotation.Qualifier;
14 | import org.springframework.context.annotation.Bean;
15 | import org.springframework.context.annotation.Configuration;
16 |
17 | @Configuration
18 | public class AppConfig {
19 |
20 | /**
21 | * We have chosen to take this optional step in order to benefit from automatic logging of every command
22 | * message being published on to the {@link CommandBus}. These are `INFO` level statements of every action.
23 | * We have qualified the given CommandBus parameter with 'localSegment' as we are using a
24 | * {@link DistributedCommandBus} and we want to associate this {@link LoggingInterceptor} with the local CommandBus
25 | * node.
26 | *
27 | * @param commandBus the {@link CommandBus} auto configured for this application
28 | */
29 | @Autowired
30 | public void configure(@Qualifier("localSegment") CommandBus commandBus) {
31 | commandBus.registerHandlerInterceptor(new LoggingInterceptor<>());
32 | }
33 |
34 | @Autowired
35 | public void configure(ObjectMapper objectMapper) {
36 | objectMapper.registerModule(new KotlinModule());
37 | objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
38 | }
39 |
40 | @Bean
41 | @Qualifier("eventSerializer")
42 | public Serializer eventSerializer(ObjectMapper objectMapper) {
43 | return new JacksonSerializer(objectMapper);
44 | }
45 |
46 | @Bean
47 | public JsonSchemaGenerator jsonSchemaGenerator(ObjectMapper objectMapper) {
48 | return new JsonSchemaGenerator(objectMapper);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.app;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
5 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
6 |
7 | @Configuration
8 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
9 |
10 | @Override
11 | protected void configure(HttpSecurity http) throws Exception {
12 | http.cors()
13 | .and()
14 | .csrf().disable()
15 | .authorizeRequests().anyRequest().permitAll();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/TraderApplication.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.app;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
8 |
9 | @EnableWebSecurity
10 | @SpringBootApplication
11 | public class TraderApplication {
12 |
13 | private static final Logger LOG = LoggerFactory.getLogger(TraderApplication.class);
14 |
15 | public static void main(String[] args) {
16 | SpringApplication.run(TraderApplication.class, args);
17 | LOG.info("Started the Axon CQRS and Event Sourcing Trader-app.");
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/command/company/CompanyOrderBookListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.command.company;
18 |
19 | import io.pivotal.refarch.cqrs.trader.coreapi.company.AddOrderBookToCompanyCommand;
20 | import io.pivotal.refarch.cqrs.trader.coreapi.company.CompanyCreatedEvent;
21 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId;
22 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.trades.CreateOrderBookCommand;
23 | import org.axonframework.commandhandling.gateway.CommandGateway;
24 | import org.axonframework.config.ProcessingGroup;
25 | import org.axonframework.eventhandling.EventHandler;
26 | import org.slf4j.Logger;
27 | import org.slf4j.LoggerFactory;
28 | import org.springframework.stereotype.Service;
29 |
30 | @Service
31 | @ProcessingGroup("commandPublishingEventHandlers")
32 | public class CompanyOrderBookListener {
33 |
34 | private static final Logger logger = LoggerFactory.getLogger(CompanyOrderBookListener.class);
35 |
36 | private final CommandGateway commandGateway;
37 |
38 | public CompanyOrderBookListener(CommandGateway commandGateway) {
39 | this.commandGateway = commandGateway;
40 | }
41 |
42 | @EventHandler
43 | public void on(CompanyCreatedEvent event) {
44 | logger.debug("About to dispatch a new command to create an OrderBook for the company {}", event.getCompanyId());
45 |
46 | OrderBookId orderBookId = new OrderBookId();
47 | commandGateway.send(new CreateOrderBookCommand(orderBookId));
48 | commandGateway.send(new AddOrderBookToCompanyCommand(event.getCompanyId(), orderBookId));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/command/user/PortfolioManagementUserListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.command.user;
18 |
19 | import io.pivotal.refarch.cqrs.trader.app.query.users.UserCreatedEvent;
20 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.CreatePortfolioCommand;
21 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId;
22 | import org.axonframework.commandhandling.gateway.CommandGateway;
23 | import org.axonframework.config.ProcessingGroup;
24 | import org.axonframework.eventhandling.EventHandler;
25 | import org.slf4j.Logger;
26 | import org.slf4j.LoggerFactory;
27 | import org.springframework.stereotype.Service;
28 |
29 | @Service
30 | @ProcessingGroup("commandPublishingEventHandlers")
31 | public class PortfolioManagementUserListener {
32 |
33 | private static final Logger logger = LoggerFactory.getLogger(PortfolioManagementUserListener.class);
34 |
35 | private final CommandGateway commandGateway;
36 |
37 | public PortfolioManagementUserListener(CommandGateway commandGateway) {
38 | this.commandGateway = commandGateway;
39 | }
40 |
41 | @EventHandler
42 | public void on(UserCreatedEvent event) {
43 | logger.debug("About to dispatch a new command to create a Portfolio for the new user {}", event.getUserId());
44 | commandGateway.send(new CreatePortfolioCommand(new PortfolioId(), event.getUserId()));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/query/company/CompanyView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.query.company;
18 |
19 | import org.springframework.data.annotation.Id;
20 |
21 | import javax.persistence.Entity;
22 |
23 | @Entity
24 | public class CompanyView {
25 |
26 | @Id
27 | @javax.persistence.Id
28 | private String identifier;
29 | private String name;
30 | private long value;
31 | private long amountOfShares;
32 | private boolean tradeStarted;
33 |
34 | public long getAmountOfShares() {
35 | return amountOfShares;
36 | }
37 |
38 | public void setAmountOfShares(long amountOfShares) {
39 | this.amountOfShares = amountOfShares;
40 | }
41 |
42 | public String getIdentifier() {
43 | return identifier;
44 | }
45 |
46 | public void setIdentifier(String identifier) {
47 | this.identifier = identifier;
48 | }
49 |
50 | public String getName() {
51 | return name;
52 | }
53 |
54 | public void setName(String name) {
55 | this.name = name;
56 | }
57 |
58 | public boolean isTradeStarted() {
59 | return tradeStarted;
60 | }
61 |
62 | public void setTradeStarted(boolean tradeStarted) {
63 | this.tradeStarted = tradeStarted;
64 | }
65 |
66 | public long getValue() {
67 | return value;
68 | }
69 |
70 | public void setValue(long value) {
71 | this.value = value;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/query/company/CompanyViewRepository.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.query.company;
18 |
19 | import org.springframework.data.jpa.repository.JpaRepository;
20 |
21 | public interface CompanyViewRepository extends JpaRepository {
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/query/orderbook/OrderBookViewRepository.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.query.orderbook;
18 |
19 | import io.pivotal.refarch.cqrs.trader.app.query.orders.trades.OrderBookView;
20 | import org.springframework.data.jpa.repository.JpaRepository;
21 |
22 | import java.util.List;
23 |
24 |
25 | public interface OrderBookViewRepository extends JpaRepository {
26 |
27 | List findByCompanyIdentifier(String companyIdentifier);
28 | }
29 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/query/orders/transaction/TradeExecutedQueryRepository.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.query.orders.transaction;
18 |
19 | import org.springframework.data.jpa.repository.JpaRepository;
20 |
21 | import java.util.List;
22 |
23 | public interface TradeExecutedQueryRepository extends JpaRepository {
24 |
25 | List findByOrderBookId(String orderBookId);
26 | }
27 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/query/orders/transaction/TradeExecutedView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.query.orders.transaction;
18 |
19 | import javax.persistence.Entity;
20 | import javax.persistence.GeneratedValue;
21 |
22 | @Entity
23 | public class TradeExecutedView {
24 |
25 | @javax.persistence.Id
26 | @GeneratedValue
27 | private Long generatedId;
28 |
29 | private long tradeCount;
30 | private long tradePrice;
31 | private String companyName;
32 | private String orderBookId;
33 |
34 | public long getTradeCount() {
35 | return tradeCount;
36 | }
37 |
38 | public void setTradeCount(long tradeCount) {
39 | this.tradeCount = tradeCount;
40 | }
41 |
42 | public String getCompanyName() {
43 | return companyName;
44 | }
45 |
46 | public void setCompanyName(String companyName) {
47 | this.companyName = companyName;
48 | }
49 |
50 | public long getTradePrice() {
51 | return tradePrice;
52 | }
53 |
54 | public void setTradePrice(long tradePrice) {
55 | this.tradePrice = tradePrice;
56 | }
57 |
58 | public String getOrderBookId() {
59 | return orderBookId;
60 | }
61 |
62 | public void setOrderBookId(String orderBookId) {
63 | this.orderBookId = orderBookId;
64 | }
65 |
66 | public Long getGeneratedId() {
67 | return generatedId;
68 | }
69 |
70 | public void setGeneratedId(Long generatedId) {
71 | this.generatedId = generatedId;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/query/portfolio/ItemEntry.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.query.portfolio;
18 |
19 | import javax.persistence.Entity;
20 | import javax.persistence.GeneratedValue;
21 |
22 | @Entity
23 | public class ItemEntry {
24 |
25 | @javax.persistence.Id
26 | @GeneratedValue
27 | private Long generatedId;
28 |
29 | private String identifier; // OrderBook identifier
30 | private String companyId;
31 | private String companyName;
32 | private long amount;
33 |
34 | public Long getGeneratedId() {
35 | return generatedId;
36 | }
37 |
38 | public void setGeneratedId(Long generatedId) {
39 | this.generatedId = generatedId;
40 | }
41 |
42 | public String getIdentifier() {
43 | return identifier;
44 | }
45 |
46 | public void setIdentifier(String identifier) {
47 | this.identifier = identifier;
48 | }
49 |
50 | public String getCompanyId() {
51 | return companyId;
52 | }
53 |
54 | public void setCompanyId(String companyId) {
55 | this.companyId = companyId;
56 | }
57 |
58 | public String getCompanyName() {
59 | return companyName;
60 | }
61 |
62 | public void setCompanyName(String companyName) {
63 | this.companyName = companyName;
64 | }
65 |
66 | public long getAmount() {
67 | return amount;
68 | }
69 |
70 | public void setAmount(long amount) {
71 | this.amount = amount;
72 | }
73 |
74 | @Override
75 | public String toString() {
76 | return "ItemEntry{" +
77 | "amount=" + amount +
78 | ", identifier='" + identifier + '\'' +
79 | ", companyId='" + companyId + '\'' +
80 | ", companyName='" + companyName + '\'' +
81 | '}';
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/query/portfolio/PortfolioViewRepository.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.query.portfolio;
18 |
19 | import org.springframework.data.jpa.repository.JpaRepository;
20 |
21 | public interface PortfolioViewRepository extends JpaRepository {
22 |
23 | PortfolioView findByUserId(String userId);
24 | }
25 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/query/transaction/TransactionViewRepository.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.query.transaction;
18 |
19 | import io.pivotal.refarch.cqrs.trader.app.query.orders.transaction.TransactionView;
20 | import org.springframework.data.jpa.repository.JpaRepository;
21 |
22 | import java.util.List;
23 |
24 | public interface TransactionViewRepository extends JpaRepository {
25 |
26 | List findByPortfolioId(String portfolioId);
27 | }
28 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/query/users/UserView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.query.users;
18 |
19 | import org.springframework.data.annotation.Id;
20 |
21 | import javax.persistence.Entity;
22 | import java.io.Serializable;
23 |
24 | @Entity
25 | public class UserView implements Serializable {
26 |
27 | @Id
28 | @javax.persistence.Id
29 | private String identifier;
30 | private String name;
31 | private String username;
32 |
33 | public String getIdentifier() {
34 | return identifier;
35 | }
36 |
37 | public void setIdentifier(String identifier) {
38 | this.identifier = identifier;
39 | }
40 |
41 | public String getName() {
42 | return name;
43 | }
44 |
45 | public void setName(String name) {
46 | this.name = name;
47 | }
48 |
49 | public String getUsername() {
50 | return username;
51 | }
52 |
53 | public void setUsername(String username) {
54 | this.username = username;
55 | }
56 |
57 | public String getUserId() {
58 | return this.identifier;
59 | }
60 |
61 | public String getUserName() {
62 | return this.username;
63 | }
64 |
65 | public String getFullName() {
66 | return this.name;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/app/query/users/UserViewRepository.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.query.users;
18 |
19 | import org.springframework.data.jpa.repository.JpaRepository;
20 |
21 | public interface UserViewRepository extends JpaRepository {
22 |
23 | UserView findByIdentifier(String identifier);
24 | }
25 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/company/CompanyId.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.company;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import org.springframework.util.Assert;
6 |
7 | import java.io.Serializable;
8 | import java.util.Objects;
9 | import java.util.UUID;
10 |
11 | public class CompanyId implements Serializable {
12 |
13 | private static final long serialVersionUID = 1284500781431396262L;
14 |
15 | private final String identifier;
16 |
17 | public CompanyId() {
18 | this(UUID.randomUUID().toString());
19 | }
20 |
21 | @JsonCreator
22 | public CompanyId(String identifier) {
23 | Assert.notNull(identifier, "Identifier parameter may not be null");
24 | this.identifier = identifier;
25 | }
26 |
27 | @JsonValue
28 | public String getIdentifier() {
29 | return identifier;
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(identifier);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object obj) {
39 | if (this == obj) {
40 | return true;
41 | }
42 | if (obj == null || getClass() != obj.getClass()) {
43 | return false;
44 | }
45 | final CompanyId other = (CompanyId) obj;
46 | return Objects.equals(this.identifier, other.identifier);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return getIdentifier();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/company/commands.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.company
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
5 | import org.axonframework.commandhandling.TargetAggregateIdentifier
6 |
7 | abstract class CompanyCommand(@TargetAggregateIdentifier open val companyId: CompanyId)
8 |
9 | data class CreateCompanyCommand(
10 | @JsonProperty("companyId") override val companyId: CompanyId = CompanyId(),
11 | @JsonProperty("companyName") val companyName: String,
12 | @JsonProperty("companyValue") val companyValue: Long,
13 | @JsonProperty("amountOfShares") val amountOfShares: Long
14 | ) : CompanyCommand(companyId)
15 |
16 | data class AddOrderBookToCompanyCommand(
17 | @JsonProperty("companyId") override val companyId: CompanyId,
18 | @JsonProperty("orderBookId") val orderBookId: OrderBookId
19 | ) : CompanyCommand(companyId)
20 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/company/events.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.company
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
4 |
5 | data class CompanyCreatedEvent(
6 | val companyId: CompanyId,
7 | val companyName: String,
8 | val companyValue: Long,
9 | val amountOfShares: Long
10 | )
11 |
12 | data class OrderBookAddedToCompanyEvent(
13 | val companyId: CompanyId,
14 | val orderBookId: OrderBookId
15 | )
16 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/company/queries.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.company
2 |
3 | data class CompanyByIdQuery(val companyId: CompanyId)
4 | data class FindAllCompaniesQuery(val pageOffset: Int, val pageSize: Int)
5 |
6 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/OrderBookId.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import org.springframework.util.Assert;
6 |
7 | import java.io.Serializable;
8 | import java.util.Objects;
9 | import java.util.UUID;
10 |
11 | public class OrderBookId implements Serializable {
12 |
13 | private static final long serialVersionUID = -3483676125514883162L;
14 |
15 | private final String identifier;
16 |
17 | public OrderBookId() {
18 | this(UUID.randomUUID().toString());
19 | }
20 |
21 | @JsonCreator
22 | public OrderBookId(String identifier) {
23 | Assert.notNull(identifier, "Identifier parameter may not be null");
24 | this.identifier = identifier;
25 | }
26 |
27 | @JsonValue
28 | public String getIdentifier() {
29 | return identifier;
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(identifier);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object obj) {
39 | if (this == obj) {
40 | return true;
41 | }
42 | if (obj == null || getClass() != obj.getClass()) {
43 | return false;
44 | }
45 | final OrderBookId other = (OrderBookId) obj;
46 | return Objects.equals(this.identifier, other.identifier);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return getIdentifier();
52 | }
53 | }
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/trades/OrderId.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders.trades;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import org.springframework.util.Assert;
6 |
7 | import java.io.Serializable;
8 | import java.util.Objects;
9 | import java.util.UUID;
10 |
11 | public class OrderId implements Serializable {
12 |
13 | private static final long serialVersionUID = 4034328048230397374L;
14 |
15 | private final String identifier;
16 |
17 | public OrderId() {
18 | this(UUID.randomUUID().toString());
19 | }
20 |
21 | @JsonCreator
22 | public OrderId(String identifier) {
23 | Assert.notNull(identifier, "Identifier parameter may not be null");
24 | this.identifier = identifier;
25 | }
26 |
27 | @JsonValue
28 | public String getIdentifier() {
29 | return identifier;
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(identifier);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object obj) {
39 | if (this == obj) {
40 | return true;
41 | }
42 | if (obj == null || getClass() != obj.getClass()) {
43 | return false;
44 | }
45 | final OrderId other = (OrderId) obj;
46 | return Objects.equals(this.identifier, other.identifier);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return getIdentifier();
52 | }
53 | }
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/trades/commands.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders.trades
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
5 | import org.axonframework.commandhandling.TargetAggregateIdentifier
6 |
7 | data class CreateOrderBookCommand(
8 | @TargetAggregateIdentifier
9 | @JsonProperty("orderBookId") val orderBookId: OrderBookId = OrderBookId()
10 | )
11 |
12 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/trades/events.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders.trades
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction.TransactionId
5 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
6 | import java.io.Serializable
7 |
8 | abstract class AbstractOrderPlacedEvent(
9 | open val orderBookId: OrderBookId,
10 | open val orderId: OrderId,
11 | open val transactionId: TransactionId,
12 | open val tradeCount: Long,
13 | open val itemPrice: Long,
14 | open val portfolioId: PortfolioId
15 | )
16 |
17 | data class BuyOrderPlacedEvent(
18 | override val orderBookId: OrderBookId,
19 | override val orderId: OrderId,
20 | override val transactionId: TransactionId,
21 | override val tradeCount: Long,
22 | override val itemPrice: Long,
23 | override val portfolioId: PortfolioId
24 | ) : AbstractOrderPlacedEvent(orderBookId, orderId, transactionId, tradeCount, itemPrice, portfolioId)
25 |
26 | data class SellOrderPlacedEvent(
27 | override val orderBookId: OrderBookId,
28 | override val orderId: OrderId,
29 | override val transactionId: TransactionId,
30 | override val tradeCount: Long,
31 | override val itemPrice: Long,
32 | override val portfolioId: PortfolioId
33 | ) : AbstractOrderPlacedEvent(orderBookId, orderId, transactionId, tradeCount, itemPrice, portfolioId)
34 |
35 | abstract class OrderBookEvent(open val orderBookId: OrderBookId)
36 |
37 | data class OrderBookCreatedEvent(override val orderBookId: OrderBookId) : OrderBookEvent(orderBookId)
38 |
39 | data class TradeExecutedEvent(
40 | val orderBookId: OrderBookId,
41 | val tradeCount: Long,
42 | val tradePrice: Long,
43 | val buyOrderId: OrderId,
44 | val sellOrderId: OrderId,
45 | val buyTransactionId: TransactionId,
46 | val sellTransactionId: TransactionId
47 | ) : Serializable {
48 | companion object {
49 | private const val serialVersionUID = 6292249351659536792L
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/trades/queries.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders.trades
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.company.CompanyId
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
5 |
6 | data class OrderBookByIdQuery(val orderBookId: OrderBookId)
7 | data class OrderBooksByCompanyIdQuery(val companyId : CompanyId)
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/transaction/TransactionId.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import org.springframework.util.Assert;
6 |
7 | import java.io.Serializable;
8 | import java.util.Objects;
9 | import java.util.UUID;
10 |
11 | public class TransactionId implements Serializable {
12 |
13 | private static final long serialVersionUID = -5267104328616955617L;
14 |
15 | private final String identifier;
16 |
17 | public TransactionId() {
18 | this(UUID.randomUUID().toString());
19 | }
20 |
21 | @JsonCreator
22 | public TransactionId(String identifier) {
23 | Assert.notNull(identifier, "Identifier parameter may not be null");
24 | this.identifier = identifier;
25 | }
26 |
27 | @JsonValue
28 | public String getIdentifier() {
29 | return identifier;
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(identifier);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object obj) {
39 | if (this == obj) {
40 | return true;
41 | }
42 | if (obj == null || getClass() != obj.getClass()) {
43 | return false;
44 | }
45 | final TransactionId other = (TransactionId) obj;
46 | return Objects.equals(this.identifier, other.identifier);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return getIdentifier();
52 | }
53 | }
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/transaction/queries.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
4 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
5 |
6 | data class TransactionByIdQuery(val transactionId: TransactionId)
7 | data class TransactionsByPortfolioIdQuery(val portfolioId: PortfolioId)
8 |
9 | data class ExecutedTradesByOrderBookIdQuery(val orderBookId: OrderBookId)
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/transaction/valueObjects.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction
2 |
3 | enum class TransactionState {
4 | STARTED, CONFIRMED, CANCELLED, EXECUTED, PARTIALLY_EXECUTED
5 | }
6 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/valueObjects.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders
2 |
3 | enum class TransactionType {
4 | SELL, BUY
5 | }
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/PortfolioId.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import org.springframework.util.Assert;
6 |
7 | import java.io.Serializable;
8 | import java.util.Objects;
9 | import java.util.UUID;
10 |
11 | public class PortfolioId implements Serializable {
12 |
13 | private static final long serialVersionUID = 5649005745169990400L;
14 |
15 | private final String identifier;
16 |
17 | public PortfolioId() {
18 | this(UUID.randomUUID().toString());
19 | }
20 |
21 | @JsonCreator
22 | public PortfolioId(String identifier) {
23 | Assert.notNull(identifier, "Identifier parameter may not be null");
24 | this.identifier = identifier;
25 | }
26 |
27 | @JsonValue
28 | public String getIdentifier() {
29 | return identifier;
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(identifier);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object obj) {
39 | if (this == obj) {
40 | return true;
41 | }
42 | if (obj == null || getClass() != obj.getClass()) {
43 | return false;
44 | }
45 | final PortfolioId other = (PortfolioId) obj;
46 | return Objects.equals(this.identifier, other.identifier);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return getIdentifier();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/cash/commands.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio.cash
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction.TransactionId
5 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioCommand
6 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
7 | import javax.validation.constraints.Min
8 |
9 | data class CancelCashReservationCommand(
10 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
11 | @JsonProperty("transactionId") val transactionId: TransactionId,
12 | @JsonProperty("amountOfMoneyToCancel") val amountOfMoneyToCancel: Long
13 | ) : PortfolioCommand(portfolioId)
14 |
15 | data class ConfirmCashReservationCommand(
16 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
17 | @JsonProperty("transactionId") val transactionId: TransactionId,
18 | @JsonProperty("amountOfMoneyToConfirmInCents") val amountOfMoneyToConfirmInCents: Long
19 | ) : PortfolioCommand(portfolioId)
20 |
21 | data class DepositCashCommand(
22 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
23 | @JsonProperty("moneyToAddInCents") @Min(0) val moneyToAddInCents: Long
24 | ) : PortfolioCommand(portfolioId)
25 |
26 | data class ReserveCashCommand(
27 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
28 | @JsonProperty("transactionId") val transactionId: TransactionId,
29 | @JsonProperty("amountOfMoneyToReserve") @Min(0) val amountOfMoneyToReserve: Long
30 | ) : PortfolioCommand(portfolioId)
31 |
32 | data class WithdrawCashCommand(
33 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
34 | @JsonProperty("amountToPayInCents") @Min(0) val amountToPayInCents: Long
35 | ) : PortfolioCommand(portfolioId)
36 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/cash/events.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio.cash
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction.TransactionId
4 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioEvent
5 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
6 |
7 | data class CashDepositedEvent(
8 | override val portfolioId: PortfolioId,
9 | val moneyAddedInCents: Long
10 | ) : PortfolioEvent(portfolioId)
11 |
12 | data class CashReservationCancelledEvent(
13 | override val portfolioId: PortfolioId,
14 | val transactionId: TransactionId,
15 | val amountOfMoneyToCancel: Long
16 | ) : PortfolioEvent(portfolioId)
17 |
18 | data class CashReservationConfirmedEvent(
19 | override val portfolioId: PortfolioId,
20 | val transactionId: TransactionId,
21 | val amountOfMoneyConfirmedInCents: Long
22 | ) : PortfolioEvent(portfolioId)
23 |
24 | data class CashReservationRejectedEvent(
25 | override val portfolioId: PortfolioId,
26 | val transactionId: TransactionId,
27 | val amountToPayInCents: Long
28 | ) : PortfolioEvent(portfolioId)
29 |
30 | data class CashReservedEvent(
31 | override val portfolioId: PortfolioId,
32 | val transactionId: TransactionId,
33 | val amountToReserve: Long
34 | ) : PortfolioEvent(portfolioId)
35 |
36 | data class CashWithdrawnEvent(
37 | override val portfolioId: PortfolioId,
38 | val amountPaidInCents: Long
39 | ) : PortfolioEvent(portfolioId)
40 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/commands.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 | import io.pivotal.refarch.cqrs.trader.coreapi.users.UserId
5 | import org.axonframework.commandhandling.TargetAggregateIdentifier
6 |
7 | abstract class PortfolioCommand(@TargetAggregateIdentifier open val portfolioId: PortfolioId)
8 |
9 | data class CreatePortfolioCommand(
10 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId = PortfolioId(),
11 | @JsonProperty("userId") val userId: UserId
12 | ) : PortfolioCommand(portfolioId)
13 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/events.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.users.UserId
4 |
5 | abstract class PortfolioEvent(open val portfolioId: PortfolioId)
6 |
7 | class PortfolioCreatedEvent(
8 | override val portfolioId: PortfolioId,
9 | val userId: UserId
10 | ) : PortfolioEvent(portfolioId)
11 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/queries.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.users.UserId
4 |
5 | data class PortfolioByIdQuery(val portfolioId: PortfolioId)
6 | data class PortfolioByUserIdQuery(val userId: UserId)
7 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/stock/commands.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio.stock
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
5 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction.TransactionId
6 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioCommand
7 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
8 | import javax.validation.constraints.Min
9 |
10 | data class AddItemsToPortfolioCommand(
11 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
12 | @JsonProperty("orderBookId") val orderBookId: OrderBookId,
13 | @JsonProperty("amountOfItemsToAdd") @Min(0) val amountOfItemsToAdd: Long
14 | ) : PortfolioCommand(portfolioId)
15 |
16 | data class CancelItemReservationForPortfolioCommand(
17 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
18 | @JsonProperty("orderBookId") val orderBookId: OrderBookId,
19 | @JsonProperty("transactionId") val transactionId: TransactionId,
20 | @JsonProperty("amountOfItemsToCancel") val amountOfItemsToCancel: Long
21 | ) : PortfolioCommand(portfolioId)
22 |
23 | data class ConfirmItemReservationForPortfolioCommand(
24 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
25 | @JsonProperty("orderBookId") val orderBookId: OrderBookId,
26 | @JsonProperty("transactionId") val transactionId: TransactionId,
27 | @JsonProperty("amountOfItemsToConfirm") val amountOfItemsToConfirm: Long
28 | ) : PortfolioCommand(portfolioId)
29 |
30 | data class ReserveItemsCommand(
31 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
32 | @JsonProperty("orderBookId") val orderBookId: OrderBookId,
33 | @JsonProperty("transactionId") val transactionId: TransactionId,
34 | @JsonProperty("amountOfItemsToReserve") val amountOfItemsToReserve: Long
35 | ) : PortfolioCommand(portfolioId)
36 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/stock/events.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio.stock
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction.TransactionId
5 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioEvent
6 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
7 |
8 | data class ItemReservationCancelledForPortfolioEvent(
9 | override val portfolioId: PortfolioId,
10 | val orderBookId: OrderBookId,
11 | val transactionId: TransactionId,
12 | val amountOfCancelledItems: Long
13 | ) : PortfolioEvent(portfolioId)
14 |
15 | data class ItemReservationConfirmedForPortfolioEvent(
16 | override val portfolioId: PortfolioId,
17 | val orderBookId: OrderBookId,
18 | val transactionId: TransactionId,
19 | val amountOfConfirmedItems: Long
20 | ) : PortfolioEvent(portfolioId)
21 |
22 | data class ItemsAddedToPortfolioEvent(
23 | override val portfolioId: PortfolioId,
24 | val orderBookId: OrderBookId,
25 | val amountOfItemsAdded: Long
26 | ) : PortfolioEvent(portfolioId)
27 |
28 | data class ItemsReservedEvent(
29 | override val portfolioId: PortfolioId,
30 | val orderBookId: OrderBookId,
31 | val transactionId: TransactionId,
32 | val amountOfItemsReserved: Long
33 | ) : PortfolioEvent(portfolioId)
34 |
35 | data class ItemToReserveNotAvailableInPortfolioEvent(
36 | override val portfolioId: PortfolioId,
37 | val orderBookId: OrderBookId,
38 | val transactionId: TransactionId
39 | ) : PortfolioEvent(portfolioId)
40 |
41 | data class NotEnoughItemsAvailableToReserveInPortfolioEvent(
42 | override val portfolioId: PortfolioId,
43 | val orderBookId: OrderBookId,
44 | val transactionId: TransactionId,
45 | val availableAmountOfItems: Long,
46 | val amountOfItemsToReserve: Long
47 | ) : PortfolioEvent(portfolioId)
48 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/users/UserId.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.users;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import org.springframework.util.Assert;
6 |
7 | import java.io.Serializable;
8 | import java.util.Objects;
9 | import java.util.UUID;
10 |
11 | public class UserId implements Serializable {
12 |
13 | private static final long serialVersionUID = -4860092244272266543L;
14 |
15 | private final String identifier;
16 |
17 | public UserId() {
18 | this(UUID.randomUUID().toString());
19 | }
20 |
21 | @JsonCreator
22 | public UserId(String identifier) {
23 | Assert.notNull(identifier, "Identifier parameter may not be null");
24 | this.identifier = identifier;
25 | }
26 |
27 | @JsonValue
28 | public String getIdentifier() {
29 | return identifier;
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(identifier);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object obj) {
39 | if (this == obj) {
40 | return true;
41 | }
42 | if (obj == null || getClass() != obj.getClass()) {
43 | return false;
44 | }
45 | final UserId other = (UserId) obj;
46 | return Objects.equals(this.identifier, other.identifier);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return getIdentifier();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/users/commands.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.users
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 | import org.axonframework.commandhandling.TargetAggregateIdentifier
5 | import java.util.*
6 | import javax.validation.constraints.NotNull
7 | import javax.validation.constraints.Size
8 |
9 | abstract class UserCommand(@TargetAggregateIdentifier open val userId: UserId)
10 |
11 | class CreateUserCommand(
12 | @JsonProperty("userId") override val userId: UserId = UserId(),
13 | @JsonProperty("name") val name: String, @NotNull @Size(min = 3)
14 | @JsonProperty("username") val username: String, @NotNull @Size(min = 3)
15 | @JsonProperty("password") val password: String
16 | ) : UserCommand(userId)
17 |
18 | data class AuthenticateUserCommand(
19 | @JsonProperty("userId") override val userId: UserId,
20 | @JsonProperty("userName") val userName: String,
21 | @JsonProperty("password") @NotNull @Size(min = 3) val password: CharArray
22 | ) : UserCommand(userId) {
23 |
24 | override fun equals(other: Any?): Boolean {
25 | if (this === other) return true
26 | if (other !is AuthenticateUserCommand) return false
27 |
28 | if (userId != other.userId) return false
29 | if (userName != other.userName) return false
30 | if (!Arrays.equals(password, other.password)) return false
31 |
32 | return true
33 | }
34 |
35 | override fun hashCode(): Int {
36 | var result = userId.hashCode()
37 | result = 31 * result + userName.hashCode()
38 | result = 31 * result + Arrays.hashCode(password)
39 | return result
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/users/events.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.app.query.users
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.users.UserId
4 |
5 | abstract class UserEvent(open val userId: UserId)
6 |
7 | data class UserCreatedEvent(
8 | override val userId: UserId,
9 | val name: String,
10 | val username: String,
11 | val password: String
12 | ) : UserEvent(userId)
13 |
14 | data class UserAuthenticatedEvent(override val userId: UserId) : UserEvent(userId)
15 |
--------------------------------------------------------------------------------
/trader-app/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/users/queries.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.users
2 |
3 | data class UserByIdQuery(val userId: UserId)
4 | data class FindAllUsersQuery(val pageOffset: Int, val pageSize: Int)
5 |
--------------------------------------------------------------------------------
/trader-app/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | #spring.h2.console.enabled=true
2 | #spring.main.banner-mode=off
3 |
4 | # Set Axon to use HA when possible
5 | axon.distributed.enabled=true
6 |
7 | # We use Eureka, so the Fallback approach shouldn't be necessary
8 | axon.distributed.spring-cloud.fallback-to-http-get=false
9 |
10 | # Axon AMQP messaging
11 | axon.amqp.exchange=trading-engine-events
12 | axon.amqp.transaction-mode=publisher_ack
13 |
14 | # Actuator
15 | management.endpoints.enabled-by-default=true
16 | management.endpoints.web.exposure.include=*
17 | management.cloudfoundry.enabled=true
18 |
19 | # Setup so that applications in the registry have the info they need to communicate directly
20 | spring.cloud.services.registrationMethod=direct
21 |
22 | # Set the general logging level within the application
23 | logging.level.io.pivotal.refarch=DEBUG
24 |
25 | # Open Session In View in combination with Server Sent Events is connection trouble waiting to happen
26 | spring.jpa.open-in-view=false
27 |
28 |
--------------------------------------------------------------------------------
/trader-app/src/main/resources/bootstrap.properties:
--------------------------------------------------------------------------------
1 | spring.application.name=trader-app
2 | #spring.main.banner-mode=off
3 | #spring.security.user.roles=ADMIN
4 |
5 | spring.jpa.generate-ddl=true
6 | spring.jpa.hibernate.ddl-auto=create
7 | spring.jpa.show-sql=true
8 |
9 | #spring.jpa.database=mysql
10 | #spring.jpa.database=h2
11 | #spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
12 | #spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
13 |
--------------------------------------------------------------------------------
/trader-app/src/test/java/io/pivotal/refarch/cqrs/trader/app/command/company/CompanyOrderBookListenerTest.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.app.command.company;
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.company.AddOrderBookToCompanyCommand;
4 | import io.pivotal.refarch.cqrs.trader.coreapi.company.CompanyCreatedEvent;
5 | import io.pivotal.refarch.cqrs.trader.coreapi.company.CompanyId;
6 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.trades.CreateOrderBookCommand;
7 | import org.axonframework.commandhandling.gateway.CommandGateway;
8 | import org.junit.*;
9 | import org.mockito.*;
10 |
11 | import java.util.List;
12 |
13 | import static org.junit.Assert.*;
14 | import static org.mockito.Mockito.*;
15 |
16 | public class CompanyOrderBookListenerTest {
17 |
18 | private final CommandGateway commandGateway = mock(CommandGateway.class);
19 |
20 | private CompanyOrderBookListener testSubject = new CompanyOrderBookListener(commandGateway);
21 |
22 | @Test
23 | public void testOnCompanyCreatedEventACreatesAndAssociatesOrderBookCommandIsPublished() {
24 | CompanyId testCompanyId = new CompanyId();
25 |
26 | testSubject.on(new CompanyCreatedEvent(testCompanyId, "AxonIQ", 1337L, 50L));
27 |
28 | ArgumentCaptor commandCaptor = ArgumentCaptor.forClass(Object.class);
29 |
30 | verify(commandGateway, times(2)).send(commandCaptor.capture());
31 |
32 | List results = commandCaptor.getAllValues();
33 | CreateOrderBookCommand firstCommand = (CreateOrderBookCommand) results.get(0);
34 | assertNotNull(firstCommand);
35 | assertNotNull(firstCommand.getOrderBookId());
36 | AddOrderBookToCompanyCommand secondCommand = (AddOrderBookToCompanyCommand) results.get(1);
37 | assertNotNull(secondCommand);
38 | assertEquals(testCompanyId, secondCommand.getCompanyId());
39 | assertNotNull(secondCommand.getOrderBookId());
40 | }
41 | }
--------------------------------------------------------------------------------
/trader-app/src/test/java/io/pivotal/refarch/cqrs/trader/app/command/user/PortfolioManagementUserListenerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010-2012. Axon Framework
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.pivotal.refarch.cqrs.trader.app.command.user;
18 |
19 | import io.pivotal.refarch.cqrs.trader.app.query.users.UserCreatedEvent;
20 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.CreatePortfolioCommand;
21 | import io.pivotal.refarch.cqrs.trader.coreapi.users.UserId;
22 | import org.axonframework.commandhandling.gateway.CommandGateway;
23 | import org.junit.*;
24 | import org.mockito.*;
25 |
26 | import static org.junit.Assert.*;
27 | import static org.mockito.Mockito.*;
28 |
29 | public class PortfolioManagementUserListenerTest {
30 |
31 | private final CommandGateway commandGateway = mock(CommandGateway.class);
32 |
33 | private final PortfolioManagementUserListener testSubject = new PortfolioManagementUserListener(commandGateway);
34 |
35 | @Test
36 | public void testOnUserCreatedEventACreatePortfolioCommandIsPublished() {
37 | UserId testUserId = new UserId();
38 |
39 | testSubject.on(new UserCreatedEvent(testUserId, "Test", "testuser", "testpassword"));
40 |
41 | ArgumentCaptor commandCaptor = ArgumentCaptor.forClass(CreatePortfolioCommand.class);
42 |
43 | verify(commandGateway).send(commandCaptor.capture());
44 |
45 | CreatePortfolioCommand result = commandCaptor.getValue();
46 | assertNotNull(result);
47 | assertNotNull(result.getPortfolioId());
48 | assertEquals(testUserId, result.getUserId());
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postAddItemsToPortfolioCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/AddItemsToPortfolioCommand"
10 | body(
11 | "portfolioId": anyUuid(),
12 | "orderBookId": anyUuid(),
13 | "amountOfItemsToAdd": anyPositiveInt()
14 | )
15 | headers {
16 | contentType applicationJson()
17 | }
18 | }
19 | response {
20 | status HttpStatus.OK.value()
21 | async()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postAddOrderBookToCompanyCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/AddOrderBookToCompanyCommand"
10 | body(
11 | "companyId": anyUuid(),
12 | "orderBookId": anyUuid()
13 | )
14 | headers {
15 | contentType applicationJson()
16 | }
17 | }
18 | response {
19 | status HttpStatus.OK.value()
20 | async()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postAuthenticateUserCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/AuthenticateUserCommand"
10 | body(
11 | "userId": anyUuid(),
12 | "userName": anyNonBlankString(),
13 | "password": anyNonBlankString()
14 | )
15 | headers {
16 | contentType applicationJson()
17 | }
18 | }
19 | response {
20 | status HttpStatus.OK.value()
21 | async()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postCancelCashReservationCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/CancelCashReservationCommand"
10 | body(
11 | "portfolioId": anyUuid(),
12 | "transactionId": anyUuid(),
13 | "amountOfMoneyToCancel": anyPositiveInt()
14 | )
15 | headers {
16 | contentType applicationJson()
17 | }
18 | }
19 | response {
20 | status HttpStatus.OK.value()
21 | async()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postCancelItemReservationForPortfolioCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/CancelItemReservationForPortfolioCommand"
10 | body(
11 | "portfolioId": anyUuid(),
12 | "orderBookId": anyUuid(),
13 | "transactionId": anyUuid(),
14 | "amountOfItemsToCancel": anyPositiveInt()
15 | )
16 | headers {
17 | contentType applicationJson()
18 | }
19 | }
20 | response {
21 | status HttpStatus.OK.value()
22 | async()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postCancelTransactionCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/CancelTransactionCommand"
10 | body(
11 | "transactionId": anyUuid()
12 | )
13 | headers {
14 | contentType applicationJson()
15 | }
16 | }
17 | response {
18 | status HttpStatus.OK.value()
19 | async()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postConfirmCashReservationCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/ConfirmCashReservationCommand"
10 | body(
11 | "portfolioId": anyUuid(),
12 | "transactionId": anyUuid(),
13 | "amountOfMoneyToConfirmInCents": anyPositiveInt()
14 | )
15 | headers {
16 | contentType applicationJson()
17 | }
18 | }
19 | response {
20 | status HttpStatus.OK.value()
21 | async()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postConfirmItemReservationForPortfolioCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/ConfirmItemReservationForPortfolioCommand"
10 | body(
11 | "portfolioId": anyUuid(),
12 | "orderBookId": anyUuid(),
13 | "transactionId": anyUuid(),
14 | "amountOfItemsToConfirm": anyPositiveInt()
15 | )
16 | headers {
17 | contentType applicationJson()
18 | }
19 | }
20 | response {
21 | status HttpStatus.OK.value()
22 | async()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postConfirmTransactionCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/ConfirmTransactionCommand"
10 | body(
11 | "transactionId": anyUuid()
12 | )
13 | headers {
14 | contentType applicationJson()
15 | }
16 | }
17 | response {
18 | status HttpStatus.OK.value()
19 | async()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postCreateCompanyCommandShouldReturnOkAndUuid.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/CreateCompanyCommand"
10 | body(
11 | "companyName": anyNonEmptyString(),
12 | "companyValue": anyPositiveInt(),
13 | "amountOfShares": anyPositiveInt()
14 | )
15 | headers {
16 | contentType applicationJson()
17 | }
18 | }
19 | response {
20 | status HttpStatus.OK.value()
21 | body("f82c4dd0-a785-11e8-98d0-529269fb1459")
22 | headers {
23 | contentType textPlain()
24 | }
25 | async()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postCreateOrderBookCommandShouldReturnOkAndUuid.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/CreateOrderBookCommand"
10 | body
11 | headers {
12 | contentType applicationJson()
13 | }
14 | }
15 | response {
16 | status HttpStatus.OK.value()
17 | body("f82c40ec-a785-11e8-98d0-529269fb1459")
18 | headers {
19 | contentType textPlain()
20 | }
21 | async()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postCreatePortfolioCommandShouldReturnOkAndUuid.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/CreatePortfolioCommand"
10 | body(
11 | "userId": anyUuid()
12 | )
13 | headers {
14 | contentType applicationJson()
15 | }
16 | }
17 | response {
18 | status HttpStatus.OK.value()
19 | body("f82c481c-a785-11e8-98d0-529269fb1459")
20 | headers {
21 | contentType textPlain()
22 | }
23 | async()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postCreateUserCommandShouldReturnOkAndUuid.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/CreateUserCommand"
10 | body(
11 | "name": anyNonBlankString(),
12 | "username": anyNonBlankString(),
13 | "password": anyNonBlankString()
14 | )
15 | headers {
16 | contentType applicationJson()
17 | }
18 | }
19 | response {
20 | status HttpStatus.OK.value()
21 | body("98684ad8-987e-4d16-8ad8-b620f4320f4c")
22 | headers {
23 | contentType textPlain()
24 | }
25 | async()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postDepositCashCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/DepositCashCommand"
10 | body(
11 | "portfolioId": anyUuid(),
12 | "moneyToAddInCents": anyPositiveInt()
13 | )
14 | headers {
15 | contentType applicationJson()
16 | }
17 | }
18 | response {
19 | status HttpStatus.OK.value()
20 | async()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postExecutedTransactionCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/ExecutedTransactionCommand"
10 | body(
11 | "transactionId": anyUuid(),
12 | "amountOfItems": anyPositiveInt(),
13 | "itemPrice": anyPositiveInt()
14 | )
15 | headers {
16 | contentType applicationJson()
17 | }
18 | }
19 | response {
20 | status HttpStatus.OK.value()
21 | async()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postOfIncorrectCommandPayloadReturnsNotFound.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/CreateCompanyCommand"
10 | body("{}") // An empty Body points to a faulty command payload
11 | headers {
12 | contentType applicationJson()
13 | }
14 | }
15 | response {
16 | status HttpStatus.NOT_FOUND.value()
17 | async()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postOfNoneExistingCommandReturnsNotFound.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/SomeNoneExistingCommand"
10 | body("{}")
11 | headers {
12 | contentType applicationJson()
13 | }
14 | }
15 | response {
16 | status HttpStatus.NOT_FOUND.value()
17 | async()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postReserveCashCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/ReserveCashCommand"
10 | body(
11 | "portfolioId": anyUuid(),
12 | "transactionId": anyUuid(),
13 | "amountOfMoneyToReserve": anyPositiveInt()
14 | )
15 | headers {
16 | contentType applicationJson()
17 | }
18 | }
19 | response {
20 | status HttpStatus.OK.value()
21 | async()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postReserveItemsCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/ReserveItemsCommand"
10 | body(
11 | "portfolioId": anyUuid(),
12 | "orderBookId": anyUuid(),
13 | "transactionId": anyUuid(),
14 | "amountOfItemsToReserve": anyPositiveInt()
15 | )
16 | headers {
17 | contentType applicationJson()
18 | }
19 | }
20 | response {
21 | status HttpStatus.OK.value()
22 | async()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postStartBuyTransactionCommandShouldReturnOkAndUuid.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/StartBuyTransactionCommand"
10 | body(
11 | "orderBookId": anyUuid(),
12 | "portfolioId": anyUuid(),
13 | "tradeCount": anyPositiveInt(),
14 | "pricePerItem": anyPositiveInt()
15 | )
16 | headers {
17 | contentType applicationJson()
18 | }
19 | }
20 | response {
21 | status HttpStatus.OK.value()
22 | body("f82c4984-a785-11e8-98d0-529269fb1459")
23 | headers {
24 | contentType textPlain()
25 | }
26 | async()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postStartSellTransactionCommandShouldReturnOkAndUuid.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/StartSellTransactionCommand"
10 | body(
11 | "orderBookId": anyUuid(),
12 | "portfolioId": anyUuid(),
13 | "tradeCount": anyPositiveInt(),
14 | "pricePerItem": anyPositiveInt()
15 | )
16 | headers {
17 | contentType applicationJson()
18 | }
19 | }
20 | response {
21 | status HttpStatus.OK.value()
22 | body("f82c4ace-a785-11e8-98d0-529269fb1459")
23 | headers {
24 | contentType textPlain()
25 | }
26 | async()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/command/postWithdrawCashCommandShouldReturnOk.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.command
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'POST'
9 | url "/command/WithdrawCashCommand"
10 | body(
11 | "portfolioId": anyUuid(),
12 | "amountToPayInCents": anyPositiveInt()
13 | )
14 | headers {
15 | contentType applicationJson()
16 | }
17 | }
18 | response {
19 | status HttpStatus.OK.value()
20 | async()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/findAllCompaniesShouldReturnAllCompanies.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/company"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.OK.value()
16 | body("""
17 | [{
18 | "identifier": "f82c40ec-a785-11e8-98d0-529269fb1459",
19 | "name": "Pivotal",
20 | "value": 1337,
21 | "amountOfShares": 42,
22 | "tradeStarted": false
23 | }]
24 | """
25 | )
26 | headers {
27 | contentType applicationJson()
28 | }
29 | async()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/findAllUsersQueryShouldReturnAllUsers.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/user"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.OK.value()
16 | body("""
17 | [{
18 | "identifier": "98684ad8-987e-4d16-8ad8-b620f4320f4c",
19 | "name": "Pieter Humphrey",
20 | "username": "john.doe",
21 | "userName": "john.doe",
22 | "fullName": "Pieter Humphrey",
23 | "userId": "98684ad8-987e-4d16-8ad8-b620f4320f4c"
24 | }]
25 | """
26 | )
27 | headers {
28 | contentType applicationJson()
29 | }
30 | async()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getCompanyByIdShouldReturnCompany.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/company/f82c40ec-a785-11e8-98d0-529269fb1459"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.OK.value()
16 | body(
17 | "identifier": "f82c40ec-a785-11e8-98d0-529269fb1459",
18 | "name": "Pivotal",
19 | "value": 1337,
20 | "amountOfShares": 42,
21 | "tradeStarted": false
22 | )
23 | headers {
24 | contentType applicationJson()
25 | }
26 | async()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getCompanyByIdShouldReturnNotFoundForNonExistingId.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/company/non-existing-id"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.NOT_FOUND.value()
16 | async()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getExecutedTradesByOrderBookIdShouldNotFoundForNonExistingId.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/executed-trades/non-existing-id"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.NOT_FOUND.value()
16 | async()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getExecutedTradesByOrderBookIdShouldReturnExecutedTrades.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/executed-trades/f82c40ec-a785-11e8-98d0-529269fb1459"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.OK.value()
16 | body([
17 | "generatedId": 1,
18 | "tradeCount" : 30,
19 | "tradePrice" : 180,
20 | "companyName": "Pivotal",
21 | "orderBookId": "f82c40ec-a785-11e8-98d0-529269fb1459"
22 | ])
23 | headers {
24 | contentType applicationJson()
25 | }
26 | async()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getOrderBookByIdShouldReturnNotFoundForNonExistingId.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/order-book/non-existing-id"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.NOT_FOUND.value()
16 | async()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getOrderBookByIdShouldReturnOrderBook.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/order-book/f82c40ec-a785-11e8-98d0-529269fb1459"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.OK.value()
16 | body(
17 | "identifier": "f82c40ec-a785-11e8-98d0-529269fb1459",
18 | "companyIdentifier": "f82c40ec-a785-11e8-98d0-529269fb1459",
19 | "companyName": "Pivotal",
20 | "sellOrders": [
21 | "jpaId" : 2,
22 | "identifier" : "f82c4ace-a785-11e8-98d0-529269fb1459",
23 | "tradeCount" : 5,
24 | "itemPrice" : 75,
25 | "userId" : "98684ad8-987e-4d16-8ad8-b620f4320f4c",
26 | "itemsRemaining": 20,
27 | "type" : "Sell"
28 | ],
29 | "buyOrders": [
30 | "jpaId" : 1,
31 | "identifier" : "f82c4984-a785-11e8-98d0-529269fb1459",
32 | "tradeCount" : 5,
33 | "itemPrice" : 50,
34 | "userId" : "98684ad8-987e-4d16-8ad8-b620f4320f4c",
35 | "itemsRemaining": 10,
36 | "type" : "Buy"
37 | ]
38 | )
39 | headers {
40 | contentType applicationJson()
41 | }
42 | async()
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getOrderBooksByCompanyIdShouldReturnNotFoundForNonExistingId.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/order-book/by-company/non-existing-id"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.NOT_FOUND.value()
16 | async()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getOrderBooksByCompanyIdShouldReturnOrderBook.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/order-book/by-company/f82c40ec-a785-11e8-98d0-529269fb1459"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.OK.value()
16 | body("""
17 | [ {
18 | "identifier" : "f82c40ec-a785-11e8-98d0-529269fb1459",
19 | "companyIdentifier" : "f82c40ec-a785-11e8-98d0-529269fb1459",
20 | "companyName" : "Pivotal",
21 | "sellOrders" : [ {
22 | "jpaId" : 2,
23 | "identifier" : "f82c4ace-a785-11e8-98d0-529269fb1459",
24 | "tradeCount" : 5,
25 | "itemPrice" : 75,
26 | "userId" : "98684ad8-987e-4d16-8ad8-b620f4320f4c",
27 | "itemsRemaining" : 20,
28 | "type" : "Sell"
29 | } ],
30 | "buyOrders" : [ {
31 | "jpaId" : 1,
32 | "identifier" : "f82c4984-a785-11e8-98d0-529269fb1459",
33 | "tradeCount" : 5,
34 | "itemPrice" : 50,
35 | "userId" : "98684ad8-987e-4d16-8ad8-b620f4320f4c",
36 | "itemsRemaining" : 10,
37 | "type" : "Buy"
38 | } ]
39 | } ]
40 | """)
41 | headers {
42 | contentType applicationJson()
43 | }
44 | async()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getPortfolioByIdShouldReturnNotFoundForNonExistingId.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/portfolio/non-existing-id"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.NOT_FOUND.value()
16 | async()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getPortfolioByIdShouldReturnPortfolio.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/portfolio/f82c481c-a785-11e8-98d0-529269fb1459"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.OK.value()
16 | body("""
17 | {
18 | "identifier" : "f82c481c-a785-11e8-98d0-529269fb1459",
19 | "userId" : "98684ad8-987e-4d16-8ad8-b620f4320f4c",
20 | "amountOfMoney" : 1000000,
21 | "reservedAmountOfMoney" : 5000,
22 | "itemsInPossession" : {
23 | "f82c40ec-a785-11e8-98d0-529269fb1459" : {
24 | "generatedId" : 1,
25 | "identifier" : "f82c40ec-a785-11e8-98d0-529269fb1459",
26 | "companyId" : "f82c40ec-a785-11e8-98d0-529269fb1459",
27 | "companyName" : "Pivotal",
28 | "amount" : 321
29 | }
30 | },
31 | "itemsReserved" : {
32 | "f82c40ec-a785-11e8-98d0-529269fb1459" : {
33 | "generatedId" : 2,
34 | "identifier" : "f82c40ec-a785-11e8-98d0-529269fb1459",
35 | "companyId" : "f82c40ec-a785-11e8-98d0-529269fb1459",
36 | "companyName" : "Pivotal",
37 | "amount" : 654
38 | }
39 | }
40 | }
41 | """)
42 | headers {
43 | contentType applicationJson()
44 | }
45 | async()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getPortfolioByUserIdShouldReturnNotFoundForNonExistingId.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/portfolio/by-user/non-existing-id"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.NOT_FOUND.value()
16 | async()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getPortfolioByUserIdShouldReturnPortfolio.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/portfolio/by-user/98684ad8-987e-4d16-8ad8-b620f4320f4c"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.OK.value()
16 | body("""
17 | {
18 | "identifier" : "f82c481c-a785-11e8-98d0-529269fb1459",
19 | "userId" : "98684ad8-987e-4d16-8ad8-b620f4320f4c",
20 | "amountOfMoney" : 1000000,
21 | "reservedAmountOfMoney" : 5000,
22 | "itemsInPossession" : {
23 | "f82c40ec-a785-11e8-98d0-529269fb1459" : {
24 | "generatedId" : 1,
25 | "identifier" : "f82c40ec-a785-11e8-98d0-529269fb1459",
26 | "companyId" : "f82c40ec-a785-11e8-98d0-529269fb1459",
27 | "companyName" : "Pivotal",
28 | "amount" : 321
29 | }
30 | },
31 | "itemsReserved" : {
32 | "f82c40ec-a785-11e8-98d0-529269fb1459" : {
33 | "generatedId" : 2,
34 | "identifier" : "f82c40ec-a785-11e8-98d0-529269fb1459",
35 | "companyId" : "f82c40ec-a785-11e8-98d0-529269fb1459",
36 | "companyName" : "Pivotal",
37 | "amount" : 654
38 | }
39 | }
40 | }
41 | """)
42 | headers {
43 | contentType applicationJson()
44 | }
45 | async()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getTransactionByIdShouldReturnNotFoundForNonExistingId.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/transaction/non-existing-id"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.NOT_FOUND.value()
16 | async()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getTransactionByIdShouldReturnTransaction.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/transaction/f82c448e-a785-11e8-98d0-529269fb1459"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.OK.value()
16 | body(
17 | "identifier": "f82c448e-a785-11e8-98d0-529269fb1459",
18 | "orderBookId": "f82c40ec-a785-11e8-98d0-529269fb1459",
19 | "portfolioId": "f82c481c-a785-11e8-98d0-529269fb1459",
20 | "companyName": "Pivotal",
21 | "amountOfItems": 50,
22 | "amountOfExecutedItems": 25,
23 | "pricePerItem": 123,
24 | "state": "CONFIRMED",
25 | "type": "BUY"
26 | )
27 | headers {
28 | contentType applicationJson()
29 | }
30 | async()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getTransactionsByPortfolioIdShouldReturnNotFoundForNonExistingId.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/transaction/by-portfolio/non-existing-id"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.NOT_FOUND.value()
16 | async()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getTransactionsByPortfolioIdShouldReturnTransactions.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/transaction/by-portfolio/f82c481c-a785-11e8-98d0-529269fb1459"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.OK.value()
16 | body([
17 | "identifier" : "f82c448e-a785-11e8-98d0-529269fb1459",
18 | "orderBookId" : "f82c40ec-a785-11e8-98d0-529269fb1459",
19 | "portfolioId" : "f82c481c-a785-11e8-98d0-529269fb1459",
20 | "companyName" : "Pivotal",
21 | "amountOfItems" : 50,
22 | "amountOfExecutedItems": 25,
23 | "pricePerItem" : 123,
24 | "state" : "CONFIRMED",
25 | "type" : "BUY"
26 | ])
27 | headers {
28 | contentType applicationJson()
29 | }
30 | async()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getUserByIdShouldReturnNotFoundForNonExistingId.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/user/non-existing-id"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.NOT_FOUND.value()
16 | async()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/trader-app/src/test/resources/contracts/trader/query/getUserByIdShouldReturnUser.groovy:
--------------------------------------------------------------------------------
1 | package contracts.trader.query
2 |
3 | import org.springframework.cloud.contract.spec.Contract
4 | import org.springframework.http.HttpStatus
5 |
6 | Contract.make {
7 | request {
8 | method 'GET'
9 | url "/query/user/98684ad8-987e-4d16-8ad8-b620f4320f4c"
10 | headers {
11 | contentType applicationJson()
12 | }
13 | }
14 | response {
15 | status HttpStatus.OK.value()
16 | body(
17 | "identifier": "98684ad8-987e-4d16-8ad8-b620f4320f4c",
18 | "name": "Pieter Humphrey",
19 | "username": "john.doe",
20 | "userName": "john.doe",
21 | "fullName": "Pieter Humphrey",
22 | "userId": "98684ad8-987e-4d16-8ad8-b620f4320f4c"
23 | )
24 | headers {
25 | contentType applicationJson()
26 | }
27 | async()
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/trading-engine/manifest.yml:
--------------------------------------------------------------------------------
1 | ---
2 | applications:
3 | - name: esrefarch-demo-trading-engine
4 | path: target/trading-engine.jar
5 | timeout: 120
6 | instances: 1
7 | buildpacks:
8 | - java_buildpack
9 | health-check-type: port
10 | services:
11 | - enginedb
12 | - rabbit
13 | - config
14 | - registry
15 | env:
16 | TRUST_CERTS: api.run.pivotal.io
17 |
--------------------------------------------------------------------------------
/trading-engine/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | trading-engine
7 | 0.0.1-SNAPSHOT
8 | jar
9 |
10 | Trading Engine
11 | Trading Engine
12 |
13 |
14 | io.pivotal.refarch.cqrs.trader
15 | trader
16 | 0.0.1-SNAPSHOT
17 |
18 |
19 |
20 | ${artifactId}
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/OrderBookId.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import org.springframework.util.Assert;
6 |
7 | import java.io.Serializable;
8 | import java.util.Objects;
9 | import java.util.UUID;
10 |
11 | public class OrderBookId implements Serializable {
12 |
13 | private static final long serialVersionUID = -3483676125514883162L;
14 |
15 | private final String identifier;
16 |
17 | public OrderBookId() {
18 | this(UUID.randomUUID().toString());
19 | }
20 |
21 | @JsonCreator
22 | public OrderBookId(String identifier) {
23 | Assert.notNull(identifier, "Identifier parameter may not be null");
24 | this.identifier = identifier;
25 | }
26 |
27 | @JsonValue
28 | public String getIdentifier() {
29 | return identifier;
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(identifier);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object obj) {
39 | if (this == obj) {
40 | return true;
41 | }
42 | if (obj == null || getClass() != obj.getClass()) {
43 | return false;
44 | }
45 | final OrderBookId other = (OrderBookId) obj;
46 | return Objects.equals(this.identifier, other.identifier);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return getIdentifier();
52 | }
53 | }
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/trades/OrderId.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders.trades;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import org.springframework.util.Assert;
6 |
7 | import java.io.Serializable;
8 | import java.util.Objects;
9 | import java.util.UUID;
10 |
11 | public class OrderId implements Serializable {
12 |
13 | private static final long serialVersionUID = 4034328048230397374L;
14 |
15 | private final String identifier;
16 |
17 | public OrderId() {
18 | this(UUID.randomUUID().toString());
19 | }
20 |
21 | @JsonCreator
22 | public OrderId(String identifier) {
23 | Assert.notNull(identifier, "Identifier parameter may not be null");
24 | this.identifier = identifier;
25 | }
26 |
27 | @JsonValue
28 | public String getIdentifier() {
29 | return identifier;
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(identifier);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object obj) {
39 | if (this == obj) {
40 | return true;
41 | }
42 | if (obj == null || getClass() != obj.getClass()) {
43 | return false;
44 | }
45 | final OrderId other = (OrderId) obj;
46 | return Objects.equals(this.identifier, other.identifier);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return getIdentifier();
52 | }
53 | }
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/trades/commands.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders.trades
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
5 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction.TransactionId
6 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
7 | import org.axonframework.commandhandling.TargetAggregateIdentifier
8 | import javax.validation.constraints.Min
9 |
10 | abstract class OrderBookCommand(@TargetAggregateIdentifier open val orderBookId: OrderBookId)
11 |
12 | data class CreateOrderBookCommand(
13 | @JsonProperty("orderBookId") override val orderBookId: OrderBookId = OrderBookId()
14 | ) : OrderBookCommand(orderBookId)
15 |
16 | abstract class OrderCommand(
17 | @JsonProperty("orderBookId") override val orderBookId: OrderBookId,
18 | @JsonProperty("orderId") open val orderId: OrderId,
19 | @JsonProperty("portfolioId") open val portfolioId: PortfolioId,
20 | @JsonProperty("transactionId") open val transactionId: TransactionId,
21 | @JsonProperty("tradeCount") @Min(0) open val tradeCount: Long,
22 | @JsonProperty("itemPrice") @Min(0) open val itemPrice: Long
23 | ) : OrderBookCommand(orderBookId)
24 |
25 | data class CreateBuyOrderCommand(
26 | override val orderId: OrderId,
27 | override val portfolioId: PortfolioId,
28 | override val orderBookId: OrderBookId,
29 | override val transactionId: TransactionId,
30 | override val tradeCount: Long,
31 | override val itemPrice: Long
32 | ) : OrderCommand(orderBookId, orderId, portfolioId, transactionId, tradeCount, itemPrice)
33 |
34 | data class CreateSellOrderCommand(
35 | override val orderId: OrderId,
36 | override val portfolioId: PortfolioId,
37 | override val orderBookId: OrderBookId,
38 | override val transactionId: TransactionId,
39 | override val tradeCount: Long,
40 | override val itemPrice: Long
41 | ) : OrderCommand(orderBookId, orderId, portfolioId, transactionId, tradeCount, itemPrice)
42 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/trades/events.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders.trades
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction.TransactionId
5 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
6 | import java.io.Serializable
7 |
8 | abstract class AbstractOrderPlacedEvent(
9 | open val orderBookId: OrderBookId,
10 | open val orderId: OrderId,
11 | open val transactionId: TransactionId,
12 | open val tradeCount: Long,
13 | open val itemPrice: Long,
14 | open val portfolioId: PortfolioId
15 | )
16 |
17 | data class BuyOrderPlacedEvent(
18 | override val orderBookId: OrderBookId,
19 | override val orderId: OrderId,
20 | override val transactionId: TransactionId,
21 | override val tradeCount: Long,
22 | override val itemPrice: Long,
23 | override val portfolioId: PortfolioId
24 | ) : AbstractOrderPlacedEvent(orderBookId, orderId, transactionId, tradeCount, itemPrice, portfolioId)
25 |
26 | data class SellOrderPlacedEvent(
27 | override val orderBookId: OrderBookId,
28 | override val orderId: OrderId,
29 | override val transactionId: TransactionId,
30 | override val tradeCount: Long,
31 | override val itemPrice: Long,
32 | override val portfolioId: PortfolioId
33 | ) : AbstractOrderPlacedEvent(orderBookId, orderId, transactionId, tradeCount, itemPrice, portfolioId)
34 |
35 | abstract class OrderBookEvent(open val orderBookId: OrderBookId)
36 |
37 | data class OrderBookCreatedEvent(override val orderBookId: OrderBookId) : OrderBookEvent(orderBookId)
38 |
39 | data class TradeExecutedEvent(
40 | val orderBookId: OrderBookId,
41 | val tradeCount: Long,
42 | val tradePrice: Long,
43 | val buyOrderId: OrderId,
44 | val sellOrderId: OrderId,
45 | val buyTransactionId: TransactionId,
46 | val sellTransactionId: TransactionId
47 | ) : Serializable {
48 | companion object {
49 | private const val serialVersionUID = 6292249351659536792L
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/transaction/TransactionId.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import org.springframework.util.Assert;
6 |
7 | import java.io.Serializable;
8 | import java.util.Objects;
9 | import java.util.UUID;
10 |
11 | public class TransactionId implements Serializable {
12 |
13 | private static final long serialVersionUID = -5267104328616955617L;
14 |
15 | private final String identifier;
16 |
17 | public TransactionId() {
18 | this(UUID.randomUUID().toString());
19 | }
20 |
21 | @JsonCreator
22 | public TransactionId(String identifier) {
23 | Assert.notNull(identifier, "Identifier parameter may not be null");
24 | this.identifier = identifier;
25 | }
26 |
27 | @JsonValue
28 | public String getIdentifier() {
29 | return identifier;
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(identifier);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object obj) {
39 | if (this == obj) {
40 | return true;
41 | }
42 | if (obj == null || getClass() != obj.getClass()) {
43 | return false;
44 | }
45 | final TransactionId other = (TransactionId) obj;
46 | return Objects.equals(this.identifier, other.identifier);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return getIdentifier();
52 | }
53 | }
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/orders/valueObjects.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.orders
2 |
3 | enum class TransactionType {
4 | SELL, BUY
5 | }
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/PortfolioId.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import org.springframework.util.Assert;
6 |
7 | import java.io.Serializable;
8 | import java.util.Objects;
9 | import java.util.UUID;
10 |
11 | public class PortfolioId implements Serializable {
12 |
13 | private static final long serialVersionUID = 5649005745169990400L;
14 |
15 | private final String identifier;
16 |
17 | public PortfolioId() {
18 | this(UUID.randomUUID().toString());
19 | }
20 |
21 | @JsonCreator
22 | public PortfolioId(String identifier) {
23 | Assert.notNull(identifier, "Identifier parameter may not be null");
24 | this.identifier = identifier;
25 | }
26 |
27 | @JsonValue
28 | public String getIdentifier() {
29 | return identifier;
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(identifier);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object obj) {
39 | if (this == obj) {
40 | return true;
41 | }
42 | if (obj == null || getClass() != obj.getClass()) {
43 | return false;
44 | }
45 | final PortfolioId other = (PortfolioId) obj;
46 | return Objects.equals(this.identifier, other.identifier);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return getIdentifier();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/cash/commands.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio.cash
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction.TransactionId
5 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioCommand
6 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
7 | import javax.validation.constraints.Min
8 |
9 | data class CancelCashReservationCommand(
10 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
11 | @JsonProperty("transactionId") val transactionId: TransactionId,
12 | @JsonProperty("amountOfMoneyToCancel") val amountOfMoneyToCancel: Long
13 | ) : PortfolioCommand(portfolioId)
14 |
15 | data class ConfirmCashReservationCommand(
16 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
17 | @JsonProperty("transactionId") val transactionId: TransactionId,
18 | @JsonProperty("amountOfMoneyToConfirmInCents") val amountOfMoneyToConfirmInCents: Long
19 | ) : PortfolioCommand(portfolioId)
20 |
21 | data class DepositCashCommand(
22 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
23 | @JsonProperty("moneyToAddInCents") @Min(0) val moneyToAddInCents: Long
24 | ) : PortfolioCommand(portfolioId)
25 |
26 | data class ReserveCashCommand(
27 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
28 | @JsonProperty("transactionId") val transactionId: TransactionId,
29 | @JsonProperty("amountOfMoneyToReserve") @Min(0) val amountOfMoneyToReserve: Long
30 | ) : PortfolioCommand(portfolioId)
31 |
32 | data class WithdrawCashCommand(
33 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
34 | @JsonProperty("amountToPayInCents") @Min(0) val amountToPayInCents: Long
35 | ) : PortfolioCommand(portfolioId)
36 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/cash/events.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio.cash
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction.TransactionId
4 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioEvent
5 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
6 |
7 | data class CashDepositedEvent(
8 | override val portfolioId: PortfolioId,
9 | val moneyAddedInCents: Long
10 | ) : PortfolioEvent(portfolioId)
11 |
12 | data class CashReservationCancelledEvent(
13 | override val portfolioId: PortfolioId,
14 | val transactionId: TransactionId,
15 | val amountOfMoneyToCancel: Long
16 | ) : PortfolioEvent(portfolioId)
17 |
18 | data class CashReservationConfirmedEvent(
19 | override val portfolioId: PortfolioId,
20 | val transactionId: TransactionId,
21 | val amountOfMoneyConfirmedInCents: Long
22 | ) : PortfolioEvent(portfolioId)
23 |
24 | data class CashReservationRejectedEvent(
25 | override val portfolioId: PortfolioId,
26 | val transactionId: TransactionId,
27 | val amountToPayInCents: Long
28 | ) : PortfolioEvent(portfolioId)
29 |
30 | data class CashReservedEvent(
31 | override val portfolioId: PortfolioId,
32 | val transactionId: TransactionId,
33 | val amountToReserve: Long
34 | ) : PortfolioEvent(portfolioId)
35 |
36 | data class CashWithdrawnEvent(
37 | override val portfolioId: PortfolioId,
38 | val amountPaidInCents: Long
39 | ) : PortfolioEvent(portfolioId)
40 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/commands.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 | import io.pivotal.refarch.cqrs.trader.coreapi.users.UserId
5 | import org.axonframework.commandhandling.TargetAggregateIdentifier
6 |
7 | abstract class PortfolioCommand(@TargetAggregateIdentifier open val portfolioId: PortfolioId)
8 |
9 | data class CreatePortfolioCommand(
10 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId = PortfolioId(),
11 | @JsonProperty("userId") val userId: UserId
12 | ) : PortfolioCommand(portfolioId)
13 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/events.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.users.UserId
4 |
5 | abstract class PortfolioEvent(open val portfolioId: PortfolioId)
6 |
7 | class PortfolioCreatedEvent(
8 | override val portfolioId: PortfolioId,
9 | val userId: UserId
10 | ) : PortfolioEvent(portfolioId)
11 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/stock/commands.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio.stock
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
5 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction.TransactionId
6 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioCommand
7 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
8 | import javax.validation.constraints.Min
9 |
10 | data class AddItemsToPortfolioCommand(
11 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
12 | @JsonProperty("orderBookId") val orderBookId: OrderBookId,
13 | @JsonProperty("amountOfItemsToAdd") @Min(0) val amountOfItemsToAdd: Long
14 | ) : PortfolioCommand(portfolioId)
15 |
16 | data class CancelItemReservationForPortfolioCommand(
17 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
18 | @JsonProperty("orderBookId") val orderBookId: OrderBookId,
19 | @JsonProperty("transactionId") val transactionId: TransactionId,
20 | @JsonProperty("amountOfItemsToCancel") val amountOfItemsToCancel: Long
21 | ) : PortfolioCommand(portfolioId)
22 |
23 | data class ConfirmItemReservationForPortfolioCommand(
24 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
25 | @JsonProperty("orderBookId") val orderBookId: OrderBookId,
26 | @JsonProperty("transactionId") val transactionId: TransactionId,
27 | @JsonProperty("amountOfItemsToConfirm") val amountOfItemsToConfirm: Long
28 | ) : PortfolioCommand(portfolioId)
29 |
30 | data class ReserveItemsCommand(
31 | @JsonProperty("portfolioId") override val portfolioId: PortfolioId,
32 | @JsonProperty("orderBookId") val orderBookId: OrderBookId,
33 | @JsonProperty("transactionId") val transactionId: TransactionId,
34 | @JsonProperty("amountOfItemsToReserve") val amountOfItemsToReserve: Long
35 | ) : PortfolioCommand(portfolioId)
36 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/portfolio/stock/events.kt:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.portfolio.stock
2 |
3 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.OrderBookId
4 | import io.pivotal.refarch.cqrs.trader.coreapi.orders.transaction.TransactionId
5 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioEvent
6 | import io.pivotal.refarch.cqrs.trader.coreapi.portfolio.PortfolioId
7 |
8 | data class ItemReservationCancelledForPortfolioEvent(
9 | override val portfolioId: PortfolioId,
10 | val orderBookId: OrderBookId,
11 | val transactionId: TransactionId,
12 | val amountOfCancelledItems: Long
13 | ) : PortfolioEvent(portfolioId)
14 |
15 | data class ItemReservationConfirmedForPortfolioEvent(
16 | override val portfolioId: PortfolioId,
17 | val orderBookId: OrderBookId,
18 | val transactionId: TransactionId,
19 | val amountOfConfirmedItems: Long
20 | ) : PortfolioEvent(portfolioId)
21 |
22 | data class ItemsAddedToPortfolioEvent(
23 | override val portfolioId: PortfolioId,
24 | val orderBookId: OrderBookId,
25 | val amountOfItemsAdded: Long
26 | ) : PortfolioEvent(portfolioId)
27 |
28 | data class ItemsReservedEvent(
29 | override val portfolioId: PortfolioId,
30 | val orderBookId: OrderBookId,
31 | val transactionId: TransactionId,
32 | val amountOfItemsReserved: Long
33 | ) : PortfolioEvent(portfolioId)
34 |
35 | data class ItemToReserveNotAvailableInPortfolioEvent(
36 | override val portfolioId: PortfolioId,
37 | val orderBookId: OrderBookId,
38 | val transactionId: TransactionId
39 | ) : PortfolioEvent(portfolioId)
40 |
41 | data class NotEnoughItemsAvailableToReserveInPortfolioEvent(
42 | override val portfolioId: PortfolioId,
43 | val orderBookId: OrderBookId,
44 | val transactionId: TransactionId,
45 | val availableAmountOfItems: Long,
46 | val amountOfItemsToReserve: Long
47 | ) : PortfolioEvent(portfolioId)
48 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/coreapi/users/UserId.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.coreapi.users;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonValue;
5 | import org.springframework.util.Assert;
6 |
7 | import java.io.Serializable;
8 | import java.util.Objects;
9 | import java.util.UUID;
10 |
11 | public class UserId implements Serializable {
12 |
13 | private static final long serialVersionUID = -4860092244272266543L;
14 |
15 | private final String identifier;
16 |
17 | public UserId() {
18 | this(UUID.randomUUID().toString());
19 | }
20 |
21 | @JsonCreator
22 | public UserId(String identifier) {
23 | Assert.notNull(identifier, "Identifier parameter may not be null");
24 | this.identifier = identifier;
25 | }
26 |
27 | @JsonValue
28 | public String getIdentifier() {
29 | return identifier;
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(identifier);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object obj) {
39 | if (this == obj) {
40 | return true;
41 | }
42 | if (obj == null || getClass() != obj.getClass()) {
43 | return false;
44 | }
45 | final UserId other = (UserId) obj;
46 | return Objects.equals(this.identifier, other.identifier);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return getIdentifier();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/tradingengine/AmqpConfiguration.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.tradingengine;
2 |
3 | import org.springframework.amqp.core.AmqpAdmin;
4 | import org.springframework.amqp.core.Exchange;
5 | import org.springframework.amqp.core.ExchangeBuilder;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.context.annotation.Bean;
8 | import org.springframework.context.annotation.Configuration;
9 |
10 | @Configuration
11 | public class AmqpConfiguration {
12 |
13 | @Bean
14 | public Exchange eventsExchange() {
15 | return ExchangeBuilder.topicExchange("trading-engine-events").build();
16 | }
17 |
18 | @Autowired
19 | public void defineExchange(AmqpAdmin admin) {
20 | admin.declareExchange(eventsExchange());
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/tradingengine/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.tradingengine;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
5 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
6 |
7 | @Configuration
8 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
9 |
10 | @Override
11 | protected void configure(HttpSecurity http) throws Exception {
12 | http.csrf().disable()
13 | .authorizeRequests()
14 | .anyRequest().permitAll();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/trading-engine/src/main/java/io/pivotal/refarch/cqrs/trader/tradingengine/TradingEngineApplication.java:
--------------------------------------------------------------------------------
1 | package io.pivotal.refarch.cqrs.trader.tradingengine;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.fasterxml.jackson.module.kotlin.KotlinModule;
5 | import org.axonframework.serialization.Serializer;
6 | import org.axonframework.serialization.json.JacksonSerializer;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.beans.factory.annotation.Qualifier;
10 | import org.springframework.boot.SpringApplication;
11 | import org.springframework.boot.autoconfigure.SpringBootApplication;
12 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
13 | import org.springframework.context.annotation.Bean;
14 |
15 | @EnableDiscoveryClient
16 | @SpringBootApplication
17 | public class TradingEngineApplication {
18 |
19 | private static final Logger LOG = LoggerFactory.getLogger(TradingEngineApplication.class);
20 |
21 | /*
22 | * There is not much to do here. Apart from integrating with Rabbit for the incoming events
23 | * and then handling those events, the rest of the functionality of the view is provided
24 | * by Spring Data Repositories.
25 | */
26 |
27 | public static void main(String[] args) {
28 | SpringApplication.run(TradingEngineApplication.class, args);
29 | }
30 |
31 | /**
32 | * Instantiate an {@link ObjectMapper} for Jackson de-/serialization.
33 | * Additionally, a {@link KotlinModule} is registered, as the Commands, Events and Queries are written in Kotlin.
34 | *
35 | * @return an {@link ObjectMapper} for Jackson de-/serialization
36 | */
37 | @Bean
38 | public ObjectMapper objectMapper() {
39 | ObjectMapper objectMapper = new ObjectMapper();
40 | objectMapper.registerModule(new KotlinModule());
41 | return objectMapper;
42 | }
43 |
44 | @Bean
45 | @Qualifier("eventSerializer")
46 | public Serializer eventSerializer(ObjectMapper objectMapper) {
47 | return new JacksonSerializer(objectMapper);
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/trading-engine/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | #spring.h2.console.enabled=true
2 | #spring.main.banner-mode=off
3 | #spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL55Dialect
4 | #
5 | management.endpoints.enabled-by-default=true
6 | management.endpoints.web.exposure.include=*
7 | management.cloudfoundry.enabled=true
8 |
9 | axon.distributed.enabled=true
10 | axon.distributed.spring-cloud.fallback-to-http-get=false
11 |
12 | axon.amqp.exchange=trading-engine-events
13 | axon.amqp.transaction-mode=publisher_ack
14 |
15 | axon.serializer.events=jackson
16 |
17 | spring.cloud.services.registrationMethod=direct
18 |
19 | # Open Session In View in combination with Server Sent Events is connection trouble waiting to happen
20 | spring.jpa.open-in-view=false
21 |
--------------------------------------------------------------------------------
/trading-engine/src/main/resources/bootstrap.properties:
--------------------------------------------------------------------------------
1 | spring.application.name=trading-engine
2 | #spring.main.banner-mode=off
3 | #org.springframework.cloud.config=DEBUG
4 |
5 |
6 | spring.jpa.generate-ddl=true
7 | spring.jpa.hibernate.ddl-auto=create
8 | spring.jpa.show-sql=true
9 |
10 | #spring.jpa.database=mysql
11 | #spring.jpa.database=h2
12 | #spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
13 | #spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
14 |
--------------------------------------------------------------------------------
/trading-engine/src/test/resources/bootstrap.properties:
--------------------------------------------------------------------------------
1 | spring.application.name=trading-engine
2 | #spring.main.banner-mode=off
3 |
4 | management.security.enabled=false
5 | security.basic.enabled=false
6 |
7 | spring.thymeleaf.cache=false
8 |
9 | axon.amqp.exchange=CatalogEvents
10 | axon.eventhandling.processors.amqpEvents.source=complaintEventsMethod
11 |
12 | # Set the general logging level within the application
13 | logging.level.io.pivotal.refarch=DEBUG
14 | your.host.is=Me
--------------------------------------------------------------------------------