Learn how and why a reactive & serverless architecure is one of the best ways to approach modern product engineering.
Previously unachievable, a reactive serverless architecture built using websockets, an api gateway, database streams and an army of reactive serverless functions in between, provides:
💸 Serverless let's you pay for what you use, let's look at the numbers:
Small Blog Application
First 1,000,000 requests [$0, free tier] +
Next 5,000,000 requests * 0.0000002
= $1 per 6,000,000 requests
Small Banking App
100000 users *
50 requests each per day *
365 days per year *
$0.0000002 per request
= $364.80 per 1,825,000,000 requests
🚌 Reactive / PubSub architecture - drive updates from the data layer, through an army of ephemeral functions to your users autonomously based on their own changes.
Benefits:
🧮 Let your engineers focus on customer needs and building a far superior and more robust product.
Benefits:
// Some custom authoriser which uses auth_key header/queryParam
// and passes user claims onto next function, such as $connect
function customAuthoriser () { ... }
// Websockets $connect action to create a connection for authenticated
// user + inserts into db using user_id as key
function connect (authClaims) { ... }
// Websockets $disconnect action to remove connection/s
// for specific user or set of users
function disconnect () { ... }
// Websockets $defaultMessage action to gracefully handle requests
// which are not supported by API
function defaultMessage () { ... }
// Send a message to a connection
// e.g., chat room, which certain user is a part of needs to be notified
// when new messages are sent there
function sendMessage (connectionId, message) { ... }
// Database change stream broadcast event
function broadcast (event) { ... }# CloudFormation template
MainTable:
Type: 'AWS::DynamoDB::Table'
DeletionPolicy: Retain
Properties:
TableName: ${self:provider.environment.mainTable}-${self:provider.stage}
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES
...export async function broadcast (
event: DynamoDBStreamEvent,
context: Context,
callback: Callback
) {
const record = event.Records[0].dynamodb;
// do something with record here
// e.g., find users who are associated with entity
// and broadcast change if any open websockets connections
// match user ids
...
}// https://rxjs-dev.firebaseapp.com/api/webSocket/webSocket
import {webSocket} from 'rxjs/webSocket';
function setup () {
const apiGatewayWebsocketUrl =
'wss://abcd-1234.ap-southeast.aws.amazon.com';
const subject = webSocket(apiGatewayWebsocketUrl);
subject.subscribe(
// Called whenever there is a message from the server
msg => console.log('message received: ' + msg),
// Called if at any point WebSocket API signals some kind of error
err => console.log(err),
// Called when connection is closed (for whatever reason)
() => console.log('complete')
);
}// https://github.com/RxSwiftCommunity/RxStarscream
import RxStarscream
socket = WebSocket(url: URL(string: "ws://localhost:8080/")!)
socket.connect()
socket.rx.response.subscribe(onNext: { (response: WebSocketEvent) in
switch response {
case .connected:
print("Connected")
case .disconnected(let error):
print("Disconnected with optional error : \(error)")
case .message(let msg):
print("Message : \(msg)")
case .data(_):
print("Data")
case .pong:
print("Pong")
}
}).disposed(by: disposeBag)