[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,
    }),
  };
};

 

上一篇:如何评估 Serverless 服务能力?这份报告给出了 40 条标准


下一篇:Azure Functions(一)什么是 ServerLess