State Machines

Add some state machines to process events

Next, we are going to add something to consume the events we are sending to our event bus!

We will use AWS Step Functions to process our wishlist events. We will have one workflow for processing LEGO gift requests and one for processing surprise gift requests.

We will use Express Workflows as these are better suited to event processing than Standard Workflows.

Add state machine for LEGO gifts

Wishlists that include a LEGO gift need to be sent to the LEGO Event-Driven Elves 🧝‍♂️, via another EventBridge event bus. You will need to setup a central event bus to receive these events - this can be done via the AWS Console. And don't forget to append your elf source (e.g. elf-luke) to the event you send so the elves know where the gift request has come from:

import {
  StateMachine,
  StateMachineType,
  TaskInput,
} from "aws-cdk-lib/aws-stepfunctions";

const legoGiftStateMachine = new StateMachine(
  this,
  "EDAWorkshopLegoGiftStateMachine-YOUR_USER_NAME",
  {
    definition: new EventBridgePutEvents(this, "PutLegoGiftEvent", {
      entries: [
        {
          eventBus: EventBus.fromEventBusArn(
            this,
            "EDAWorkshopCentralBus-YOUR_USER_NAME",
            "CENTRAL_EVENT_BUS_ARN"
          ),
          detail: TaskInput.fromObject({
            legoSet: JsonPath.stringAt("$.legoSKU"),
            giftTo: JsonPath.stringAt("$.from"),
          }),
          detailType: "GiftRequested",
          source: "elf-YOUR_NAME",
        },
      ],
    }),
    stateMachineName: "EDAWorkshopLegoGiftStateMachine-YOUR_USER_NAME",
    stateMachineType: StateMachineType.EXPRESS,
  }
);

Bonus: Add a nice message to the elves as part of the event detail, like: "Hey LEGO Event-Driven Elves, {from} would like LEGO set {legoSKU}. Thanks!"

Add state machine for surprise gifts

Wishlists that include a surprise gift can to be sent direct to the workshop, ready for random gift selection, via an S3 bucket. You can ask the Event-Driven Elves for the central bucket ARN too. Again, don't forget to append your elf source to the object!

import { Bucket } from "aws-cdk-lib/aws-s3";
import {
  JsonPath,
  StateMachine,
  StateMachineType,
  TaskInput,
} from "aws-cdk-lib/aws-stepfunctions";
import {
  CallAwsService,
  EventBridgePutEvents,
} from "aws-cdk-lib/aws-stepfunctions-tasks";

const centralBucket = Bucket.fromBucketArn(
  this,
  "EDAWorkshopCentralBucket-YOUR_USER_NAME",
  "CENTRAL_BUCKET_ARN"
);

const surpriseGiftStateMachine = new StateMachine(
  this,
  "EDAWorkshopSurpriseGiftStateMachine-YOUR_USER_NAME",
  {
    definition: new CallAwsService(this, "PutSurpriseGiftObject", {
      action: "PutObject",
      iamAction: "s3:*",
      iamResources: [centralBucket.arnForObjects("*")],
      parameters: {
        Body: JSON.stringify({ giftTo: JsonPath.stringAt("$.from") }),
        Bucket: centralBucket.bucketName,
        Key: JsonPath.stringAt("$.from"),
        Tagging: "source=elf-YOUR_NAME",
      },
      service: "s3",
    }),
    stateMachineName: "EDAWorkshopSurpriseGiftStateMachine-YOUR_USER_NAME",
    stateMachineType: StateMachineType.EXPRESS,
  }
);

surpriseGiftStateMachine.addToRolePolicy(
  new PolicyStatement({
    actions: ["s3:*"],
    resources: [centralBucket.bucketArn],
  })
);

Add event rules

Now we have our Step Functions workflows, we need to add rules to our event bus, per gift type, to execute the state machines based on the gift requested.

import {
  EventField,
  EventBus,
  Rule,
  RuleTargetInput,
} from "aws-cdk-lib/aws-events";
import { SfnStateMachine } from "aws-cdk-lib/aws-events-targets";

new Rule(this, "EDAWorkshopLegoGiftRule-YOUR_USER_NAME", {
  eventBus: eventBus,
  eventPattern: {
    detail: {
      gift: ["lego"],
    },
    detailType: ["WishlistReceived"],
    source: ["workshop.eda"],
  },
  ruleName: "EDAWorkshopLegoGiftRule-YOUR_USER_NAME",
  targets: [
    new SfnStateMachine(legoGiftStateMachine, {
      input: RuleTargetInput.fromObject({
        from: EventField.fromPath("$.detail.from"),
        legoSKU: EventField.fromPath("$.detail.legoSKU"),
      }),
    }),
  ],
});

new Rule(this, "EDAWorkshopSurpriseGiftRule-YOUR_USER_NAME", {
  eventBus: eventBus,
  eventPattern: {
    detail: {
      gift: ["surprise"],
    },
    detailType: ["WishlistReceived"],
    source: ["workshop.eda"],
  },
  ruleName: "EDAWorkshopSurpriseGiftRule-YOUR_USER_NAME",
  targets: [
    new SfnStateMachine(surpriseGiftStateMachine, {
      input: RuleTargetInput.fromObject({
        from: EventField.fromPath("$.detail.from"),
      }),
    }),
  ],
});

Deploy and test one more time - check that both types of gift are processed correctly and end up where they should!

Congratulations - you've saved Christmas (and built a neat event-driven architecture on AWS!) Nice work

Here are a few more bonuses...

Bonus: Enable X-Ray tracing on your state machines

Bonus: Setup the EventBridge websocket stack for monitoring via Postman

Bonus: Select a random gift from available surprises to help Santa out. You'll need to store the list of surprise gifts somewhere.

Bonus: Only allow 3 gifts per person - gifts don't grow on trees!

Last updated