Около года назад мне было поручено возглавить группу разработчиков по созданию облачного веб-приложения для государственного учреждения. Естественно, мы решили реализовать внутренний сервис с использованием 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