Amazon EventBridge Intro | Serverless
Tags: aws, serverless, javascript, distributedsystems
Published: 2022-08-03 19:13:00

Amazon EventBridge helps you to simplify your architecture, it is a serverless event bus service, you can use it to connect your applications with data from a variety of sources

The main parts of this article:
1. Amazon EventBridge (Main components)
2. About Event Bus
3. Example

## 1. About Amazon EventBridge
EventBridge allows you to build event-driven architectures, which are loosely coupled and distributed. It enables you to be faster in building and innovating

Some Keynotes:
- **Decoupling**- When using distributed architecture, EventBridge makes sure the application remains decoupled
- **Simplified event routing** - Ensure services remain simple, only listening to events they care about, and only being
responsible for creating events
- **Improved Availability** - In synchronous APIs the availability of the entire system can be impacted from the failure
of any single service. While in asynchronous infrastructure based on events allows more resilience, and with the right architecture design you can improve availability too
- **Third party integration** - you can ingest data seamlessly in near real time using events

EventBridge main parts:
- **Event Buses** - An event bus is a pipeline that receives events. Rules associated with the event bus evaluate events as they arrive. Each rule checks whether an event matches the rule’s criteria
- **Events** - An event is a real-time change in a system, data, or environment. This change can be either in your application or in an AWS service or a SaaS partner service
- **Rules** - A rule matches incoming events and sends them to targets for processing. A single rule can send an event to multiple targets, which then run in parallel. Rules are based either on an event pattern or a schedule
- **Targets** - A target is a resource or endpoint that EventBridge sends an event when it matches the event pattern defined for a rule. The rule processes the event data and sends the pertinent information to the target

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwbami15qci39nsc0j4k.png) 

> 📋 **Note:** For more information and detailed documentation you can visit the official site through [this link](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html)

## 2. What is Event Bus?
An event bus is a pipeline that receives events. Rules associated with the event bus evaluate events as they arrive. Each rule checks whether an event matches the rule's criteria. You associate a rule with a specific event bus, so the rule only applies to events received by that event bus

Event producer don't need any knowledge who is listening to the events and on the other hand services that consume events don't need to know about upstream changes

Event Bus provides an endpoint where the event producer can send events, the router manages directing and filtering those events to the appropriate downstream consumers, consumers can get the events reliably while the producers remain decoupled

## 3. Example
In this section we are going to build a scenario based example

Before starting in serverless.yml we need to add the permission, so that our lambda function is able to put the event into the EventBridge

- serverless.yml:
```yml
provider:
  name: aws
  runtime: nodejs14.x
  stage: v1
  region: eu-west-1
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "events:PutEvents"
      Resource: "*"
```

Every time the user calls `addProduct (λ)` API, the lambda will add the item inside MySQL database and it will trigger an EventBridge that will trigger another Lambda Function to store the logs inside DynamoDB `logProduct (λ)`

- Architecture:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i8uzdufo52g7pqm4d3rz.png) 

- Code logic overview:
```
on API call:
  store in MySQL Database
  if stored successfully:
    putEvents [Source, DetailType, Detail]

when we receive the event:
  trigger target lambda:
    get the value from `event`
    put the data in DynamoDB
```

Rules inside EventBridge use `Event Pattern` that are JSON objects, through the console we are going to create a Rule inside Amazon EventBridge that has the following Event Pattern

```json
{
  "source": ["log.addProduct"]
}
```
And then we specify which lambda function will be triggered in the targets section

The `addProduct` Lambda function: (provoked from API Gateway)
- Route (routes.yml):
```yml
addProduct:
  handler: src/modules/Test/controller/test.addProduct
  events:
    - http:
        method: post
        path: product
        cors: true
```
- Body (sent from postman):
```json
{
    "userId": "user-123",
    "name": "My First Book",
    "category": "Finance",
    "price": 100
}
```

> 📋 **Note:** I am using `mysql-serverless` in order to be able to query MySQL Database

- To stablish connection with my database:
```javascript
const mysql = require('serverless-mysql')({
  config: {
    host: process.env.MYSQL_HOST,
    database: process.env.MYSQL_DATABASE,
    user: process.env.MYSQL_USER,
    password: process.env.MYSQL_PASSWORD,
  }
});

module.exports.mysql = mysql;
```

> 📋 **Note:** In order to create my `Products` table in my database, we are going to use `MySQL Workbench`, and run this sql code below:
```sql
use products;

CREATE TABLE Products (
  ID int Not NULL AUTO_INCREMENT,
  UserID varchar(255),
  Name varchar(255),
  Category varchar(255),
  Price varchar(255),
  PRIMARY KEY (ID)
)
```

