<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Amir BEN YAALA</title>
    <description>The latest articles on DEV Community by Amir BEN YAALA (@amir_benyaala).</description>
    <link>https://dev.to/amir_benyaala</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4090315%2F7b3f00b6-2c8e-4eca-8f2e-3a320489c39a.png</url>
      <title>DEV Community: Amir BEN YAALA</title>
      <link>https://dev.to/amir_benyaala</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amir_benyaala"/>
    <language>en</language>
    <item>
      <title>EventBridge, SQS and Lambda: How to Wire Event-Driven Systems on AWS</title>
      <dc:creator>Amir BEN YAALA</dc:creator>
      <pubDate>Sun, 23 Aug 2026 17:14:44 +0000</pubDate>
      <link>https://dev.to/amir_benyaala/eventbridge-sqs-and-lambda-how-to-wire-event-driven-systems-on-aws-3ie1</link>
      <guid>https://dev.to/amir_benyaala/eventbridge-sqs-and-lambda-how-to-wire-event-driven-systems-on-aws-3ie1</guid>
      <description>&lt;p&gt;Publish domain events to an EventBridge bus, never straight into another service's SQS queue, and never wire the bus directly to Lambda for serious workloads. The bus owns routing: one rule and one queue per consumer. Each queue owns delivery: buffering, retries, backpressure, a dead-letter queue. Here is why each hop earns its place, and when a plain queue is still the right call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should publishers never write directly to SQS?
&lt;/h2&gt;

&lt;p&gt;Because a queue is the private inbox of exactly one consumer. When a producer publishes into it, the producer takes a hard dependency on that consumer's existence, name, and permissions. Fan-out dies: adding a second consumer means changing producer code. And filtering, archive, and replay never existed in the first place.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Coupling.&lt;/strong&gt; The producer must know the queue URL and hold &lt;code&gt;sqs:SendMessage&lt;/code&gt; on it. Rename or split the consumer and the producer redeploys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No fan-out.&lt;/strong&gt; A queue delivers each message to one consumer pool. A second audience means a second publish call in producer code, then a third.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Permission sprawl.&lt;/strong&gt; Every producer-consumer pair adds another IAM edge, and soon nobody can say who reads what without an audit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lost capabilities.&lt;/strong&gt; No content filtering, no archive, no replay, no schema registry. Once a message is consumed, it is gone.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What changes when you publish to an EventBridge bus?
&lt;/h2&gt;

&lt;p&gt;It reverses the dependency. The producer emits a domain event to the bus and stops caring who listens. Rules match on event content, and each consumer gets its own rule, its own SQS queue, and its own Lambda. Adding a consumer touches nothing on the producer's side, and the bus adds archive, replay, and a schema registry.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/what-is-amazon-eventbridge.html" rel="noopener noreferrer"&gt;EventBridge&lt;/a&gt; also gives every target its own dead-letter queue for delivery failures, and the archive lets you replay a time range of events after a bug fix. The producer's contract shrinks to one thing: a stable envelope.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Publish a domain event to the bus (TypeScript, AWS SDK v3)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EventBridgeClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PutEventsCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-eventbridge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;randomUUID&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventBridgeClient&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutEventsCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;EventBusName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;com.shop.orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;DetailType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OrderPlaced&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;          &lt;span class="c1"&gt;// producer-side id: consumers dedup on this&lt;/span&gt;
      &lt;span class="na"&gt;occurredAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ord_1042&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;totalCents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;129900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EUR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two disciplines keep the envelope durable: the producer generates the event id (consumers deduplicate on it, including after a replay), and &lt;code&gt;detail&lt;/code&gt; carries facts, not instructions. The resulting topology: one publisher, one bus, rules fanning out to one queue per consumer, each drained by its own function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftgw173gw3mqjeee812rz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftgw173gw3mqjeee812rz.png" alt="Publisher sends events to EventBridge; rules fan out to one SQS queue per consumer, each drained by its own Lambda" width="800" height="421"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;One event in, any number of consumers out: the bus owns routing, the queues own delivery.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why put SQS between the rule and the Lambda?
&lt;/h2&gt;

&lt;p&gt;Because a direct rule-to-Lambda target gives you no buffer and no control. A queue between them absorbs spikes, lets Lambda poll in batches, retries on a visibility timeout you choose, and dead-letters poison messages after a bounded number of receives. It also gives every consumer an independent throttle and an off switch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backpressure.&lt;/strong&gt; A slow consumer makes messages age in its own queue, visible in the oldest-message-age metric. Nothing is dropped, and no other consumer is affected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Batching.&lt;/strong&gt; The &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html" rel="noopener noreferrer"&gt;SQS event source&lt;/a&gt; delivers records in batches with partial batch responses, far cheaper than one invocation per event.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Per-consumer concurrency.&lt;/strong&gt; &lt;code&gt;maxConcurrency&lt;/code&gt; on the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventsourcemapping.html" rel="noopener noreferrer"&gt;event source mapping&lt;/a&gt; caps one consumer (the floor is 2) without starving the rest of the account, and protects fragile downstream dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real retries.&lt;/strong&gt; Visibility timeout, receive count, and a redrive policy to a DLQ you can inspect and drain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pause and resume.&lt;/strong&gt; Disable the event source mapping during an incident: events keep accumulating safely, then drain when you re-enable it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How do you wire a rule to a queue?
&lt;/h2&gt;

