Около года назад мне было поручено возглавить группу разработчиков по созданию облачного веб-приложения для государственного учреждения. Естественно, мы решили реализовать внутренний сервис с использованием AWS Serverless Stack, включая такие сервисы, как:

  • AWS Lambda
  • Amazon API Gateway
  • Amazon Cognito
  • AWS Step Functions
  • Amazon EventBridge и т. Д.

Для IaC («Инфраструктура как код») мы решили использовать бессерверную платформу, поскольку она предоставляет зрелое предложение, которое, что наиболее важно, поддерживает локальную разработку Lambda с такими подключаемыми модулями, как sls-offline, а также позволяет нам использование системы контроля версий означает, что ни один разработчик не написал код в консоли AWS Lambda.

Начиная

Поначалу это может показаться сложным, начиная с sls create — template aws-nodejs, который генерирует только самые базовые каркасы серверной службы. Мы выбрали среду выполнения Node.js, поскольку она легкая, надежная и очень хорошо работает с AWS lambda (низкое время холодного запуска).

В первые дни проекта я потратил много времени на исследования и попытки придумать лучший дизайн, который бы структурировал наш внутренний код с учетом следующих основных концепций:

  • Читаемость. Когда к нам присоединяется новый разработчик, процесс адаптации должен быть максимально гладким и беспроблемным.
  • Ремонтопригодность. Код должен легко изменяться или расширяться за счет новых функций по мере необходимости.
  • Раздельная логика. Разделите внутренние задачи на части, которые: легко писать, отлаживать и тестировать. Также отделите бизнес-логику от; Доступ к данным, служебные программы и проверки таким образом, чтобы одно не влияло на другое.

Несмотря на обширные исследования, я не смог найти достаточно подробный подход, который бы отвечал всем моим требованиям.

Шаблон кода

После грусти первых дней мы, наконец, смогли задействовать все возможности и приступить к выполнению поставленной задачи. Это привело к созданию хорошо продуманного продукта SaaS, отвечающего всем нашим требованиям. Из уроков, извлеченных в этом процессе, материализовался более общий шаблон кода, которым я хотел бы поделиться с вами сегодня. Это поможет вам заложить прочный фундамент с самого начала с помощью вашей бессерверной серверной службы и основывается на шаблоне служб, определенном в этом сообщении бессерверным.

Внутренний поток выглядит примерно так:

Вызов клиентского API авторизован, выгружается в обработчик лямбда, полезная нагрузка затем проверяется, проходит через службу бизнес-логика, затем через уровень доступа к данным, и, наконец, данные либо выбираются, либо удаляются, либо вставляются в базу данных.

Чтобы продемонстрировать шаблон, определенный выше, я написал небольшое бессерверное приложение с использованием TypeScript и опубликовал его на Github в качестве обобщенного варианта использования. Это простой сервис Movie REST API, который поможет проиллюстрировать подход на примере.

Вот как выглядит предлагаемая структура в VSCode:

В этом шаблоне кода каждый бизнес-объект, существующий в нашем приложении, будет иметь следующие компоненты:

  • Лямбда-обработчик
  • Запросить валидатор
  • Услуга
  • DAO
  • Схема

Присмотритесь к элементам узора

Давайте подробнее рассмотрим каждый из упомянутых выше элементов.

Лямбда-обработчик

Здесь важно отметить, что каждая лямбда-функция обрабатывает все операции CRUD, относящиеся к данному бизнес-объекту. Это должно привести вас примерно к 4 отдельным точкам входа для каждой лямбда-функции.

Как вы должны видеть обработчик лямбда-выражений, это оркестратор серверной службы, он организует фрагменты логики, которые мы пишем в других частях нашего кода. Он будет использовать нашу логику проверки, нашу логику службы и направит запрос в правильную точку входа на основе входящего ресурса API.

Выглядит это примерно так:

Запросить валидатор

Цель валидатора запроса - проверить полезную нагрузку данного запроса API и убедиться, что она соответствует ожиданиям уровня доступа к данным. Идея здесь в том, что если полезная нагрузка неверна, запрос останавливается и больше не обрабатывается.

Существует множество библиотек Node.js, которые могут выполнить эту работу, или вы можете использовать промежуточное программное обеспечение Lambda, такое как Middy, но я лично предпочитаю иметь полный контроль над процессом проверки, поэтому я использую библиотеку ajv.

Валидатор выглядит примерно так:

Услуга

Здесь у вас будет бизнес-логика для данной сущности. Служба также будет единственным логическим компонентом, который взаимодействует с уровнем доступа к данным, если запросы пройдут проверку.

Обработчик Lambda передаст запрос службе, которая затем позаботится о его выполнении.

В моем примере Movie REST API у меня есть небольшая логика, которая проверяет, существует ли фильм в общедоступной базе данных фильмов и возвращает ли он ошибку. Очевидно, что в большинстве веб-приложений бизнес-логика будет намного сложнее, но функция, выполняемая компонентом службы, остается прежней.

Вот как выглядит сервис сущности:

DAO и схема сущности

В бессерверном контексте и там, где это применимо, мы склонны использовать механизмы баз данных NoSQL, такие как DynamoDB и MongoDB, из-за их масштабируемости и простоты использования, что означает, что для объекта нет конкретной ожидаемой схемы. Это может быть как благословением, так и проклятием.

Что мне здесь нравится, так это иметь файл схемы сущности, в котором есть интерфейс, определяющий структуру и свойства объекта. По сути, это публичный контракт, который должен будет реализовать класс Entity. Что мне также нравится делать здесь, так это убедиться, что все общие свойства инкапсулированы в интерфейс BaseEntity, который расширяется всеми другими сущностями. Например, в DynamoDB все записи требуют PK и SK, поэтому в моем примере мой BaseEntity выглядит примерно так:

Вот интерфейс, расширяющий BaseEntity:

Реализация этого интерфейса представлена ​​в виде класса, который будет иметь несколько статических методов для взаимодействия с базой данных, а также конструктор, определяющий экземпляр класса. В качестве дополнительного бонуса интерфейсы предоставляют разработчикам отличную помощь во время компиляции, поскольку они точно знают, что нужно определить в свойствах класса!

Вот как может выглядеть DAO:

Как вы, вероятно, можете сказать по моим методам, я использую дизайн единой таблицы Рика Хулихана, хотя и очень упрощенным способом, и PK и SK одинаковы, но это кое-что на другой день.

Заворачивать

Надеюсь, я смог дать вам возможность заглянуть в бессерверный мир с помощью шаблона кода и примера. Вначале это может показаться немного другим, но как только вы начнете, это действительно полезно, зная, что вы можете сразу перейти к поставленной задаче, и думать только о написании чистого и эффективного кода!

Больше контента на plainenglish.io