To develop event-driven applications in AWS, we often use services like SQS to send commands and SNS to publish events. As an alternative to SNS, AWS also offers Amazon EventBridge.
EventBridge is a serverless service that uses events to connect application components together, making it easier for you to build scalable event-driven applications.
Amazon EventBridge is designed with features that extend beyond SNS, such as advanced event filtering, transformation, and routing. It handles structured events that can follow a specific schema and provides more integration options than SNS. These new features come at a slightly higher cost than SNS, and currently, it does not include a feature to ensure event ordering, which SNS already supports.
The key components to understand about Amazon EventBridge are:
Events: An event is a simple JSON object.
Sources: An event comes from AWS services, custom applications, and partners (SaaS applications).
Event Bus: An event bus acts as a router that receives events and delivers them to zero or more targets.
Rules: An event bus can have multiple rules. A rule filters and routes events to multiple targets when they match a pattern.
Targets: A target is the destination where events are sent.
Let's see how this works in practice by creating two Lambda functions, one to produce events and the other to consume them.
Pre-requisites
Have an IAM User with programmatic access.
Install the Amazon Lambda Templates (
dotnet new -i Amazon.Lambda.Templates
)Install the Amazon Lambda Tools (
dotnet tool install -g Amazon.Lambda.Tools
)Install AWS SAM CLI.
The Backend Services
Run the following commands to set up our Lambda functions:
dotnet new lambda.EmptyFunction -n MyLambda -o .
dotnet add src/MyLambda package AWSSDK.EventBridge
dotnet add src/MyLambda package Amazon.Lambda.APIGatewayEvents
dotnet add src/MyLambda package Amazon.Lambda.CloudWatchEvents
dotnet new sln -n EventBridge
dotnet sln add --in-root src/MyLambda
Open the Program.cs
file and update the content as follows:
using Amazon.EventBridge;
using Amazon.EventBridge.Model;
using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.CloudWatchEvents;
using Amazon.Lambda.Core;
using System.Text.Json;
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace MyLambda;
public class Function
{
private readonly AmazonEventBridgeClient _eventBridgeClient;
public Function()
{
_eventBridgeClient = new AmazonEventBridgeClient();
}
public record Payload(string Key);
public async Task<APIGatewayProxyResponse> Produce(APIGatewayProxyRequest input, ILambdaContext context)
{
var putEventsRequest = new PutEventsRequest
{
Entries = new List<PutEventsRequestEntry>
{
new PutEventsRequestEntry
{
Source = "myapplication",
DetailType = "mycustomevent",
Detail = JsonSerializer.Serialize(new Payload(Guid.NewGuid().ToString())),
EventBusName = "MyEventBus"
}
}
};
var response = await _eventBridgeClient.PutEventsAsync(putEventsRequest);
if (response.FailedEntryCount > 0)
{
return new APIGatewayProxyResponse
{
StatusCode = 500,
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
};
}
else
{
return new APIGatewayProxyResponse
{
StatusCode = 200,
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
};
}
}
public async Task Consume(CloudWatchEvent<Payload> input, ILambdaContext context)
{
context.Logger.LogLine("Event Source: " + input.Source);
context.Logger.LogLine("Event Detail Type: " + input.DetailType);
context.Logger.LogLine("Event Detail: " + input.Detail.Key);
await Task.CompletedTask;
}
}
The Produce
method uses the AmazonEventBridgeClient
class from the package AWSSDK.EventBridge
to publish events. Each PutEventsRequestEntry
defines:
Source
: An identifier for the event's origin.DetailType
: The type of event (typically used for filtering).Detail
: JSON-serialized data for the event.EventBusName
: The name of the event bus. If left empty, thedefault
EventBridge will be used.
The Detail
, DetailType
, and Source
are required to successfully publish an event. If we include event entries in a request that does not include each of those properties, EventBridge will fail that entry. If we submit a request in which none of the entries have each of these properties, EventBridge fails the entire request.
The Consume
method is much simpler. It uses the CloudWatchEvent<T>
class from the Amazon.Lambda.CloudWatchEvents
package to receive events from EventBridge.
AWS SAM template
At the solution level, create a template.yml
file with the following content:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
SAM
Resources:
MyEventBus:
Type: AWS::Events::EventBus
Properties:
Name: "MyEventBus"
ProducerFunction:
Type: AWS::Serverless::Function
Properties:
Timeout: 60
MemorySize: 512
Tracing: Active
Runtime: dotnet8
Architectures:
- x86_64
Handler: MyLambda::MyLambda.Function::Produce
CodeUri: ./src/MyLambda/
Policies:
- EventBridgePutEventsPolicy:
EventBusName: !Ref MyEventBus
Events:
Post:
Type: Api
Properties:
Path: /events
Method: post
ConsumerFunction:
Type: AWS::Serverless::Function
Properties:
Timeout: 60
MemorySize: 512
Tracing: Active
Runtime: dotnet8
Architectures:
- x86_64
Handler: MyLambda::MyLambda.Function::Consume
CodeUri: ./src/MyLambda/
Events:
Trigger:
Type: EventBridgeRule
Properties:
EventBusName: !Ref MyEventBus
Pattern:
source:
- "myapplication"
detail-type:
- "mycustomevent"
Outputs:
MyApiEndpoint:
Description: "API endpoint"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/events"
The script above defines a custom event bus using the AWS::Events::EventBus
resource. Then, we defined two functions with the AWS::Serverless::Function
resource:
ProducerFunction
: The producer function uses theEventBridgePutEventsPolicy
policy template, allowing it to publish events to the custom event bus we created earlier.ConsumerFunction
: The consumer function uses theEventBridgeRule
event source type, which sets the function as the target of an Amazon EventBridge rule. ThePattern
property is used to match the rule with incoming events.
Instead of the
EventBridgeRule
event source, we can use theAWS::Events::Rule
andAWS::Lambda::Permission
resources to achieve the same result.
EventBridgeRule:
Type: AWS::Events::Rule
Properties:
Name: MyRule
EventPattern:
source:
- "myapplication"
detail-type:
- "mycustomevent"
State: "ENABLED"
Targets:
- Arn: !GetAtt ConsumerFunction.Arn
Id: "MyTarget"
LambdaInvokePermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref ConsumerFunction
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt MyEventBus.Arn
Run the following commands to deploy the resources to AWS:
sam build
sam deploy --guided
Once deployed, our producer function is ready to be invoked. You can find all the code here. Thanks, and happy coding.