Event Driven Architecture
Overview
In this tutorial, we'll use Klotho config to demonstrate how to build an application with an event driven architecture, using Klotho's pubsub
annotation.
Why?
Some applications only need actions to be taken on specific events. By using Klotho's pubsub capability, we can see how to trigger our application to only run when necessary, thus saving cost.
Getting started with event driven architectures
Prerequisites
- Klotho CLI installed
curl
- Node.js 16.x+ (& NPM)
- TypeScript
Repository
Clone our sample apps git repo and install the npm packages for the ts-events
app.
git clone https://github.com/klothoplatform/sample-apps.git
cd sample-apps/ts-events
npm install
Application Overview
The ts-events
application utilizes the following annotations
- index.ts
- hello_handler.ts
- emitter.ts
- other/handler.ts
/**
* @klotho::execution_unit {
* id = "events-api"
* }
*/
import * as express from "express";
...
/**
* @klotho::expose {
* target = "public"
* id = "app"
* }
*/
app.listen(3000, () => console.log("listening on :3000"))
/**
* @klotho::execution_unit {
* id = "events-hello"
* }
*/
import { MyEmitter } from "./emitter"
...
import events = require("events")
/**
* @klotho::pubsub {
* id = "myEmmitter"
* }
*/
export const MyEmitter = new events.EventEmitter();
...
/**
* @klotho::execution_unit {
* id = "events-other"
* }
*/
...
Event Overview
By annotating an instance of EventEmitter, we can now create publishers and subscribers of our event driven architecture. We do so by declaring events to act on
and events we will emit
.
/* @klotho::pubsub {
* id = "myEmmitter"
* }
*/
export const MyEmitter = new events.EventEmitter();
We define an event hello
, which we will emit when our api endoint /hello/:user
is called:
...
app.post("/hello/:user", async (req, resp) => {
console.log("got", {params: req.params})
MyEmitter.emit('hello', req.params.user)
resp.status(201).send()
})
...
The hello
event triggers our function within hello_handler.ts
, which adds the user to a JavaScript Set, and proceeds to emit a other
event:
MyEmitter.on("hello", async (user) => {
console.log(`hello ${user}`);
await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep 1s
users.add(user);
console.log(`goodbye ${user}`);
MyEmitter.emit("other", "disconnecting");
});
The other
event triggers our function within other/handler.ts
:
pkg.MyEmitter.on("other", async () => {
console.log(`...`);
await new Promise((resolve) =>
setTimeout(resolve, Math.random() * 4000 + 1000)
); // sleep random 1-5 seconds
console.log(`!`);
});
Klotho compile the application
Let start by compiling our TypeScript application into JavaScript.
npx tsc
Then we'll compile using Klotho.
klotho \
. \
--provider aws \
--app myfirstapp \
--outDir compiled
██╗ ██╗██╗ ██████╗ ████████╗██╗ ██╗ ██████╗
██║ ██╔╝██║ ██╔═══██╗╚══██╔══╝██║ ██║██╔═══██╗
█████╔╝ ██║ ██║ ██║ ██║ ███████║██║ ██║
██╔═██╗ ██║ ██║ ██║ ██║ ██╔══██║██║ ██║
██║ ██╗███████╗╚██████╔╝ ██║ ██║ ██║╚██████╔╝
╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
Adding resource input_file_dependencies:
Adding resource exec_unit:events-api
Adding resource exec_unit:events-hello
Adding resource exec_unit:events-other
index.js:21:0: Found 1 route(s) on server 'app' var: app
Adding resource gateway:app
Adding resource pubsub:myEmmitter
index.js: Found 1 topics produced to emitter.js#MyEmitter: [hello]
hello_handler.js: Found 1 topics produced to emitter.js#MyEmitter: [other]
hello_handler.js: Found 1 topics consumed from emitter.js#MyEmitter: [hello]
other/handler.js: Found 1 topics consumed from emitter.js#MyEmitter: [other]
Adding resource topology:myfirstapp
Adding resource infra_as_code:Pulumi (AWS)
Pulumi.myfirstapp.yaml: Make sure to run `pulumi config set aws:region YOUR_REGION --cwd 'compiled/' -s 'myfirstapp'` to configure the target AWS region.
Deploying the application
To deploy the application, refer to the deploying guide.