- The `addProduct` Lambda function code:
```javascript
module.exports.addProduct = async (event) => {
  try {
    console.log('event =>', event);
    const body = JSON.parse(event.body);

    const {
      userId,
      name,
      category,
      price,
    } = body;
    const result = await mysql.query(`INSERT INTO Products (UserID, Name, Category, Price) VALUES (?, ?, ?, ?)`, [userId, name, category, price]);
    console.log('result =>', result);

    const putEventResult = await putEvent(body);
    console.log('putEventResult =>', putEventResult);

    return {
      statusCode: 200,
      body: JSON.stringify({message: 'This is EventBridge API'}, null, 2),
    };
  } catch (error) {
    console.log(error);
  }
};
```

```javascript
const AWS = require('aws-sdk');
const eventBridge = new AWS.EventBridge();

module.exports.putEvent = (body) => {
  var params = {
    Entries: [
      {
        Detail: JSON.stringify(body),
        DetailType: 'add-product',
        Source: 'log.addProduct'
      }
    ]
  };
  return eventBridge.putEvents(params).promise();
}
```
If we can notice `Source` here is `log.addProduct` the value that we created previously from the console (`Event Pattern`)

The `logProduct` Lambda function code: (triggered from EventBridge)
- Route:
```yml
logProduct:
  handler: src/modules/Test/controller/test.logProduct
```
- Code:
```javascript
module.exports.logProduct = async (event) => {
  try {
    console.log('event =>', event);
    const {
      detail,
    } = event;

    const addResult = await putLog(detail);
    console.log('addResult =>', addResult);

  } catch (error) {
    console.log(error);
  }
};
```
```javascript
const dynamoose = require('dynamoose');
const { v4: uuidv4 } = require('uuid');
const { Log } = require('./log.schema');

dynamoose.aws.sdk.config.update({
  accessKeyId: process.env.access_key_id,
  secretAccessKey: process.env.secret_access_key,
  region: process.env.region,
});

module.exports.putLog = async (body) => {
  const {
    insertedId,
    userId: UserId,
    name: Name,
  } = body;
  
  return await Log.create({
    PK: `Log#${uuidv4()}`,
    SK: `Product#Log`,
    ProductId: insertedId,
    UserId,
    Name,
    Action: 'NEW_PRODUCT',
  });
}
```
- Dynamoose Log Schema:

> 📋 **Note:** Dynamoose is a modeling tool for Amazon DynamoDB

```javascript
const dynamoose = require('dynamoose');

const string_required = {
  type: String,
  required: true,
};

const number_required = {
  type: Number,
  required: true,
};

const LogSchema = new dynamoose.Schema({
  PK: string_required,
  SK: string_required,
  ProductId: number_required,
  UserId: string_required,
  Name: string_required,
  Action: {
    type: String,
    required: true,
    enum: ["NEW_PRODUCT"],
  },
});

module.exports.Log = dynamoose.model('Log', LogSchema);
```

The **event** output inside the CloudWatch for the logProduct lambda function:
```
2022-07-11T21:03:56.946Z	e8e89bb6-1341-4bb9-88c6-3840f5ca2298	INFO	event => {
  version: '0',
  id: '87dafa0a-9e87-9502-a032-c6b02b48a37a',
  'detail-type': 'add-product',
  source: 'log.addProduct',
  account: '260776636198',
  time: '2022-07-11T21:03:56Z',
  region: 'eu-west-1',
  resources: [],
  detail: {
    userId: 'user-123',
    name: 'My First Book',
    category: 'Finance',
    price: 100,
    insertedId: 1
  }
}
```

- Lambda Layers (Dependencies):
```json
{
  "dependencies": {
    "aws-sdk": "^2.1185.0",
    "dynamoose": "^2.8.6",
    "serverless-mysql": "^1.5.4",
    "uuid": "^8.3.2"
  }
}
```

Result:
- Data stored in MySQL Database:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hmj841cbwbmuph3ullla.png) 
- Data stored in DynamoDB:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kb7n6h040pgbgqk4o197.png) 

> 📋 **Note:** To keep this blog simple I am using some of the parameters in the example above, in order to go deeper with AWS-SDK for Amazon EventBridge you can visit [this link](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/EventBridge.html)

## 4. Conclusion
In this article I tried to showcase how powerful the Amazon EventBridge is, and in case you are building distributed architecture it will help you a lot in decoupling your components

For more articles like this and to stay up-to-date, you can connect me on [LinkedIn](https://www.linkedin.com/in/awedis)

This article is part of "Messaging with Serverless" series that I've been writing for a while, if you are interested to read other Serverless offerings from AWS, feel free to visit the links below