Skip to main content

Command Palette

Search for a command to run...

Scalable Event-Driven Notifications:

Decoupling with AWS EventBridge and Lambda

Published
4 min read
P

As a associate system administrator I worked on Redhat Linux servers, including user management, permissions, services, and performance monitoring Automated routine administrative tasks using Bash scripting and cron jobs, reducing manual effort by ~30% I am aws certified sysops administrator and Google Certified Cloud Engineer. Determined to transition my career into cloud architect /Cloud Support role

In modern microservices, the ability to respond to system changes in real-time without tightly coupling services is a competitive advantage. AWS EventBridge sits at the heart of this architecture, acting as a serverless event bus that simplifies the orchestration of complex workflows.

In this guide, we’ll build a high-scale notification engine for an e-commerce platform. We will demonstrate how to route events to trigger Lambda functions for various channels: Email, SMS, and Push Notifications.

The Core Concept: The "Init-Consumer" Pattern

One of the most effective ways to build a flexible notification system is to split the logic into two distinct phases:

  1. The Initializer (Init): Triggered by a source (like an ERP or Order service). Its job is to enrich the event data, fetch user preferences, and determine which channels (Email, SMS, etc.) should be activated.

  2. The Consumer: Specialized Lambda functions triggered by specific EventBridge rules to handle the actual delivery (e.g., calling Amazon SES for email or Amazon SNS for SMS).

Architectural Workflow

  1. ERP System publishes an Order Processed event to the custom Event Bus.

  2. EventBridge Rule detects the "init" pattern and triggers the Notification-Handler Lambda.

  3. The Handler prepares the data and publishes "consumer-ready" events back to the bus.

  4. Targeted Rules catch these specific payloads and trigger the final delivery functions.

🛠 Provisioning with AWS SAM

Using the AWS Serverless Application Model (SAM), we can define our event bus, rules, and functions as code. This ensures consistency across dev, UAT, and production environments.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Event-Driven Notification Service

Resources:
  # 1. Custom Event Bus for E-commerce events
  EcommerceEventBus:
    Type: AWS::Events::EventBus
    Properties: 
      Name: !Sub "${AWS::StackName}-event-bus"

  # 2. Main Handler: The "Brain" of the system
  NotificationHandlerFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./src
      Handler: handler.init
      Runtime: nodejs18.x
      Policies:
        - EventBridgePutEventsPolicy:
            EventBusName: !Ref EcommerceEventBus

  # 3. Rule for Email Consumer
  EmailNotificationRule:
    Type: AWS::Events::Rule
    Properties:
      EventBusName: !Ref EcommerceEventBus
      EventPattern:
        source: ["NOTIFICATION-HANDLER"]
        detail-type: ["consumer"]
        detail:
          channel: ["email"]
      Targets:
        - Arn: !GetAtt EmailDeliveryFunction.Arn
          Id: "EmailTarget"
          InputTransformer:
            InputPathsMap:
              recipient: "$.detail.body.recipients"
              content: "$.detail.body.emailTemplate"
            InputTemplate: '{"to": <recipient>, "template": <content>}'

💻 Implementation: The Lambda Logic

Phase 1: The Initialization (The "Init" Handler)

This function acts as a dispatcher. It receives raw order data and formats it for downstream consumers.

// src/handler.js
const AWS = require('aws-sdk');
const eventbridge = new AWS.EventBridge();

exports.init = async (event) => {
  console.log("Order Event Received:", JSON.stringify(event));

  const { orderId, userEmail, userPhone } = event.detail;
  const entries = [];

  // Logic to determine channels (e.g., check user preferences in DynamoDB)
  if (userEmail) {
    entries.push({
      Source: 'NOTIFICATION-HANDLER',
      DetailType: 'consumer',
      EventBusName: process.env.EVENT_BUS_NAME,
      Detail: JSON.stringify({
        channel: 'email',
        body: {
          recipients: userEmail,
          emailTemplate: { id: "shipping-update", vars: { orderId } }
        }
      })
    });
  }

  // Publish enriched events back to the bus
  await eventbridge.putEvents({ Entries: entries }).promise();
  return { status: "Enriched events published" };
};

Phase 2: The Consumer (Email Delivery)

Once EventBridge matches the channel: ["email"] pattern, this function executes the final delivery.

// src/delivery.js
exports.emailConsumer = async (event) => {
  // Data arriving here is pre-formatted by the InputTransformer in the SAM template
  const { to, template } = event;

  console.log(`Sending email to ${to} using template ${template.id}`);

  // Integration with Amazon SES or SendGrid would go here
  return { success: true };
};

💡 Advanced Event Pattern Matching

EventBridge allows for powerful filtering. You can create complex rules without writing a single line of code in your Lambda:

  • Prefix Matching: Trigger only for specific order types (e.g., orderId: [{"prefix": "VIP-"}]).

  • Numeric Range: Trigger notifications only for high-value orders (e.g., totalValue: [{ "numeric": [ ">", 1000 ] }]).

Conclusion:

By leveraging AWS EventBridge and Lambda, we’ve built a notification system that is:

  1. Scalable: Can handle thousands of events per second.

  2. Extensible: Adding a new channel (like WhatsApp or Slack) simply requires a new rule and a small consumer function.

  3. Maintainable: Changes to the Email logic don't affect the SMS logic.

This decoupling is essential for any enterprise-grade e-commerce platform looking to improve agility and reduce the "blast radius" of service failures.

Happy Architecting!