eventuate-tram/eventuate-tram-examples-customers-and-orders
An example of Choreography-based sagas in Spring Boot/JPA microservices
= An Eventuate project
image::https://eventuate.io/i/logo.gif[]
This project is part of http://eventuate.io[Eventuate], which is a microservices collaboration platform.
Eventuate Tram Customers and Orders
This application demonstrates two key patterns:
- http://microservices.io/patterns/data/saga.html[Sagas] - implement transactions that span services
- http://microservices.io/patterns/data/cqrs.html[CQRS] - implement queries that retrieve data from multiple services.
The application consists of three services:
Order Service- manages ordersCustomer Service- manages customersOrder History Service- maintains the order history
All services are implemented using Spring Boot, JPA and the https://github.com/eventuate-tram/eventuate-tram-core[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.
The Order History Service implements a CQRS view and subscribes to domain events published by the Order Service and Customer Service
Scroll down to get a tour of a code within a Github Codespace or Visual Studio Code.
== About Sagas
http://microservices.io/patterns/data/saga.html[Sagas] are a mechanism for maintaining data consistency in a http://microservices.io/patterns/microservices.html[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.
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:
- Look at the https://github.com/eventuate-tram/eventuate-tram-sagas-examples-customers-and-orders[Orchestration-based saga example]
- Read the http://microservices.io/patterns/data/saga.html[Saga pattern]
- Look at https://microservices.io/microservices/general/2019/04/28/asynchronous-microservices.html[MicroCPH 2019 presentation]
- Read about sagas in my https://microservices.io/book[Microservices patterns book]
=== The Create Order saga
The saga for creating an Order consists of the follow steps:
- The Order Service creates an
Orderin aPENDINGstate and publishes anOrderCreatedevent - The
Customer Servicereceives the event attempts to reserve credit for thatOrder. It publishes either aCredit Reservedevent or aCreditLimitExceededevent. - The
Order Servicereceives the event and changes the state of the order to eitherAPPROVEDorREJECTED.
== About Command Query Responsibility Segregation (CQRS)
The http://microservices.io/patterns/data/cqrs.html[CQRS pattern] implements queries that retrieves data from multiple services.
It maintains a queryable replica of the data by subscribing to domain events published by the services that own the data.
In this example, the Order History Service maintains a CQRS view in MongoDB by subscribing to domain events published by the Order Service and Customer Service.
The CQRS view stores each customer as a MongoDB document that contains information the customer and their orders.
To learn more about why you need CQRS if you are using microservices:
- Read the http://microservices.io/patterns/data/cqrs.html[CQRS pattern]
- Look at https://microservices.io/microservices/general/2019/04/28/asynchronous-microservices.html[GOTO Chicago 2019 presentation]
- Read about CQRS in my https://microservices.io/book[Microservices patterns book]
== Transactional messaging with Eventuate Tram
The services uses the https://github.com/eventuate-tram/eventuate-tram-core[Eventuate Tram framework] to communicate asynchronously using events.
The flow for publishing a domain event using Eventuate Tram is as follows:
- Eventuate Tram inserts events into the
MESSAGEtable as part of the ACID transaction that updates the JPA entity. - The Eventuate Tram CDC service tracks inserts into the
MESSAGEtable using the MySQL binlog (or Postgres WAL) and publishes messages to Apache Kafka. - A service subscribes to the events, updates its database, and possibly publishes more events.
== Architecture
The following diagram shows the architecture of the Customers and Orders application.
image::./images/Eventuate_Tram_Customer_and_Order_Architecture.png[]
The application consists of three services: Customer Service, Order Service, and Order History Service
=== Customer Service
The Customer Service implements a REST API for managing customers.
The service persists the Customer JPA entity in a MySQL/MsSQL/Postgres database.
Using Eventuate Tram, it publishes Customer domain events that are consumed by the Order Service.
For more information, see the link:./customer-service-canvas.adoc[microservice canvas for the Customer Service].
image::./customer-service-canvas.png[width=300]
=== Order Service
The Order Service implements REST API for managing orders.
The service persists the Order JPA entity in MySQL/MsSQL/Postgres database.
Using Eventuate Tram, it publishes Order domain events that are consumed by the Customer Service.
For more information, see the link:./order-service-canvas.adoc[microservice canvas for the Order Service].
image::./order-service-canvas.png[width=300]
=== Order History Service
The Order History Service implements REST API for querying a customer's order history
This service subscribes to events published by the Order Service and Customer Service and updates a MongoDB-based CQRS view.
For more information, see the link:./order-history-service-canvas.adoc[microservice canvas for the Order History Service].
image::./order-history-service-canvas.png[width=300]
== Building and running
Start the application and the required infrastructure services by running either
./gradlew :end-to-end-tests:runApplicationMySQL
or
./gradlew :end-to-end-tests:runApplicationPostgres
This command starts the containers on unique ports.
It prints out the home page URL.
== Using the application
There are a couple of ways to interact with the application: using the Swagger UIs or using curl.
Once the application has started, visit the home page URL to see the URLs for the Swagger UIs and the API Gateway.
=== Using the Swagger UIs
You can use the Swagger UIs to create customers and orders.
=== Using curl
You can also use curl to interact with the services via the API Gateway - note you need to replace port 8080 with the correct port for the API Gateway.
First, let's create a customer:
$ curl -X POST --header "Content-Type: application/json" -d '{
"creditLimit": {
"amount": 5
},
"name": "Jane Doe"
}' http://localhost:8080/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://localhost:8080/orders
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
{
"orderId": 1
}
Next, check the status of the Order:
$ curl -X GET http://localhost:8080/orders/1
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
{
"orderId": 1,
"orderState": "APPROVED"
}Finally, look at the customer's order history in the Order History Service:
$ curl -X GET --header "Accept: */*" "http://localhost:8080/customers/1/orderhistory"
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
{
"id": 1,
"orders": {
"1": {
"state": "APPROVED",
"orderTotal": {
"amount": 4
}
}
},
"name": "Chris",
"creditLimit": {
"amount": 100
}
}== Get a tour of the code
I've configured a https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour[Code Tour] that will walk through the code in either Visual Studio Code or Github Codespaces.
=== In Visual Studio Code
- If necessary, install the Code Tour extension from the Visual Studio Code Marketplace.
- Use the
CodeTour: Start Tourcommand from the command palette to start the tour.
=== In a Github Codespace
++++
- Create a codespace.
- If necessary, install the Code Tour extension from the Visual Studio Code Marketplace.
- Use the
CodeTour: Start Tourcommand from the command palette to start the tour.
== Got questions?
Don't hesitate to create an issue or see