Skip to content
Microservices, Choreography-based saga, CQRS and Redis!
Branch: master
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci Initial version Mar 22, 2019
buildSrc Initial version Mar 22, 2019
common-swagger Fixed issue with Swagger UI Mar 29, 2019
common Initial version Mar 22, 2019
customer-service-api-events Initial version Mar 22, 2019
customer-service-api-web Initial version Mar 22, 2019
customer-service Initial version Mar 22, 2019
end-to-end-tests Initial version Mar 22, 2019
gradle
images Initial version Mar 22, 2019
java-development Simplified Dockerfile Mar 23, 2019
order-history-service-common Initial version Mar 22, 2019
order-history-service Initial version Mar 22, 2019
order-service-api-events
order-service-api-web Initial version Mar 22, 2019
order-service Initial version Mar 22, 2019
.gitignore Ignore .DS_Store files Mar 29, 2019
LICENSE.md Initial version Mar 22, 2019
README.adoc Initial version Mar 22, 2019
build-and-restart-service.sh Initial version Mar 22, 2019
build-and-test-all.sh Initial version Mar 22, 2019
build.gradle
docker-compose-java-development.yml Initial version Mar 22, 2019
docker-compose.yml
gradle.properties Upgraded to Eventuate Tram RC2 Apr 8, 2019
gradlew
gradlew.bat Initial version Mar 22, 2019
mysql-cli.sh Initial version Mar 22, 2019
prepare-java-development-docker-image.sh
publish-docker-images.sh Fixed issues with Java development image Mar 22, 2019
redis-cli.sh Initial version Mar 22, 2019
settings.gradle Initial version Mar 22, 2019
verify-docker-host-ip.sh Initial version Mar 22, 2019
wait-for-mysql.sh Initial version Mar 22, 2019
wait-for-services.sh Initial version Mar 22, 2019

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:

  • Customer Service - manages customers

  • Order Service - creates orders

  • Order History Service - a CQRS view service that maintains a customer’s order history

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

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.

About CQRS

CQRS is a pattern for implementing queries that retrieve data from multiple services. A service maintains a easily queried replica of the data by subscribing to events published by services that own the data.

Architecture

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

Eventuate Tram Customer and Order Redis Architecture

The application consists of three 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 and the Order History Service.

  • Order Service - implements RESTs 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 and the Order History Service.

  • Order History Service - a CQRS view service that maintains a customer’s order history in Redis. It provides a REST endpoint for retrieving the order history.

There is also:

  • Message Broker - Redis Streams

  • Order History Database - Redis

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 the message broker, which in this example is Redis (Streams). 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.

Build and launch the services using Docker Compose:

docker-compose composeUp

Note:

  1. If you want to run the tests, you need to set DOCKER_HOST_IP before running Docker Compose. This must be an IP address. 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

You can’t perform that action at this time.