&lt;p&gt;Two pieces of configuration: an event pattern on the rule, and a resource policy on the queue. The pattern filters on envelope fields, so a consumer receives only what it asked for. The policy authorizes events.amazonaws.com to send messages, scoped by source ARN to that one rule. Add a dead-letter queue on the target for delivery failures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;pattern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;billing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;rule:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;large&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;EUR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;reach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;queue&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"com.shop.orders"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"detail-type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"OrderPlaced"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"EUR"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"totalCents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"numeric"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html" rel="noopener noreferrer"&gt;event pattern syntax&lt;/a&gt; covers prefix, numeric, and exists matching, so routing decisions live in configuration rather than in consumer code. The queue side needs one policy, and forgetting it is the classic first-deployment failure: the rule matches, delivery fails, and only the rule's FailedInvocations metric tells you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;policy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;billing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;queue:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;rule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;may&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;write&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;it&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowEventBridgeRule"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"events.amazonaws.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sqs:SendMessage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:sqs:eu-west-3:123456789012:billing-orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ArnEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"aws:SourceArn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:events:eu-west-3:123456789012:rule/orders/billing-large-orders"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When is publishing straight to a queue correct?
&lt;/h2&gt;

&lt;p&gt;When you are sending a command, not announcing a fact. A command targets one known handler and expects it to act: resize this image, send this receipt. Point-to-point work distribution is exactly what SQS was built for. The anti-pattern is not the queue itself; it is broadcasting events through one consumer's private inbox.&lt;/p&gt;

&lt;p&gt;The test we apply: if a second team asking for the same message would be legitimate, it is an event and belongs on the bus. If a second reader would be a bug, it is a command, and a queue is the honest contract. Our guide on &lt;a href="https://www.nodetech-consulting.com/en/blog/when-to-use-sqs" rel="noopener noreferrer"&gt;when to use SQS&lt;/a&gt; covers those point-to-point cases in depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  What trade-offs does the bus impose?
&lt;/h2&gt;

&lt;p&gt;Three honest costs. EventBridge does not guarantee ordering: when sequence matters, target a FIFO queue and set a message group id per aggregate. Delivery is at-least-once end to end, so consumers must deduplicate on the producer's event id. And the extra hop adds latency, typically tens of milliseconds per event.&lt;/p&gt;

&lt;p&gt;None of this is new work. &lt;a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/standard-queues.html" rel="noopener noreferrer"&gt;Standard queues&lt;/a&gt; were already at-least-once, so idempotent handlers were already mandatory; we detailed those handler patterns in &lt;a href="https://www.nodetech-consulting.com/en/blog/event-driven-lambda-sqs" rel="noopener noreferrer"&gt;our article on reliable Lambda and SQS systems&lt;/a&gt;. As for latency: if a user is waiting synchronously on the result, measure before adding hops. For background work, the operational control is worth far more than the milliseconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Direct queue or bus first: the comparison
&lt;/h2&gt;

&lt;p&gt;The table compares the two wirings across the properties that decide production outcomes. Direct publishing wins on nothing except hop count. The bus-first wiring costs one resource policy and a small latency tax, and buys fan-out, filtering, replay, per-consumer retries, and producers that never change when consumers do.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Publisher → SQS direct&lt;/th&gt;
&lt;th&gt;Publisher → EventBridge → SQS → Lambda&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fan-out&lt;/td&gt;
&lt;td&gt;None: one queue, one consumer pool&lt;/td&gt;
&lt;td&gt;Any number of consumers via rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adding a consumer&lt;/td&gt;
&lt;td&gt;Producer code change and redeploy&lt;/td&gt;
&lt;td&gt;New rule and queue; producer untouched&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filtering&lt;/td&gt;
&lt;td&gt;In consumer code, after delivery&lt;/td&gt;
&lt;td&gt;Content-based, in the rule, before delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replay&lt;/td&gt;
&lt;td&gt;None once messages are consumed&lt;/td&gt;
&lt;td&gt;Bus archive, replayable by time range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry semantics&lt;/td&gt;
&lt;td&gt;Visibility timeout on the single queue&lt;/td&gt;
&lt;td&gt;Per consumer: own timeout, receive count, DLQ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backpressure&lt;/td&gt;
&lt;td&gt;Yes, but shared by all message types&lt;/td&gt;
&lt;td&gt;Per consumer, with its own concurrency cap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coupling&lt;/td&gt;
&lt;td&gt;Producer knows the consumer's queue and IAM&lt;/td&gt;
&lt;td&gt;Producer knows only the bus and the envelope&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The wiring checklist
&lt;/h2&gt;

&lt;p&gt;Seven checks before an event pipeline ships. Each traces back to a section above, and together they guarantee the properties that matter: producers ignorant of consumers, consumers that fail independently, and events that can be replayed once the fix is deployed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Producers publish domain events to the bus, never into another service's queue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One rule, one queue, one function per consumer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A DLQ on the EventBridge target and a redrive policy on the queue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A resource policy on each queue, scoped to its rule's ARN.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;maxConcurrency&lt;/code&gt; set per consumer; alarms on queue age and DLQ depth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;FIFO queue and message group id where order matters; idempotent consumers everywhere, deduplicating on the event id.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Commands go point-to-point to a queue; events go to the bus.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.nodetech-consulting.com/en/blog/eventbridge-sqs-lambda-pattern" rel="noopener noreferrer"&gt;NodeTech Consulting engineering blog&lt;/a&gt;. We design, build and operate Node.js and AWS serverless backends.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>eventbridge</category>
      <category>sqs</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
