Serverless isn’t just “no servers to manage” — it’s event-driven + async orchestration. This is the production combination of AWS Lambda + Step Functions + EventBridge + SQS/SNS, plus Saga compensation, event sourcing, cold start optimization, PlantUML sequence diagrams for async chains.
@startuml title "Lambda Function Internal Architecture"
actor "Caller" as C participant "Lambda Runtime" as Runtime participant "Handler Function" as Handler participant "Init Code" as Init database "DynamoDB" as DB participant "External API" as API participant "CloudWatch Logs" as Logs
C -> Runtime : ① invoke {payload}
alt Cold Start Runtime -> Init : ② initialize (VPC ENI, SDK clients, ORM) Init -> DB : ③ create connection pool Init -> Logs : ④ log init duration end
actor "Client" as C participant "API Gateway" as GW participant "Lambda" as Fn database "DynamoDB" as DB
C -> GW : ① GET /api/users/123 GW -> GW : ② verify JWT / API Key GW -> Fn : ③ invoke (sync) Fn -> DB : ④ GetItem DB --> Fn : ⑤ user record Fn --> GW : ⑥ 200 {user: {...}} GW --> C : ⑦ 200
note right of Fn Timeout limits: - Lambda: 15 min - API Gateway: 29 sec - ALB: same end note
@enduml
API Gateway timeout is shorter than Lambda — long tasks use async.
actor "Client" as C participant "API Gateway" as GW participant "Lambda (Producer)" as Producer queue "SQS Queue" as Q participant "Lambda (Consumer)" as Consumer database "DynamoDB" as DB
C -> GW : ① POST /api/orders {items: [...]} GW -> Producer : ② invoke Producer -> Producer : ③ validate request Producer -> Q : ④ SendMessage {order_id, payload} Producer --> GW : ⑤ 202 Accepted {order_id} GW --> C : ⑥ 202 Accepted
note over Q : SQS persists, Lambda failure auto retries
participant "Order Service" as Order participant "EventBridge Bus" as EB participant "Payment Lambda" as Pay participant "Inventory Lambda" as Inv participant "Email Lambda" as Email participant "Analytics Lambda" as Analytics
Order -> EB : ① PutEvents {OrderPlaced, order_id, items}
EB -> EB : ② route matching rule
par parallel triggers EB -> Pay : ③ OrderPlaced → process payment EB -> Inv : ④ OrderPlaced → reserve inventory EB -> Email : ⑤ OrderPlaced → send confirmation EB -> Analytics : ⑥ OrderPlaced → record analytics end
note right of EB EventBridge rule: - source = "com.orders" - detail-type = "OrderPlaced" - filter (e.g. region) - multiple targets parallel end note
@enduml
EventBridge benefits:
Decoupled — sender doesn’t know who’s subscribed
Multi-target parallel — one event triggers multiple downstreams
Cross-account — events route across AWS accounts
SaaS integration — direct to Datadog / Zendesk / PagerDuty
state "OrderReceived" as S1 state "ValidateOrder" as S2 state "ChargePayment" as S3 state "ReserveInventory" as S4 state "ShipOrder" as S5 state "NotifyCustomer" as S6 state "OrderCompleted" as S7 state "OrderFailed" as SF state "RefundPayment" as SR
@startuml title "Saga Pattern - Distributed Transaction with Compensation"
actor "User" as U participant "Order Service" as Order participant "Payment Service" as Payment participant "Inventory Service" as Inventory participant "Shipping Service" as Shipping
U -> Order : ① place order Order -> Order : ② create order (status=pending) Order -> Payment : ③ charge Payment --> Order : ④ success / fail
alt Payment success Order -> Inventory : ⑤ reserve stock Inventory --> Order : ⑥ success / fail alt Inventory success Order -> Shipping : ⑦ create shipment Shipping --> Order : ⑧ success / fail alt Shipping success Order -> Order : ⑨ status=confirmed else Shipping failure Order -> Inventory : ⑩ compensate: release stock Order -> Payment : ⑪ compensate: refund Order -> Order : ⑫ status=cancelled end else Inventory failure Order -> Payment : ⑬ compensate: refund Order -> Order : ⑭ status=cancelled end
else Payment failure Order -> Order : ⑮ status=cancelled end
@enduml
Two Saga implementations:
Orchestration — central coordinator (Step Functions) calls each service
Choreography — each service listens to events, decides next step
actor "User" as U participant "Command Handler" as Cmd participant "Aggregate" as Agg queue "Event Store\n(DynamoDB Stream / Kinesis)" as Store participant "Projection" as Proj database "Read Model\n(DynamoDB)" as Read participant "Query Handler" as Q
U -> Cmd : ① PlaceOrder {items} Cmd -> Agg : ② load aggregate Agg -> Agg : ③ business rule validation Agg -> Store : ④ append OrderPlaced {order_id, ...} Store -> Proj : ⑤ stream event Proj -> Read : ⑥ update read model
U -> Q : ⑦ GET /orders/{id} Q -> Read : ⑧ query Read --> Q : ⑨ order info
note right of Store Event Store features: - immutable (append-only) - time-ordered - replayable end note
database "DynamoDB Table" as Table participant "DynamoDB Stream" as Stream participant "Lambda (CDC)" as Lambda participant "OpenSearch" as ES participant "S3 (Data Lake)" as S3
Table -> Stream : ① INSERT/UPDATE/DELETE auto-captured Stream -> Lambda : ② trigger (new image, old image) Lambda -> ES : ③ index update Lambda -> S3 : ④ write data lake Parquet Lambda -> Stream : ⑤ checkpoint (processed)
note right of Stream DynamoDB Streams: - 24 hour retention - ordered by partition key - trigger batch size configurable end note
@startuml title "Lambda Distributed Trace (X-Ray)"
actor "User" as U participant "API Gateway" as GW participant "Lambda: OrderAPI" as L1 queue "SQS" as Q participant "Lambda: OrderProcessor" as L2 participant "Lambda: PaymentProcessor" as L3 database "DynamoDB" as DB
U -> GW : ① GET /api/orders GW -> L1 : ② invoke L1 -> DB : ③ Query L1 -> Q : ④ SendMessage (async) L1 --> GW : ⑤ 200
note over GW X-Ray Service Map: API Gateway → OrderAPI → SQS → OrderProcessor → PaymentProcessor → DynamoDB Each segment shows latency proportion Find the slowest link end note
@enduml
X-Ray / CloudWatch ServiceLens auto-draw this trace, PlantUML for documentation.
Field foot-guns
Lambda concurrency limit — account default 1000, throttling under burst. Request quota increase or SQS buffering.
Lambda timeout — sync API Gateway 29s, async max 15min. Long tasks use Step Functions.
Step Functions state machine too large — over 200 states slow compile. Split into multiple state machines, nested execution.
EventBridge rule quota — 300 rules per bus default. Cross-bus routing or merge rules.
DLQ not configured — failed messages retry infinitely. Every SQS / EventBridge target has a DLQ.
DynamoDB Stream 24h retention — long lag data lost. Use Kinesis Data Stream instead.