Merge remote-tracking branch 'remotes/upstream/master' into wip-customer

This commit is contained in:
dartpopikyardo
2016-10-19 19:47:57 +03:00
8 changed files with 54 additions and 229 deletions

120
README.md
View File

@@ -1,4 +1,4 @@
#Event-Sourcing+CQRS example application # Event-Sourcing+CQRS example application
This example application is the money transfer application described in my talk [Building and deploying microservices with event sourcing, CQRS and Docker](http://plainoldobjects.com/presentations/building-and-deploying-microservices-with-event-sourcing-cqrs-and-docker/). This example application is the money transfer application described in my talk [Building and deploying microservices with event sourcing, CQRS and Docker](http://plainoldobjects.com/presentations/building-and-deploying-microservices-with-event-sourcing-cqrs-and-docker/).
This talk describes a way of architecting highly scalable and available applications that is based on microservices, polyglot persistence, This talk describes a way of architecting highly scalable and available applications that is based on microservices, polyglot persistence,
@@ -35,111 +35,99 @@ The following diagram shows the architecture:
![Money transfer architecture](https://github.com/cer/event-sourcing-examples/wiki/i/applicationarchitecture.png) ![Money transfer architecture](https://github.com/cer/event-sourcing-examples/wiki/i/applicationarchitecture.png)
There are four logical services: There are the following logical services:
* Customers (command-side) - REST API for creating customers
* Accounts (command-side) - REST API for creating accounts * Accounts (command-side) - REST API for creating accounts
* Money transfers (command-side) - REST API for transferring money * Money transfers (command-side) - REST API for transferring money
* Account view updater (query-side) - subscribes to events and updates a MongoDB View * Customers (query-side) - subscribes to events and updates a MongoDB View, and provides an API for retrieving customers
* Account view reader (query-side) - REST API for retrieving accounts * Accounts (query-side) - subscribes to events and updates a MongoDB View, and provides an API for retrieving accounts
There is also an [API gateway](http://microservices.io/patterns/apigateway.html) service that acts as a Facade in front of the services.
One of the neat things about the modular architecture is that there are two ways to deploy these four services: One of the neat things about the modular architecture is that there are two ways to deploy these four services:
* monolithic-service - all services are packaged as a single Spring Boot executable JAR * monolithic-service - all services are packaged as a single Spring Boot executable JAR
* Microservices - three separate Spring Boot executable JARs * Microservices - three separate Spring Boot executable JARs
* customer-command-side-service - command-side customers
* accounts-command-side-service - command-side accounts * accounts-command-side-service - command-side accounts
* transactions-command-side-service - command-side money transfers * transactions-command-side-service - command-side money transfers
* accounts-query-side-service - Account View Updater and Account View Reader * customers-query-side-service - query-side customers
* accounts-query-side-service - query-side accounts
* api-gateway-service - API gateway service
# About the examples # About the examples
There are currently the following versions of the example application: There are currently the following versions of the example application:
* java-spring - a Java and Spring Boot example * java-spring - a Java and Spring Boot example
* scala-spring - a Scala and Spring Boot example * scala-spring - a Scala and Spring Boot example (NOTE: this version is lagging the Java Spring and hasn't been updated in a longtime.)
Other examples will be added shortly including a Scala/Play example. Other examples will be added shortly including a Scala/Play example.
For more information, please see the [wiki](../../wiki) For more information, please see the [wiki](../../wiki)
# About the Event Store # About the Eventuate Platform
The application uses one of two event stores: The application is built using [Eventuate](http://eventuate.io/), which is an application platform for writing transactional microservices.
It provides a simple yet powerful event-driven programming model that is based on event sourcing and Command Query Responsibility Segregation (CQRS).
Eventuate solves the distributed data management problems inherent in a microservice architecture.
It consists of a scalable, distributed event store and client libraries for various languages and frameworks including Java, Scala, and the Spring framework.
* Embedded SQL-based event store, which is great for integration tests. There are two versions of Eventuate:
It is also used when running the monolithic version of the application.
* Event Store server - this is a full featured event store.
See this [wiki page](../../wiki/AboutTheEventStoreServer) for more details.
# Building the application (and running the tests) * [Eventuate SaaS server](http://eventuate.io/usingeventuate.html) - this is a full featured event store that is hosted on AWS
* [Eventuate Local](http://eventuate.io/usingeventuate.html) - an open-source event store that is built using MySQL and Kafka
Both versions of the application use Gradle. There is also an embedded test event store, which is great for integration tests.
To build an application, execute this command in the application's top-level directory:
# Building and running the microservices
This is a Gradle project.
However, you do not need to install Gradle since it will be downloaded automatically.
You just need to have Java 8 installed.
The details of how to build and run the services depend slightly on whether you are using Eventuate SaaS or Eventuate Local.
## Building and running using Eventuate SaaS
First, must [sign up to get your credentials](https://signup.eventuate.io/) in order to get free access to the SaaS version.
Next, build the application
``` ```
./gradlew assemble cd java-spring
./gradlew assemble -P eventuateLocal=true
``` ```
Note: you do not need to install Gradle. Next, you can launch the services using [Docker Compose](https://docs.docker.com/compose/):
It will be automatically downloaded by `./gradlew`.
This will build a Spring Boot jar in each of the `*-service` directories.
You can also run the tests using `gradle build`.
However, you must set some environment variables.
First, you need to tell the query side code how to connect to MongoDB:
``` ```
export SPRING_DATA_MONGODB_URI=mongodb://192.168.59.103/yourdb docker-compose up -d
``` ```
[Docker Compose](https://docs.docker.com/compose/) is a great way to run MongoDB. Finally, you can open the home page, which is served up by the API Gateway: `http://$DOCKER_HOST_IP:8080`
You can run the `docker-compose up -d mongodb` to run MongoDB and then set `SPRING_DATA_MONGODB_URI` as follows:
```
export SPRING_DATA_MONGODB_URI=mongodb://$(docker-machine ip default)/yourdb
```
Second, some of the tests in accounts-command-side-service, transactions-command-side-service, accounts-query-side-service and e2e-test require you to set some environment variables that tell them how to connect to the Event Store server. Note: `DOCKER_HOST_IP` is the IP address of the machine where Docker is running, e.g. the IP address of the VirtualBox VM.
But don't worry.
The build is configured to ignore failures for those projects.
# Running the application ## Building and running using Eventuate Local
To run the application, you must to set the SPRING_DATA_MONGODB_URI environment variable, which tells the query services how to connect to MongoDB. First, build the application
There are a couple of different ways of running the application.
## Running the monolithic application
One option is to run the self-contained monolithic application.
It uses the embedded event store.
Simply use this command:
``` ```
java -jar monolithic-service/build/libs/monolithic-service.jar cd java-spring
./gradlew assemble -P eventuateDriver=local
``` ```
This will start the service running on port 8080 (you can change using the --server.port=9999 option). Next, launch the services using [Docker Compose](https://docs.docker.com/compose/):
Once the service has started you can open the Swagger UI: http://localhost:8080/swagger-ui.html. ```
You can then: export DOCKER_HOST_IP=...
docker-compose -f docker-compose-eventuate-local.yml up -d
```
1. Create two accounts (save the account ids) Note: You need to set `DOCKER_HOST_IP` before running Docker Compose.
2. Create a money transfer This must be an IP address or resolvable hostname.
3. View the updated account balances It cannot be `localhost`.
## Running the microservices Finally, you can open the home page, which is served up by the API Gateway: `http://$DOCKER_HOST_IP:8080`
The other option is to run the services separately.
However, in order to do this you need to [get credentials for the Event Store](../../wiki/AboutTheEventStoreServer).
One way to run the services is to use the scripts `run-all-services.sh`, which runs the services, and `kill-all-services.sh`, which kills the processes.
A much better way, however, is to use Docker Compose.
Simply run the command `docker-compose up` to launch the services.
This will create containers for MongoDB and each of the services.
You can now, for example, use the curl commands in `handy-curl-commands.sh` to interact with the server.
You can also use the Swagger UI exposed by each service `http://host:port/swagger-ui.html`.

103
Vagrantfile vendored
View File

@@ -1,103 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure(2) do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://atlas.hashicorp.com/search.
config.vm.box = "ubuntu/trusty64"
config.vm.provider "virtualbox" do |v|
v.memory = 2048
v.cpus = 2
end
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# config.vm.network "forwarded_port", guest: 80, host: 8080
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# config.vm.synced_folder "../data", "/vagrant_data"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
# end
#
# View the documentation for the provider you are using for more
# information on available options.
# Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
# such as FTP and Heroku are also available. See the documentation at
# https://docs.vagrantup.com/v2/push/atlas.html for more information.
# config.push.define "atlas" do |push|
# push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
# end
# Enable provisioning with a shell script. Additional provisioners such as
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
# documentation for more information about their specific syntax and use.
config.vm.provision "shell", inline: <<-SHELL
#!/bin/sh
# https://github.com/pussinboots/vagrant-devel/blob/master/provision/packages/java8.sh
if which java >/dev/null; then
echo "skip java 8 installation"
else
echo "java 8 installation"
apt-get install --yes python-software-properties
add-apt-repository ppa:webupd8team/java
apt-get update -qq
echo debconf shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections
echo debconf shared/accepted-oracle-license-v1-1 seen true | /usr/bin/debconf-set-selections
apt-get install --yes oracle-java8-installer
yes "" | apt-get -f install
fi
SHELL
config.vm.provision "docker" do |d|
end
config.vm.provision "shell", inline: <<-SHELL
if which docker-compose >/dev/null; then
echo "skip docker-compose installation"
else
sudo bash -c "curl -L https://github.com/docker/compose/releases/download/1.6.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose ; chmod +x /usr/local/bin/docker-compose"
fi
SHELL
end

View File

@@ -1,6 +0,0 @@
#! /bin/bash -e
for dir in java-spring scala-spring; do
(cd $dir ; ./gradlew -b build.gradle $*)
done

View File

@@ -1,4 +0,0 @@
#! /bin/bash
kill `cat account-cs.pid account-qs.pid transfers-cs.pid`
rm account-cs.pid account-qs.pid transfers-cs.pid

View File

@@ -1,24 +0,0 @@
#! /bin/bash -e
# Execute this script in the java-spring or scala-spring directory
# Runs all of the services
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [[ -f account-cs.pid ]]; then
echo pid file exists
exit 1
fi
java -jar accounts-command-side-service/build/libs/accounts-command-side-service.jar > account-cs.log &
echo $! > account-cs.pid
java -jar accounts-query-side-service/build/libs/accounts-query-side-service.jar --server.port=8081 > account-qs.log &
echo $! > account-qs.pid
java -jar transactions-command-side-service/build/libs/transactions-command-side-service.jar --server.port=8082 > transfers-cs.log &
echo $! > transfers-cs.pid
echo -n waiting for services....
$DIR/wait-for-services.sh localhost

View File

@@ -1,5 +0,0 @@
#! /bin/bash -e
for dir in java-spring scala-spring; do
(cd $dir ; ../run-e2e-test.sh $*)
done

View File

@@ -1,15 +0,0 @@
#! /bin/bash -e
# Must be run in the java-spring or scala-spring directories
echo starting services
../run-all-services.sh
echo running test
./gradlew $* -P ignoreE2EFailures=false :e2e-test:cleanTest :e2e-test:test
echo killing services
../kill-all-services.sh

View File

@@ -1,6 +0,0 @@
#! /bin/bash -e
export EVENTUATE_API_KEY_ID=Aladdin
export EVENTUATE_API_KEY_SECRET="open sesame"
export SPRING_DATA_MONGODB_URI=mongodb://192.168.59.103/mydb