You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: README.md
+82-20
Original file line number
Diff line number
Diff line change
@@ -1,34 +1,54 @@
1
1
# Serverless Shopping Cart Microservice
2
2
3
-
This application is a sample application to demonstrate how you could implement a shopping cart microservice using serverless technologies on AWS. The backend is built as a REST API interface, making use of [Amazon API Gateway](https://aws.amazon.com/api-gateway/), [AWS Lambda](https://aws.amazon.com/lambda/), [Amazon Cognito](https://aws.amazon.com/cognito/), and [Amazon DynamoDB](https://aws.amazon.com/dynamodb/). The frontend is a Vue.js application using the [AWS Amplify](https://aws-amplify.github.io/) SDK for authentication and communication with the API.
3
+
This application is a sample application to demonstrate how you could implement a shopping cart microservice using
4
+
serverless technologies on AWS. The backend is built as a REST API interface, making use of [Amazon API Gateway](https://aws.amazon.com/api-gateway/), [AWS Lambda](https://aws.amazon.com/lambda/), [Amazon Cognito](https://aws.amazon.com/cognito/), and [Amazon DynamoDB](https://aws.amazon.com/dynamodb/). The frontend is a Vue.js application using the [AWS Amplify](https://aws-amplify.github.io/) SDK for authentication and communication with the API.
4
5
5
-
To assist in demonstrating the functionality, a bare bones mock "products" service has also been included. Since the authentication parts are likely to be shared between components, there is a separate template for it. The front-end doesn't make any real payment integration at this time.
6
+
To assist in demonstrating the functionality, a bare bones mock "products" service has also been included. Since the
7
+
authentication parts are likely to be shared between components, there is a separate template for it. The front-end
8
+
doesn't make any real payment integration at this time.
Before building the application, I set some requirements on how the cart should behave:
14
17
15
-
- Users should be able to add items to the cart without logging in (an "anonymous cart"), and that cart should persist across browser restarts etc.
16
-
- When logging in, if there were products in an anonymous cart, they should be added to the user's cart from any previous logged in sessions.
18
+
- Users should be able to add items to the cart without logging in (an "anonymous cart"), and that cart should persist
19
+
across browser restarts etc.
20
+
- When logging in, if there were products in an anonymous cart, they should be added to the user's cart from any
21
+
previous logged in sessions.
17
22
- When logging out, the anonymous cart should not have products in it any longer.
18
-
- Items in an anonymous cart should be removed after a period of time, and items in a logged in cart should persist for a longer period.
23
+
- Items in an anonymous cart should be removed after a period of time, and items in a logged in cart should persist
24
+
for a longer period.
19
25
- Admin users to be able to get an aggregated view of the total number of each product in users' carts at any time.
20
26
21
27
### Cart Migration
22
28
23
-
When an item is added to the cart, an item is written in DynamoDB with an identifier which matches a randomly generated (uuid) cookie which is set in the browser. This allows a user to add items to cart and come back to the page later without losing the items they have added. When the user logs in, these items will be removed, and replaced with items with a user id as the pk. If the user already had that product in their cart from a previous logged in session, the quantities would be summed. Because we don't need the deletion of old items to happen immediately as part of a synchronous workflow, we put messages onto an SQS queue, which triggers a worker function to delete the messages.
29
+
When an item is added to the cart, an item is written in DynamoDB with an identifier which matches a randomly generated
30
+
(uuid) cookie which is set in the browser. This allows a user to add items to cart and come back to the page later
31
+
without losing the items they have added. When the user logs in, these items will be removed, and replaced with items
32
+
with a user id as the pk. If the user already had that product in their cart from a previous logged in session, the
33
+
quantities would be summed. Because we don't need the deletion of old items to happen immediately as part of a
34
+
synchronous workflow, we put messages onto an SQS queue, which triggers a worker function to delete the messages.
24
35
25
-
To expire items from users' shopping carts, DynamoDB's native functionality is used where a TTL is written along with the item, after which the item should be removed. In this implementation, the TTL defaults to 1 day for anonymous carts, and 7 days for logged in carts.
36
+
To expire items from users' shopping carts, DynamoDB's native functionality is used where a TTL is written along with
37
+
the item, after which the item should be removed. In this implementation, the TTL defaults to 1 day for anonymous
38
+
carts, and 7 days for logged in carts.
26
39
27
40
### Aggregated View of Products in Carts
28
41
29
-
It would be possible to scan our entire DynamoDB table and sum up the quantities of all the products, but this will be expensive past a certain scale. Instead, we can calculate the total as a running process, and keep track of the total amount.
42
+
It would be possible to scan our entire DynamoDB table and sum up the quantities of all the products, but this will be
43
+
expensive past a certain scale. Instead, we can calculate the total as a running process, and keep track of the total
44
+
amount.
30
45
31
-
When an item is added, deleted or updated in DynamoDB, an event is put onto DynamoDB Streams, which in turn triggers a Lambda function. This function calculates the change in total quantity for each product in users' carts, and writes the quantity back to DynamoDB. The Lambda function is configured so that it will run after either 60 seconds pass, or 100 new events are on the stream. This would enable an admin user to get real time data about the popular products, which could in turn help anticipate inventory. In this implementation, the API is exposed without authentication to demonstrate the functionality.
46
+
When an item is added, deleted or updated in DynamoDB, an event is put onto DynamoDB Streams, which in turn triggers a
47
+
Lambda function. This function calculates the change in total quantity for each product in users' carts, and writes the
48
+
quantity back to DynamoDB. The Lambda function is configured so that it will run after either 60 seconds pass, or 100
49
+
new events are on the stream. This would enable an admin user to get real time data about the popular products, which
50
+
could in turn help anticipate inventory. In this implementation, the API is exposed without authentication to
51
+
demonstrate the functionality.
32
52
33
53
34
54
## Api Design
@@ -44,7 +64,8 @@ POST
44
64
Accepts a product id and quantity as json. Adds specified quantity of an item to cart.
45
65
46
66
`/cart/migrate`
47
-
Called after logging in - migrates items in an anonymous user's cart to belong to their logged in user. If you already have a cart on your logged in user, your "anonymous cart" will be merged with it when you log in.
67
+
Called after logging in - migrates items in an anonymous user's cart to belong to their logged in user. If you already
68
+
have a cart on your logged in user, your "anonymous cart" will be merged with it when you log in.
48
69
49
70
`/cart/checkout`
50
71
Currently just empties cart.
@@ -55,7 +76,8 @@ Accepts a product id and quantity as json. Updates quantity of given item to pro
55
76
56
77
GET
57
78
`/cart/{product-id}/total`
58
-
Returns the total amount of a given product across all carts. This API is not used by the frontend but can be manually called to test.
79
+
Returns the total amount of a given product across all carts. This API is not used by the frontend but can be manually
80
+
called to test.
59
81
60
82
### Product Mock Service
61
83
@@ -75,17 +97,27 @@ SAM CLI, >= version 0.50.0
75
97
AWS CLI
76
98
yarn
77
99
78
-
### Deploy the Backend
100
+
### Setup steps
101
+
102
+
Fork the github repo, then clone your fork locally:
103
+
`git clone https://github.com/<your-github-username>/aws-serverless-shopping-cart && cd aws-serverless-shopping-cart`
79
104
80
-
Clone the project: `git clone <repo-url> && cd <repo-dir>`
105
+
If you wish to use a named profile for your AWS credentials, you can set the environment variable `AWS_PROFILE` before
106
+
running the below commands. For a profile named "development": `export AWS_PROFILE=development`.
81
107
82
-
If you wish to use a named profile for your AWS credentials, you can set the environment variable `AWS_PROFILE` before running the below commands. For a profile named "development": `export AWS_PROFILE=development`.
108
+
You now have 2 options - you can deploy the backend and run the frontend locally, or you can deploy the whole project
109
+
using the AWS Amplify console.
110
+
111
+
## Option 1 - Deploy backend and run frontend locally
112
+
### Deploy the Backend
83
113
84
-
An S3 bucket will be created for you which will be used for deploying source code to AWS. If you wish to use an existing bucket instead, you can manually set the `S3_BUCKET` environment variable to the name of your bucket.
114
+
An S3 bucket will be automatically created for you which will be used for deploying source code to AWS. If you wish to
115
+
use an existing bucket instead, you can manually set the `S3_BUCKET` environment variable to the name of your bucket.
85
116
86
117
Build and deploy the resources:
87
118
```bash
88
-
make backend # Creates S3 bucket if not existing already, then deploys CloudFormation stacks for authentication, a product mock service and the shopping cart service.
119
+
make backend # Creates S3 bucket if not existing already, then deploys CloudFormation stacks for authentication, a
120
+
product mock service and the shopping cart service.
89
121
```
90
122
91
123
### Run the Frontend Locally
@@ -95,16 +127,46 @@ Start the frontend locally:
95
127
make frontend-serve # Retrieves backend config from ssm parameter store to a .env file, then starts service.
96
128
```
97
129
98
-
Once the service is running, you can access the frontend on http://localhost:8080/ and start adding items to your cart. You can create an account by clicking on "Sign In" then "Create Account". Be sure to use a valid email address as you'll need to retrieve the verification code.
130
+
Once the service is running, you can access the frontend on http://localhost:8080/ and start adding items to your cart.
131
+
You can create an account by clicking on "Sign In" then "Create Account". Be sure to use a valid email address as
132
+
you'll need to retrieve the verification code.
99
133
100
-
**Note:** CORS headers on the backend service default to allowing http://localhost:8080/. You will see CORS errors if you access the frontend using the ip (http://127.0.0.1:8080/), or using a port other than 8080.
134
+
**Note:** CORS headers on the backend service default to allowing http://localhost:8080/. You will see CORS errors if
135
+
you access the frontend using the ip (http://127.0.0.1:8080/), or using a port other than 8080.
101
136
102
-
## Clean Up
137
+
###Clean Up
103
138
Delete the CloudFormation stacks created by this project:
104
139
```bash
105
140
make backend-delete
106
141
```
107
142
143
+
## Option 2 - Automatically deploy backend and frontend using Amplify Console
144
+
145
+
146
+
[Create a new personal access token](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line)
147
+
on GitHub.
148
+
Amplify will need this to access your repository. The token will need the “repo” OAuth scope.
0 commit comments