AWS Serverless and DynamoDb Single Table Design using .Net 6 – Part 2
Introduction
This is a continuation of the previous article AWS Serverless and DynamoDb Single Table Design using .Net 6 – Part 1. In this part we’re going to create a sample Serverless
application using DynamoDb
and deploy that on AWS Lambda
.
Tools
Configure
Configure the AWS Toolkit using the link. While creating the IAM user make sure to attach the below policies
We’ll be using this user for creating the serverless application and deploying the same from Visual Studio
or dotnet tools
command line interface.
Why Serverless
Serverless solutions offer technologies for running code, managing data, and integrating applications, all without managing servers. Serverless technologies feature automatic scaling, built-in high availability, and a pay-for-use billing model to increase agility and optimize costs. These technologies also eliminate infrastructure management tasks like capacity provisioning and patching, so you can focus on writing code that serves your customers. Some of the popular serverless solutions are AWS Lambda
and Azure Functions
.
Development
AWS Toolkit for Visual Studio provides many built-in templates for creating AWS based serverless applications quickly.
Create a new project from Visual Studio and type ‘serverless’ in the search box and select `AWS Serverless Application (.NET Core – C#).
Enter the project name and continue.
Then select the ASP.NET Core Web API
blueprint from the selection and click Finish
Once the project is ready in Visual Studio, you can see a file called serverless.template
. This is AWS CloudFormation Serverless Application Model template file for declaring your Serverless functions and other AWS resources. Make sure to add two policies( AWSLambda_FullAccess
and AmazonDynamoDBFullAccess
) as shown below: These permissions are required for the Lambda
to Read and Write to DynamoDb
Then add the below Nuget
packages:
AWSSDK.DynamoDBv2Newtonsoft.JsonSwashbuckle.AspNetCore.SwaggerGenSwashbuckle.AspNetCore.SwaggerUI
Create an Interface
called IEmployeeDb
to define the methods
public interface IEmployeeDb{ Task<IEnumerable<EmployeeModel>> GetAllReporteesAsync(string empCode); Task<EmployeeModel> GetEmployeeAsync(string empCode); Task SaveAsync(EmployeeModel model); Task SaveBatchAsync(List<EmployeeModel> models);}
Create a class
to implement the IEmployeeDb
interface. The constructor
would look like the below:
public EmployeeDb(ILogger<EmployeeDb> logger, IWebHostEnvironment configuration){ //Comment out the below four line if you're not using the DynamoDb local instance. if (configuration.IsDevelopment()) { _clientConfig.ServiceURL = "http://localhost:8000"; } _client = new AmazonDynamoDBClient(_clientConfig); _context = new DynamoDBContext(_client); _logger = logger;}
We configured the ServiceURL
to point the localhost
in case we’re using DynamoDb
local instance. We also initialized the AmazonDynamoDBClient
and DynamoDBContext
. We’ll be mainly using the Highlevel API
called DynamoDBContext
for reading and writing data from DynamoDb
.
The below methods are responsible for writing/saving the data:
public async Task SaveAsync(EmployeeModel model){ await SaveInDbAsync(GetUserModelForSave(PrepareEmpModel(model))); await SaveInDbAsync(GetReporteeModelForSave(PrepareEmpModel(model)));}private async Task SaveInDbAsync(EmployeeModel model){ await _context.SaveAsync(model); _logger.LogInformation("Saved {} successfully!", model.EmployeeCode);}private EmployeeModel PrepareEmpModel(EmployeeModel model){ model.EmployeeCode = model.EmployeeCode?.ToUpper(); model.ReportingManagerCode = model.ReportingManagerCode?.ToUpper(); return model;}
When saving a record, this method will actually insert two objects, one for user
type and the other for reportee
type. We discussed the reason and logic for creating two entries in the previous part.
In the below method we implemented the logic for fetching the employee by EmployeeCode:
public async Task<EmployeeModel> GetEmployeeAsync(string empCode){ var result = await _context.LoadAsync<EmployeeModel>(empCode.ToUpper(), empCode.ToUpper()); if (result != null) result.ReportingManagerCode = ""; //ReportingManagerCode was same as EmployeeCode, so just remove it return result;}
Next method will cover the logic for fetching the reportees by EmployeeCode:
public async Task<IEnumerable<EmployeeModel>> GetAllReporteesAsync(string empCode){ var config = new DynamoDBOperationConfig { QueryFilter = new List<ScanCondition> { new ScanCondition("Type", ScanOperator.Equal, "Reportee"), new ScanCondition("LastWorkingDate", ScanOperator.IsNull) } }; var result = await _context.QueryAsync<EmployeeModel>(empCode.ToUpper(), config).GetRemainingAsync(); return PrepareReporteeReturnModel(result); //swap the EmployeeCode and ReportingManagerCode and return}
All the other code fragments and complete solution can be downloaded from the GitHub repository.
Once you complete the development you need to create a DynamoDb table in your AWS account. There are many ways to create a service in AWS. You can use CLI
, Console
, SDK
or even Visual Studio Toolkit
. Below is the CLI
command for creating the table and setting up the pk
and sk
.
aws dynamodb create-table --table-name employees --attribute-definitions AttributeName=EmployeeCode,AttributeType=S AttributeName=ReportingManagerCode,AttributeType=S --key-schema AttributeName=EmployeeCode,KeyType=HASH AttributeName=ReportingManagerCode,KeyType=RANGE --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 --table-class STANDARD
Now you can deploy the serverless application either using Visual Studio or dotnet tools. To deploy using Visual Studio, right click on the project and select the Publish to AWS Lambda
button.
To deploy using dotnet tools
you need to follow the below steps in the command line.
dotnet tool install -g Amazon.Lambda.Toolscd "AWSServerlessDynamoDb/AWSServerlessDynamoDb" #or whatever the folderdotnet lambda deploy-serverless
After successful deployment, you will get a Lambda endpoint(ApiURL) as below:
You can access your SwaggerUI
by adding /swagger
in the above url and you can test the APIs.
Complete source code can be found here.
Happy coding!!