-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add food ordering example Current 2023
- Loading branch information
Showing
122 changed files
with
33,838 additions
and
5,509 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,151 +1,56 @@ | ||
# Food ordering example: How to integrate with external services by using Awakeables and side effects | ||
# Serverless food ordering app with Restate | ||
|
||
This example demonstrates how you can integrate [Restate](https://restate.dev) with external services by using Awakeables and side effects. | ||
The code in this repo was used for: | ||
- Current 2023 presentation | ||
|
||
The example application implements an order processing middleware which sits between food delivery providers and restaurants. | ||
Delivery providers interact with the application by calling the Restate `OrderService`. | ||
The `OrderService` interacts with the restaurants' external point of sale service to dispatch the orders. | ||
The app logic (order workflow) discussed in the presentation can be found under: `app/src/restate-app/services/order_service.ts`. | ||
|
||
![Example diagrams.png](./img/arch.png) | ||
The demo scenario looks as follows: | ||
|
||
The example illustrates the following aspects: | ||
![demo_overview.png](demo_overview.png) | ||
|
||
- How you can use Restate's side effects to make synchronous calls to external services. | ||
- How you can use Awakeables to connect Restate handlers with asynchronous external services. | ||
- How to resolve Awakeables from an external service and thereby resuming Restate invocations. | ||
- How delayed calls can be used to schedule tasks for later moments in time. | ||
|
||
## Download the example | ||
|
||
```shell | ||
wget https://github.com/restatedev/examples/releases/latest/download/typescript-food-ordering.zip && unzip typescript-food-ordering.zip -d typescript-food-ordering && rm typescript-food-ordering.zip | ||
``` | ||
|
||
## Detailed description | ||
|
||
This application implements the order processing middleware that sits between food delivery providers and restaurants. | ||
Delivery providers forward orders to the Restate application via API requests (CreateOrder / CancelOrder / PrepareOrder). | ||
The Restate services process the order and forward it to the appropriate point-of-sale (restaurant handling the order). | ||
|
||
The app is implemented as a single keyed service that is keyed by `orderId`` and maintains the state machine of that order (i.e. the status of the order) as state in Restate. | ||
|
||
When an order is created a workflow is executed to check if the restaurant is open. | ||
If this is the case then the order is accepted and gets created in the point of sales system of the restaurant. | ||
The workflow becomes just another gRPC method that can be called and retried. | ||
It calls the point of sales software of the restaurants as side effects and saves the state of the workflow in Restate. | ||
|
||
### Delayed calls | ||
|
||
Customers can schedule an order for later on (deliveryDelay). | ||
This is implemented via Restate's delayed calls that schedule the preparation of the order to take place at the desired time. | ||
This delayed call is persisted in Restate. | ||
Restate ensures that it happens, and takes care of retries to prevent lost orders and unhappy customers. | ||
|
||
Have a look at the implementation of the `createOrder` function in the OrderService in `services/src/order_service.ts`. | ||
|
||
### Awakeables | ||
|
||
When the order needs to be prepared, the `OrderService` creates an awakeable (persistent promise) and sends the awakeable ID together with the preparation request to the point of sales API of the restaurant. | ||
The preparation is an asynchronous operation during which the workflow is paused. | ||
Once the restaurant has finished the preparation, it resolves the awakeable to resume the `OrderService`. | ||
The `OrderService` then notifies the delivery provider that they should send a driver to the restaurant. | ||
|
||
Have a look at the implementation of the `prepareOrder` function in the ` OrderService`` in `services/src/order_service.ts`. | ||
|
||
## Running this example | ||
|
||
- Latest stable version of [NodeJS](https://nodejs.org/en/) >= v18.17.1 and [npm CLI](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) >= 9.6.7 installed. | ||
- [Docker Engine](https://docs.docker.com/engine/install/) to launch the Restate runtime (not needed for the app implementation itself). | ||
- Optional: Docker Compose | ||
|
||
## Deployment with Docker Compose | ||
|
||
Build the services: | ||
|
||
```shell | ||
docker build ./services/ -t dev.local/food-ordering/services:0.0.1 && \ | ||
docker build ./pos_server/ -t dev.local/food-ordering/pos_server:0.0.1 | ||
``` | ||
## Running locally with Docker compose | ||
|
||
Launch the Docker compose setup: | ||
|
||
```shell | ||
docker compose up | ||
``` | ||
|
||
### Send requests to the service | ||
WebUI is running at http://localhost:3000 | ||
|
||
Create a new order for five cheeseburgers at `FastFood123` with immediate delivery | ||
|
||
You can do this via curl: | ||
Jaeger is running at http://localhost:16686 | ||
|
||
Clean up after bringing setup down: | ||
```shell | ||
curl -X POST http://localhost:8080/OrderService/createOrder -H 'content-type: application/json' -d '{ | ||
"key": "134", | ||
"request": {"restaurantId": "FastFood123", "deliveryDelay": 0, "items": [{"productName": "cheeseburger", "quantity": 5}]} | ||
}' | ||
docker compose rm | ||
``` | ||
|
||
Create a new order for five cheeseburgers at `FastFood123` with delivery delayed for 10 seconds: | ||
## Exploring the demo | ||
|
||
Restate has a psql interface to query the state of the system. | ||
|
||
If you buy some products via the webUI, you can see how the order workflow is executed by querying the state of the order status service: | ||
```shell | ||
curl -X POST http://localhost:8080/OrderService/createOrder -H 'content-type: application/json' -d '{ | ||
"key": "174", | ||
"request": {"restaurantId": "FastFood123", "deliveryDelay": 10000, "items": [{"productName": "cheeseburger", "quantity": 5}]} | ||
}' | ||
watch -n 1 'psql -h localhost -p 9071 -c "select service, service_key_utf8, key, value_utf8 from state s where s.service='"'"'orderStatus'"'"';"' | ||
``` | ||
|
||
You can also check the status of the delivery via: | ||
|
||
Or have a look at the state of all the services, except for the driver simulator: | ||
```shell | ||
curl -X POST http://localhost:8080/OrderService/getOrderStatus -H 'content-type: application/json' -d '{ "key": "174" }' | ||
watch -n 1 'psql -h localhost -p 9071 -c "select service, service_key_utf8, key, value_utf8 from state s where s.service not in ('"'"'driverSimulator'"'"');"' | ||
``` | ||
|
||
To understand the requests that are done, you can have a look at the logs of the runtime, service and PoS server. | ||
For the delayed order request, you will see the late order being scheduled for preparation after 10 seconds. | ||
|
||
## Running locally | ||
|
||
### Run the services | ||
|
||
Install the dependencies and build the application: | ||
|
||
Or you can check the state of the ongoing invocations via: | ||
```shell | ||
cd services | ||
npm install && npm run build | ||
watch -n 1 'psql -h localhost -p 9071 -c "select service, method, service_key_utf8, id, status, invoked_by_service, invoked_by_id from sys_status;"' | ||
``` | ||
|
||
Run the application with: | ||
|
||
```shell | ||
npm run app | ||
``` | ||
|
||
### Run the point of sales server | ||
|
||
In another terminal session, run the point of sales server. | ||
|
||
Install the dependencies and build the application: | ||
|
||
```shell | ||
cd pos_server | ||
npm install && npm run build | ||
``` | ||
|
||
Run the application with: | ||
|
||
```shell | ||
npm run app | ||
``` | ||
|
||
### Start the Restate runtime | ||
|
||
Now [launch the runtime](../../README.md#launching-the-runtime) and [discover the services](../../README.md#connect-runtime-and-services). | ||
## Attribution | ||
|
||
Now you can send requests to the application as described [here](README.md#send-requests-to-the-service). | ||
The implementation of the web app is based on the MIT Licensed repository here: https://github.com/jeffersonRibeiro/react-shopping-cart. | ||
|
||
## Releasing | ||
## Releasing (for Restate developers) | ||
|
||
### Upgrading Typescript SDK | ||
|
||
Upgrade the `@restatedev/restate-sdk` version as described [here](../../README.md#upgrading-the-sdk-dependency-for-restate-developers). | ||
Then run the example via Docker compose. |
2 changes: 1 addition & 1 deletion
2
...ript/food-ordering/services/.dockerignore → typescript/food-ordering/app/.dockerignore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
.dockerignore | ||
node_modules | ||
dist | ||
src/generated | ||
Dockerfile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.