- Conversation: Supports one-on-one conversation between 2 users. Both User A and User B can initiate the call to start chat with the other party.
- Message Processing: Chat service stores and relays the messages.
- Acknowledgement: Users get acknowledgemnets on their messages.
- Media: Messages are in plain text.
- Storage: Chat service provides persistent storage to save the messages when the recipient is offline.
Out of scope:
- Media sharing: Users share files and photos
- Notification: Users get notified if they are offline and get new messages.
- Consistency: Chat service delivers the messages in the same order of receiving from chat parties.
- Low Latency: User should recieve messages with low latency.
- Availability: Should be highly available.
- Scalability: Should be highly scalable and adapt to the workload (i.e. number of users and number of messages in a given time period)
- Security: Communication must be secure.
- Http(s): Http is client-initiated protocol and server can not initiate the conversation. Http with
keep-alive
in header may establish a connection with the server. But this does not help the server to start the communication. - Polling: Client requests data from the server priodically. This is inefficient as it leads to high number network calls.
- Long polling: To reduce the number of requests, server keeps the connection open till there is data to send.
- Websockets: Common solution for bidirectional real-time communication on a single connection with the lowest latency. We can use this to send async messages from server to the client.
- Stateful services: chat service is the only stateful service and users have persistent network connection.
- Stateless services: Authentications, profiles, etc (TBD)
- Third party integrations: e.g. push notifications (TBD)
Monorepo
architecture to accommodate both frontend and backend projects in the same repository.- Serverless, Event-driven, Function as a Service (FaaS) in the backend with AWS Lambda functions. This gives us high availability for backend services.
- Host frontend in AWS S3.
For services like Authentication and Profile (TBD), Relational databses are a good candidate. We'll revist this once these services implemented.
For Chat service, we will have huge amout of data which users may want to access although the request to have the most recent chat history is more frequent. A key-value NoSQL database looks a better candidate to support our needs although some features (e.g. search) out of scope but might be implemented later.
For our Serverless Architecture, we use DynamoDB
database.
- DynamoDB is a serverless service meaning that it's billed based on your usage and well integrated with Lambda service that we are using.
- DynamoDB is horizontally scalable.
- DynamoDB provides low latency to access data.
- DynamoDB is a good candidate if later we decide to improve our service and make it Multi-Region service with
Global Tables
. This will result to even lower latency for a system to serve the users across the globe. You may find more details of a Multi-region serverless architecture with DynamoDB Global Tables here.
TBD
- connect
- disconnect
- send message
- get messages
- get users
We benefit from API Gateway features to enforce security of the application. We also need to take more steps to make the connection secure.
- Communications Encryption: API Gateway provides encrypted websockets over Transport Layer Security (TLS) with scheme
wss://
. - Cross-Site Websocket Hijacking: This is Cross-Site request Forgery (CSRF) for websockets. If Websocket uses cookies for handshakes in the session and does not provide CSRF token, the connection is vulnerable to an attack in which the attacker may obtain sensitive information by establishing a cross-site websocket connection. We are not establishing sessions and are not facing this attack.
- Denial of Service (DoS): API gateways employ rate limiting and throttling as security measures to mitigate DoS attacks.
- Input Validation: If server does not valiadate and sanitize the input, the request to the server might be intercepted and trigger SQL injection or Cross-Site Scripting (XSS) attack by replacing the payload of the request.
- Authentication and Authorization: Authentication has not been implemented in this project yet (TBD).
We select Serverless Architecture for the backend with the following details.
We detect the events and act based on them on decoupled services.
- Scale and fail independently by decoupling services
- Agile development with leveraging the event router
- Reduce costs by spinning the server (or serverless functions) only when trigerred by the event.
Function as a Service (FaaS) is the comouting part of the Serverless architecture.
- Decoupled
- Lightweight
- Highly Scalable
- Highly Available
- Very cost efficient (pay per use)
We use API Gateway (WebSocket), Lambda functions, and DynamoDB which are well suited for Serverless application and follow the event-driven
architecture with FaaS
serverless compute.
Note: With Serverless architecture, we no longer need Load balancer
or services like Zookeeper
for Service Discovery
. API Gateway is good enough for this purpose and our Serverless architecture adapts well to the workload with Lambda functions.
npm run deploy
We are going to manually upload our React app to S3 bucket and create a website which will be avilable through Cloudfront.
Create a bucket policy that allows public read:
touch ./bucket_policy.json
code ./bucket_policy.json
Add the following into the policy json file:
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<bucket-name>/*"
}
]
}
Run the commands below to get the website url:
npm run build
aws s3api create-bucket --bucket <bucket-name> --region=us-east-1
# When creating a new bucket, default policy doesn't grant public access (Amazon S3 Block Public Access is enabled). If your new bucket # # policy grants public access, you need to run this:
aws s3api delete-public-access-block --bucket <bucket-name>
aws s3api put-bucket-policy --bucket chat-frontend --policy file:///tmp/bucket_policy.json
aws s3 sync ./build s3://<bucket-name>/
aws s3 website s3://<bucket-name>/ --index-document index.html --error-document index.html
The website should be available at
http://<bucket-name>.s3-website.us-east-1.amazonaws.com/
To host the app with CloudFront and S3 using Amplify, use the chat-amplify
folder and follow the steps below to publish the app:
First configure the project
npm install
amplify configure
amplify int
Now is the time to publish
amplify add hosting
amplify publish
Postman | Chat App |
---|---|
We use AWS CodePipeline. A Lambda function accommodates the logic to trigger the appropriate pipeline for each project (i.e. backend and frontend).
Users across the globe connect to the same APIs as well as frontend url available in edge locations.
- Set up a routing rule on Route 53 (e.g. least latency) to route the users to a region.
- Lambda function maps the S3 buckets to regions and modifies the request to hit the right region.