├── destroy.sh
├── .gitignore
├── lambda
├── src
│ ├── main
│ │ └── java
│ │ │ └── airhacks
│ │ │ ├── lambda
│ │ │ └── example
│ │ │ │ ├── entity
│ │ │ │ └── ExampleEvent.java
│ │ │ │ ├── control
│ │ │ │ └── EventProcessor.java
│ │ │ │ └── boundary
│ │ │ │ └── EventListener.java
│ │ │ └── logging
│ │ │ └── control
│ │ │ └── Log.java
│ └── test
│ │ └── java
│ │ └── airhacks
│ │ └── lambda
│ │ └── example
│ │ └── boundary
│ │ ├── EventListenerTest.java
│ │ └── InvokeEventListenerIT.java
└── pom.xml
├── cdk
├── cdk.json
├── src
│ ├── main
│ │ └── java
│ │ │ └── airhacks
│ │ │ ├── CDKApp.java
│ │ │ └── LambdaStack.java
│ └── test
│ │ └── java
│ │ └── airhacks
│ │ └── CDKAppTest.java
├── README.md
└── pom.xml
├── LICENSE
└── README.md
/destroy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cd cdk && cdk destroy
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | pom.xml.tag
3 | pom.xml.releaseBackup
4 | pom.xml.versionsBackup
5 | pom.xml.next
6 | release.properties
7 | dependency-reduced-pom.xml
8 | buildNumber.properties
9 | .mvn/timing.properties
10 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar
11 | .mvn/wrapper/maven-wrapper.jar
12 |
--------------------------------------------------------------------------------
/lambda/src/main/java/airhacks/lambda/example/entity/ExampleEvent.java:
--------------------------------------------------------------------------------
1 | package airhacks.lambda.example.entity;
2 |
3 | /**
4 | * Domain-specific event representation extracted from AWS service events.
5 | * Rename this record to match your domain context, e.g., OrderEvent, PaymentEvent, BuildStatusEvent
6 | */
7 | public record ExampleEvent(String payload) {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/cdk/cdk.json:
--------------------------------------------------------------------------------
1 | {
2 | "app": "mvn -e -q compile exec:java",
3 | "watch":{
4 | "include":[
5 | "../target/function.jar"
6 | ]
7 | },
8 | "context": {
9 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
10 | "@aws-cdk/core:stackRelativeExports": true,
11 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
12 | "@aws-cdk/aws-lambda:recognizeVersionProps": true,
13 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true
14 | }
15 | }
--------------------------------------------------------------------------------
/cdk/src/main/java/airhacks/CDKApp.java:
--------------------------------------------------------------------------------
1 | package airhacks;
2 |
3 | import java.util.Map;
4 |
5 | import software.amazon.awscdk.App;
6 | import software.amazon.awscdk.Tags;
7 |
8 | public interface CDKApp {
9 |
10 | static void main(String... args) {
11 | var app = new App();
12 | var appName = "aws-lambda-cdk-plain";
13 | Tags.of(app).add("project", "airhacks.live");
14 | Tags.of(app).add("environment", "workshops");
15 | Tags.of(app).add("application", appName);
16 |
17 | new LambdaStack.Builder(app, appName)
18 | .functionHandler("airhacks.lambda.example.boundary.EventListener::onEvent")
19 | .functionName("airhacks_EventListener")
20 | .configuration(Map.of("message", "hello,duke"))
21 | .build();
22 | app.synth();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lambda/src/main/java/airhacks/lambda/example/control/EventProcessor.java:
--------------------------------------------------------------------------------
1 | package airhacks.lambda.example.control;
2 |
3 | import airhacks.lambda.example.entity.ExampleEvent;
4 | import airhacks.logging.control.Log;
5 |
6 | /**
7 | * Processes domain events extracted from AWS service events.
8 | * Implements business logic for event handling.
9 | */
10 | public interface EventProcessor {
11 |
12 | /**
13 | * Processes the domain event with business logic.
14 | * Replace with actual processing logic for your domain.
15 | *
16 | * @param event domain event to process
17 | */
18 | static void process(ExampleEvent event) {
19 | Log.info("Processing domain event with payload: %s", event.payload());
20 |
21 | // Implement actual business logic here:
22 | // - Validate event data
23 | // - Transform data
24 | // - Call downstream services
25 | // - ...
26 | Log.info("Domain event processed successfully");
27 | }
28 | }
--------------------------------------------------------------------------------
/lambda/src/test/java/airhacks/lambda/example/boundary/EventListenerTest.java:
--------------------------------------------------------------------------------
1 | package airhacks.lambda.example.boundary;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | import airhacks.lambda.example.entity.ExampleEvent;
8 |
9 | /**
10 | * Tests event extraction logic.
11 | * Real-world implementations should test with actual AWS event structures
12 | * from EventBridge, SNS, SQS, CodeBuild, etc. to ensure proper parsing and
13 | * transformation.
14 | */
15 | public class EventListenerTest {
16 |
17 | @Test
18 | void awsToDomainEventConversion() {
19 | var awsEvent = """
20 | {
21 | "type":"aws",
22 | "payload:"test"
23 | }
24 | """;
25 | var actualEvent = EventListener.extract(awsEvent);
26 | var expectedEvent = new ExampleEvent(awsEvent);
27 | assertThat(actualEvent).isEqualTo(expectedEvent);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Adam Bien
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/cdk/README.md:
--------------------------------------------------------------------------------
1 | # Slightly Streamlined AWS Cloud Development Kit (CDK) Boilerplate
2 |
3 | This module is based on [aws-cdk-plain](https://github.com/AdamBien/aws-cdk-plain)
4 | which fully relies on [AWS CDK for Java](https://docs.aws.amazon.com/cdk/latest/guide/work-with-cdk-java.html).
5 |
6 | ## Installation
7 |
8 | 1. Install [AWS CDK CLI](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html)
9 | 2. [`cdk boostrap --profile YOUR_AWS_PROFILE`](https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html)
10 |
11 | ## Useful commands
12 |
13 | * `mvn package` compile and run tests
14 | * `cdk ls` list all stacks in the app
15 | * `cdk synth` emits the synthesized CloudFormation template
16 | * `cdk deploy` deploy this stack to your default AWS account/region
17 | * `cdk diff` compare deployed stack with current state
18 | * `cdk docs` open CDK documentation
19 |
20 | Enjoy!
21 |
22 | ## in action
23 |
24 | [](https://www.youtube.com/embed/eTG7EV1ThqQ?rel=0)
25 |
26 |
27 |
28 | See you at: [airhacks.live](https://airhacks.live)
--------------------------------------------------------------------------------
/lambda/src/main/java/airhacks/logging/control/Log.java:
--------------------------------------------------------------------------------
1 | package airhacks.logging.control;
2 |
3 | /**
4 | * Centralized logging for AWS Lambda functions.
5 | * All output is automatically captured and redirected to CloudWatch Logs by the Lambda runtime.
6 | * Log streams are created per Lambda execution environment, with logs retained based on CloudWatch retention policy.
7 | */
8 | public interface Log {
9 |
10 | static void info(String message) {
11 | System.out.println("[INFO] " + message);
12 | }
13 |
14 | static void info(String message, Object... args) {
15 | System.out.println("[INFO] " + message.formatted(args));
16 | }
17 |
18 | static void error(String message) {
19 | System.out.println("[ERROR] " + message);
20 | }
21 |
22 | static void error(String message, Throwable throwable) {
23 | System.out.println("[ERROR] " + message);
24 | throwable.printStackTrace(System.out);
25 | }
26 |
27 | static void debug(String message) {
28 | System.out.println("[DEBUG] " + message);
29 | }
30 |
31 | static void debug(String message, Object... args) {
32 | System.out.println("[DEBUG] " + message.formatted(args));
33 | }
34 | }
--------------------------------------------------------------------------------
/cdk/src/test/java/airhacks/CDKAppTest.java:
--------------------------------------------------------------------------------
1 | package airhacks;
2 |
3 | import static org.assertj.core.api.Assertions.assertThat;
4 |
5 | import java.io.IOException;
6 | import java.util.Map;
7 |
8 | import com.fasterxml.jackson.databind.ObjectMapper;
9 | import com.fasterxml.jackson.databind.SerializationFeature;
10 |
11 | import org.junit.jupiter.api.Test;
12 |
13 | import software.amazon.awscdk.App;
14 |
15 | public class CDKAppTest {
16 | private final static ObjectMapper JSON =
17 | new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true);
18 |
19 | @Test
20 | public void testStack() throws IOException {
21 | App app = new App();
22 | var stack = new LambdaStack.Builder(app, "test")
23 | .functionHandler("airhacks.lambda.greetings.boundary.Greetings::onEvent")
24 | .functionName("airhacks_POJOGreetings")
25 | .configuration(Map.of("message", "hello,duke"))
26 | .build();
27 |
28 | // synthesize the stack to a CloudFormation template
29 | var actual = JSON.valueToTree(app.synth().getStackArtifact(stack.getArtifactId()).getTemplate());
30 |
31 | // Update once resources have been added to the stack
32 | assertThat(actual.get("Resources")).isNotEmpty();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lambda/src/main/java/airhacks/lambda/example/boundary/EventListener.java:
--------------------------------------------------------------------------------
1 | package airhacks.lambda.example.boundary;
2 |
3 | import airhacks.lambda.example.control.EventProcessor;
4 | import airhacks.lambda.example.entity.ExampleEvent;
5 | import airhacks.logging.control.Log;
6 |
7 | /**
8 | * Example Lambda handler for processing AWS events from EventBridge, SNS, SQS, CodeBuild or other AWS services.
9 | * Demonstrates event-driven processing pattern for AWS Lambda functions.
10 | *
11 | * Rename this class to match the actual event type, e.g., OrderEventListener, PaymentEventListener, BuildEventListener
12 | */
13 | public class EventListener {
14 |
15 | static String message = System.getenv("message");
16 |
17 | public EventListener() {
18 | Log.info("initialized with configuration: %s", message);
19 | }
20 |
21 | /**
22 | * Lambda function entry point - handles incoming AWS service events.
23 | * This method is invoked by the Lambda runtime for each event trigger.
24 | *
25 | * @param awsEvent AWS service event (EventBridge, SNS, SQS, etc.)
26 | */
27 | public void onEvent(Object awsEvent) {
28 | Log.info("event received: %s", awsEvent);
29 | var domainEvent = extract(awsEvent);
30 | Log.info("event converted: %s", domainEvent);
31 | EventProcessor.process(domainEvent);
32 | Log.info("event processed: %s", domainEvent);
33 | }
34 |
35 | /**
36 | * Transforms AWS service event into domain-specific representation.
37 | * Implement actual parsing logic based on the specific AWS service event structure
38 | * (e.g., EventBridge detail, SNS Message, SQS body, CodeBuild state change)
39 | *
40 | * @param event AWS event
41 | * @return domain specific payload
42 | */
43 | static ExampleEvent extract(Object event){
44 | //placeholder for conversion
45 | return new ExampleEvent(event.toString());
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/lambda/src/test/java/airhacks/lambda/example/boundary/InvokeEventListenerIT.java:
--------------------------------------------------------------------------------
1 | package airhacks.lambda.example.boundary;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertNull;
4 |
5 | import org.junit.jupiter.api.BeforeEach;
6 | import org.junit.jupiter.api.Test;
7 |
8 | import airhacks.logging.control.Log;
9 | import software.amazon.awssdk.services.lambda.LambdaClient;
10 | import software.amazon.awssdk.services.lambda.model.InvocationType;
11 | import software.amazon.awssdk.services.lambda.model.InvokeRequest;
12 | import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
13 | import software.amazon.awssdk.core.SdkBytes;
14 |
15 | /**
16 | * Integration test for Lambda function invocation.
17 | * Requires AWS credentials configured via environment variables, AWS CLI configuration,
18 | * or IAM instance role. The Lambda function must be deployed before running this test.
19 | */
20 | public class InvokeEventListenerIT {
21 | String functionName = "airhacks_EventListener";
22 | LambdaClient client;
23 |
24 | @BeforeEach
25 | public void initClient() {
26 | var credentials = DefaultCredentialsProvider.builder().build();
27 | this.client = LambdaClient.builder()
28 | .credentialsProvider(credentials)
29 | .build();
30 | }
31 |
32 | @Test
33 | public void invokeLambdaAsynchronously() {
34 | var json = """
35 | {
36 | "user":"duke"
37 | }
38 | """;
39 | var payload = SdkBytes.fromUtf8String(json);
40 |
41 | var request = InvokeRequest.builder()
42 | .functionName(functionName)
43 | .payload(payload)
44 | .invocationType(InvocationType.REQUEST_RESPONSE)
45 | .build();
46 |
47 | var response = this.client.invoke(request);
48 | var error = response.functionError();
49 | assertNull(error);
50 | var value = response.payload().asUtf8String();
51 | Log.info("Function executed. Response: " + value);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/cdk/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | airhacks
8 | aws-lambda-cdk-plain
9 | 0.1
10 |
11 |
12 |
13 | org.codehaus.mojo
14 | exec-maven-plugin
15 | 3.1.0
16 |
17 | airhacks.CDKApp
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | software.amazon.awscdk
27 | aws-cdk-lib
28 | 2.232.1
29 |
30 |
31 | software.constructs
32 | constructs
33 | 3.4.344
34 |
35 |
36 | org.junit.jupiter
37 | junit-jupiter-api
38 | 5.10.2
39 | test
40 |
41 |
42 | org.junit.jupiter
43 | junit-jupiter-engine
44 | 5.10.2
45 | test
46 |
47 |
48 | org.assertj
49 | assertj-core
50 | 3.25.3
51 | test
52 |
53 |
54 |
55 | UTF-8
56 | 25
57 | 25
58 | 25
59 |
60 |
61 |
--------------------------------------------------------------------------------
/lambda/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | airhacks
6 | aws-lambda-cdk-plain
7 | 0.0.1-SNAPSHOT
8 | jar
9 |
10 |
11 |
12 | maven-shade-plugin
13 | 3.5.1
14 |
15 |
16 | package
17 |
18 | shade
19 |
20 |
21 |
22 |
23 | function
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | software.amazon.awssdk
32 | bom
33 | 2.30.36
34 | pom
35 | import
36 |
37 |
38 |
39 |
40 |
47 |
48 | com.amazonaws
49 | aws-lambda-java-events
50 | 3.16.1
51 |
52 |
53 |
54 | software.amazon.awssdk
55 | lambda
56 | test
57 |
58 |
59 | org.junit.jupiter
60 | junit-jupiter
61 | 5.13.4
62 | test
63 |
64 |
65 | org.assertj
66 | assertj-core
67 | 3.27.3
68 | test
69 |
70 |
71 |
72 | UTF-8
73 | 25
74 | 25
75 | 25
76 |
77 |
--------------------------------------------------------------------------------
/cdk/src/main/java/airhacks/LambdaStack.java:
--------------------------------------------------------------------------------
1 | package airhacks;
2 |
3 | import java.util.Map;
4 | import java.util.Objects;
5 |
6 | import software.amazon.awscdk.CfnOutput;
7 | import software.amazon.awscdk.Duration;
8 | import software.amazon.awscdk.Stack;
9 | import software.amazon.awscdk.services.lambda.Architecture;
10 | import software.amazon.awscdk.services.lambda.Code;
11 | import software.amazon.awscdk.services.lambda.Function;
12 | import software.amazon.awscdk.services.lambda.Runtime;
13 | import software.amazon.awscdk.services.lambda.Tracing;
14 | import software.constructs.Construct;
15 |
16 | public class LambdaStack extends Stack {
17 |
18 | public static class Builder {
19 |
20 | private Construct construct;
21 | private String stackId;
22 | private String functionName;
23 | private String functionHandler;
24 | private Map configuration = Map.of();
25 | private final int ONE_CPU = 1700;
26 | private int ram = ONE_CPU;
27 |
28 | public Builder(Construct construct, String stackNamePrefix) {
29 | this.construct = construct;
30 | this.stackId = stackNamePrefix.toLowerCase() + "-stack";
31 | }
32 |
33 | public Builder functionName(String functionName) {
34 | this.functionName = functionName;
35 | return this;
36 | }
37 |
38 | public Builder functionHandler(String handler) {
39 | this.functionHandler = handler;
40 | return this;
41 | }
42 |
43 | public Builder ram(int ram) {
44 | this.ram = ram;
45 | return this;
46 | }
47 |
48 | public Builder withOneCPU() {
49 | this.ram = ONE_CPU;
50 | return this;
51 | }
52 |
53 | public Builder withHalfCPU() {
54 | this.ram = ONE_CPU / 2;
55 | return this;
56 | }
57 |
58 | public Builder withTwoCPUs() {
59 | this.ram = ONE_CPU * 2;
60 | return this;
61 | }
62 |
63 | public Builder configuration(Map configuration) {
64 | this.configuration = configuration;
65 | return this;
66 | }
67 |
68 | public LambdaStack build() {
69 | Objects.requireNonNull(this.functionName, "Function name is required");
70 | Objects.requireNonNull(this.functionHandler, "Function handler (fqn::methodName) is required");
71 | return new LambdaStack(this);
72 | }
73 |
74 | }
75 |
76 |
77 | public LambdaStack(Builder builder) {
78 | super(builder.construct, builder.stackId);
79 | var timeout = 10;
80 |
81 | var function = createFunction(builder.functionName, builder.functionHandler, builder.configuration, builder.ram, timeout);
82 | CfnOutput.Builder.create(this, "FunctionARN").value(function.getFunctionArn()).build();
83 | }
84 |
85 |
86 | Function createFunction(String functionName,String functionHandler, Map configuration, int memory, int timeout) {
87 | return Function.Builder.create(this, functionName)
88 | .runtime(Runtime.JAVA_21)
89 | .architecture(Architecture.ARM_64)
90 | .code(Code.fromAsset("../lambda/target/function.jar"))
91 | .handler(functionHandler)
92 | .memorySize(memory)
93 | .functionName(functionName)
94 | .environment(configuration)
95 | .timeout(Duration.seconds(timeout))
96 | .tracing(Tracing.ACTIVE)
97 | .build();
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Simplest Possible, BCE structured, AWS Lambda Function with Cloud Development Kit (CDK) Boilerplate
2 |
3 | A lean starting point for building, testing and deploying AWS Lambdas with Java. This project serves as a reference implementation for serverless [BCE (Boundary-Control-Entity)](https://bce.design) architecture.
4 |
5 | # TL;DR
6 |
7 | A simple Java AWS Lambda without any AWS dependencies:
8 |
9 | ```java
10 | package airhacks.lambda.example.boundary;
11 |
12 | public class EventListener {
13 |
14 | static String message = System.getenv("message");
15 |
16 | public void onEvent(Object awsEvent) {
17 | Log.info("event received: %s", awsEvent);
18 | var domainEvent = extract(awsEvent);
19 | EventProcessor.process(domainEvent);
20 | }
21 |
22 | static ExampleEvent extract(Object event) {
23 | return new ExampleEvent(event.toString());
24 | }
25 | }
26 | ```
27 |
28 | ...deployed with AWS Cloud Development Kit:
29 |
30 |
31 | ```java
32 | import software.amazon.awscdk.services.lambda.*;
33 |
34 | Function createFunction(String functionName, String functionHandler, Map configuration, int memory, int timeout) {
35 | return Function.Builder.create(this, functionName)
36 | .runtime(Runtime.JAVA_21)
37 | .architecture(Architecture.ARM_64)
38 | .code(Code.fromAsset("../lambda/target/function.jar"))
39 | .handler(functionHandler)
40 | .memorySize(memory)
41 | .functionName(functionName)
42 | .environment(configuration)
43 | .timeout(Duration.seconds(timeout))
44 | .tracing(Tracing.ACTIVE)
45 | .build();
46 | }
47 | ```
48 |
49 | ...provisioned with maven and cdk:
50 |
51 | ```bash
52 | cd lambda && mvn clean package
53 | cd ../cdk && mvn clean package && cdk deploy
54 | ```
55 |
56 | ...and (blackbox) tested with [AWS SDK for Java 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide):
57 |
58 | ```java
59 | @BeforeEach
60 | public void initClient() {
61 | var credentials = DefaultCredentialsProvider.builder().build();
62 | this.client = LambdaClient.builder()
63 | .credentialsProvider(credentials)
64 | .build();
65 | }
66 |
67 | @Test
68 | public void invokeLambdaAsynchronously() {
69 | var json = """
70 | {
71 | "user":"duke"
72 | }
73 | """;
74 | var payload = SdkBytes.fromUtf8String(json);
75 |
76 | var request = InvokeRequest.builder()
77 | .functionName("airhacks_EventListener")
78 | .payload(payload)
79 | .invocationType(InvocationType.REQUEST_RESPONSE)
80 | .build();
81 |
82 | var response = this.client.invoke(request);
83 | var error = response.functionError();
84 | assertNull(error);
85 | var value = response.payload().asUtf8String();
86 | Log.info("Function executed. Response: " + value);
87 | }
88 | ```
89 |
90 | ## In Action
91 |
92 | [](https://www.youtube.com/embed/rHq514-1aHM?rel=0)
93 |
94 | ## Java "vs." JavaScript
95 |
96 | Cold and "warm" starts of JavaScript and Java Lambdas:
97 |
98 | [](https://www.youtube.com/embed/28Da0l0MFms?rel=0)
99 |
100 | ## AWS Lambda on Java: How Good / Bad Is The Cold Start?
101 |
102 | [](https://www.youtube.com/embed/EXSZ5TFgUKU?rel=0)
103 |
104 | ## Lambda Configuration
105 |
106 | [](https://www.youtube.com/embed/Z3Ir-AQEsKk?rel=0)
107 |
108 | ## References
109 |
110 | The deployment is borrowed from: ["Slightly Streamlined AWS Cloud Development Kit (CDK) Boilerplate"](https://github.com/AdamBien/aws-cdk-plain)
111 |
--------------------------------------------------------------------------------