[Serverless] Refactoring: Using Ports and Adapters pattern to refactor code
The original code:
createGroup.ts:
import { v4 as uuidv4 } from "uuid";
import "source-map-support/register";
import * as AWS from "aws-sdk";
import {
APIGatewayProxyEvent,
APIGatewayProxyHandler,
APIGatewayProxyResult,
} from "aws-lambda";
import { getUserId } from "../../auth/utils";
const docClient = new AWS.DynamoDB.DocumentClient();
const groupTables = process.env.GROUPS_TABLE;
export const handler: APIGatewayProxyHandler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
console.log("Processing event: ", event);
const itemId = uuidv4();
const parsedBody = JSON.parse(event.body);
const authorization = event.headers.Authorization;
const splits = authorization.split(" ");
const jwtToken = splits[1];
const newItem = {
id: itemId,
userId: getUserId(jwtToken),
...parsedBody,
};
await docClient
.put({
TableName: groupTables,
Item: newItem,
})
.promise();
return {
statusCode: 201,
headers: {
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({
newItem,
}),
};
};
getGroups.ts:
import * as AWS from "aws-sdk";
import {
APIGatewayProxyEvent,
APIGatewayProxyHandler,
APIGatewayProxyResult,
} from "aws-lambda";
const docClient = new AWS.DynamoDB.DocumentClient();
const groupTables = process.env.GROUPS_TABLE;
export const handler: APIGatewayProxyHandler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
console.log("Processing event: ", event);
const result = await docClient
.scan({
TableName: groupTables,
})
.promise();
const items = result.Items;
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({
items,
}),
};
};
Tow entry functions both contains busniess logic code and data access code (dynamoDB).
What we want is:
Entry function --> Busniess logic code --> Data access layer
First, create interfaces for both Reqest & Model:
requests/CreateGroupRequest.ts:
export interface CreateGroupRequest { name: string; description: string; }
models/Group.ts:
export interface Group { id: string; name: string; description: string; userId: string; }
Then create data access layer code, it mainly handle all the dynamoDB operations.
dataAccess/groupAccess.ts:
import * as AWS from "aws-sdk"; import { DocumentClient } from "aws-sdk/clients/dynamodb"; import {Group} from '../models/Group' export class GroupAccess { constructor( private readonly docClient: DocumentClient = new AWS.DynamoDB.DocumentClient(), private readonly groupsTable = process.env.GROUPS_TABLE ) { } async getAllGroups(): Promise<Group[]> { console.log('Getting all groups'); const result = await this.docClient.scan({ TableName: this.groupsTable, }).promise() const items = result.Items; return items as Group[]; } async createGroup(group: Group): Promise<Group> { console.log('Creating a group with id', group.id) await this.docClient.put({ TableName: this.groupsTable, Item: group }).promise(); return group; } }
Then create busniessLogic layer, it mainly talk to data access layer and passing and returning the data.
busniessLogic/groups.ts:
import * as uuid from "uuid"; import { Group } from "../models/Group"; import { GroupAccess } from "../dataAccess/groupAccess"; import { CreateGroupRequest } from "../requests/CreateGroupRequest"; import { getUserId } from "../auth/utils"; const groupAccess = new GroupAccess(); export async function getAllGroups(): Promise<Group[]> { return groupAccess.getAllGroups(); } export async function createGroup( createGroupRequest: CreateGroupRequest, jwtToken: string ) { const itemId = uuid.v4(); const userId = getUserId(jwtToken); return await groupAccess.createGroup({ id: itemId, userId: userId, name: createGroupRequest.name, description: createGroupRequest.description, }); }
Lastly, entry function will just talk to busniess layer code:
createGroup.ts:
import "source-map-support/register"; import { APIGatewayProxyEvent, APIGatewayProxyHandler, APIGatewayProxyResult, } from "aws-lambda"; import { createGroup } from "../../busniessLogic/groups"; import { CreateGroupRequest } from "../../requests/CreateGroupRequest"; export const handler: APIGatewayProxyHandler = async ( event: APIGatewayProxyEvent ): Promise<APIGatewayProxyResult> => { console.log("Processing event: ", event); const newGroup: CreateGroupRequest = JSON.parse(event.body); const authorization = event.headers.Authorization; const splits = authorization.split(" "); const jwtToken = splits[1]; const newItem = await createGroup(newGroup, jwtToken); return { statusCode: 201, headers: { "Access-Control-Allow-Origin": "*", }, body: JSON.stringify({ newItem, }), }; };
getGroups.ts:
import { APIGatewayProxyEvent, APIGatewayProxyHandler, APIGatewayProxyResult, } from "aws-lambda"; import { getAllGroups } from "src/busniessLogic/groups"; export const handler: APIGatewayProxyHandler = async ( event: APIGatewayProxyEvent ): Promise<APIGatewayProxyResult> => { console.log("Processing event: ", event); const items = await getAllGroups(); return { statusCode: 200, headers: { "Access-Control-Allow-Origin": "*", }, body: JSON.stringify({ items, }), }; };

浙公网安备 33010602011771号