An example of Choreography-based sagas in Spring Boot/JPA microservices
Switch branches/tags
Nothing to show
Clone or download
Permalink
Failed to load latest commit information.
.circleci Added CircleCI support Jun 1, 2018
buildSrc/src/main/groovy Adding order history. Mar 19, 2018
common-swagger Basic implementation. Mar 16, 2018
common Refactoring. Mar 20, 2018
customer-backend Refactoring. Mar 20, 2018
customer-service Adding order history. Mar 19, 2018
customer-web-api Basic implementation. Mar 16, 2018
end-to-end-tests Postgres wal support with manually build tram Mar 30, 2018
gradle Basic implementation. Mar 16, 2018
images Enhanced README Mar 27, 2018
order-backend Refactoring. Mar 20, 2018
order-history-backend Refactoring. Mar 20, 2018
order-history-common Adding order history. Mar 19, 2018
order-history-view-service Adding order history. Mar 19, 2018
order-service Fixing according feedback from the codereview. Mar 19, 2018
order-web-api Adding order history. Mar 19, 2018
.gitignore Basic implementation. Mar 16, 2018
LICENSE.md Added License Mar 19, 2018
README.adoc Enhanced README Mar 27, 2018
_build-and-test-all.sh Refactoring. Apr 10, 2018
_set-env.sh Refactoring. Apr 10, 2018
build-and-test-all-mysql-binlog.sh Postgres wal support with manually build tram Mar 30, 2018
build-and-test-all-postgres-polling.sh Postgres wal support with manually build tram Mar 30, 2018
build-and-test-all-postgres-wal.sh Postgres wal support with manually build tram Mar 30, 2018
build-and-test-everything.sh Postgres wal support with manually build tram Mar 30, 2018
build.gradle Basic implementation. Mar 16, 2018
docker-compose-mysql-binlog.yml Postgres Wal support. Replacing manually build images/libs to release… May 4, 2018
docker-compose-postgres-polling.yml Postgres Wal support. Replacing manually build images/libs to release… May 4, 2018
docker-compose-postgres-wal.yml Postgres Wal support. Replacing manually build images/libs to release… May 4, 2018
gradle.properties Postgres Wal support. Replacing manually build images/libs to release… May 4, 2018
gradlew Basic implementation. Mar 16, 2018
gradlew.bat Basic implementation. Mar 16, 2018
mysql-cli.sh Changed how DB cli tools connect to DB server Jun 3, 2018
postgres-cli.sh Changed how DB cli tools connect to DB server Jun 3, 2018
set-env-mysql.sh Refactoring. Apr 10, 2018
set-env-postgres.sh Refactoring. Apr 10, 2018
settings.gradle Adding order history. Mar 19, 2018
wait-for-mysql.sh Basic implementation. Mar 16, 2018
wait-for-postgres.sh Basic implementation. Mar 16, 2018
wait-for-services.sh Basic implementation. Mar 16, 2018

README.adoc

Eventuate Tram Customers and Orders

This application demonstrates how to maintain data consistency in an Java/JDBC/JPA-base microservice architecture using sagas. It’s a choreography-based saga version of Eventuate Tram Sagas Customers and Orders

The application consists of two services:

  • Order Service - creates orders

  • Customer Service - manages customers

Both services are implemented using Spring Boot, JPA and the Eventuate Tram framework, which provides transactional publish/subscribe.

The Order Service uses a choreography-based saga to enforce the customer’s credit limit when creating orders.

About sagas

Sagas are a mechanism for maintaining data consistency in a microservice architecture. A saga is a sequence of transactions, each of which is local to a service.

There are two main ways to coordinate sagas: orchestration and choreography. Please see example to learn about orchestration-based sagas. This example uses choreography-based sagas, which use domain events for coordination. Each step of a saga updates the local database and publishes a domain event. The domain event is processed by an event handler, which performs the next local transaction.

To learn more about why you need sagas if you are using microservices:

The Create Order saga

The saga for creating an Order consists of the follow steps:

  1. The Order Service creates an Order in a pending state and publishes an OrderCreated event

  2. The Customer Service receives the event attempts to reserve credit for that Order. It publishes either a Credit Reserved event or a CreditLimitExceeded event.

  3. The Order Service receives the event and changes the state of the order to either approved or rejected.

Architecture

The following diagram shows the architecture of the Customers and Orders application.

Eventuate Tram Customer and Order Architecture

The application consists of two services:

  • Customer Service - implements the REST endpoints for managing customers. The service persists the Customer JPA entity in a MySQL/Postgres database. Using Eventuate Tram, it publishes Customer domain events that are consumed by the Order Service.

  • Order Service - implements a REST endpoint for managing orders. The service persists the Order JPA entity in MySQL/Postgres database. Using Eventuate Tram, it publishes Order domain events that are consumed by the Customer Service.

A service publishes events using Eventuate Tram. Eventuate Tram inserts events into the MESSAGE table as part of the ACID transaction that updates the JPA entity. The Eventuate Tram CDC service tracks inserts into the MESSAGE table using the MySQL binlog and publishes messages to Apache Kafka. A service subscribes to the events and updates its JPA entities in response.

Building and running

Note: you do not need to install Gradle since it will be downloaded automatically. You just need to have Java 8 installed.

First, build the application

./gradlew assemble

Next, launch the services using Docker Compose:

export DOCKER_HOST_IP=...
docker-compose -f docker-compose-mysql.yml build
docker-compose -f docker-compose-mysql.yml up -d

Note:

  1. You can also run the Postgres version using docker-compose-eventuate-postgres.yml

  2. You need to set DOCKER_HOST_IP before running Docker Compose. This must be an IP address or resolvable hostname. It cannot be localhost. See this guide to setting DOCKER_HOST_IP for more information.

Using the application

Once the application has started, you can use the application via the Swagger UI:

You can also use curl to interact with the services. First, let’s create a customer:

$ curl -X POST --header "Content-Type: application/json" -d '{
  "creditLimit": {
    "amount": 5
  },
  "name": "Jane Doe"
}' http://${DOCKER_HOST_IP}:8082/customers

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8

{
  "customerId": 1
}

Next, create an order:

$ curl -X POST --header "Content-Type: application/json" -d '{
  "customerId": 1,
  "orderTotal": {
    "amount": 4
  }
}' http://${DOCKER_HOST_IP}:8081/orders

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8

{
  "orderId": 1
}

Finally, check the status of the Order:

$ curl -X GET http://${DOCKER_HOST_IP}:8081/orders/1

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8

{
  "orderId": 1,
  "orderState": "APPROVED"
}

Got questions?

Don’t hesitate to create an issue or see