Почему Лакония? ну, давайте почитаем немного их документации
Laconia — это микрофреймворк для создания бессерверных приложений Node.js на основе функций. Laconia защищает вас от трудностей бессерверной разработки, предоставляя согласованные архитектурные шаблоны и опыт разработчиков.
Звучит интересно, но почему бы не использовать некоторые из самых известных фреймворков nodeJS, таких как Express, Koa, Nest и т. д.?
Большинство этих фреймворков и библиотек создано для Интернета, и если вы хотите использовать их с Serverless, у вас есть чтобы установить некоторые другие библиотеки поверх них.
Самая большая разница заключается в том, что ExpressJS создает сервер, и он будет все время прослушивать какой-то порт, но то, что предлагает serverless, — это использовать функцию как услугу (FaaS), которая выполняется по запросу, так зачем использовать что-то, чего нет? построен для этого?
Давайте продолжим и создадим простое бессерверное приложение с двумя конечными точками, которые используют этот API и развернут его в AWS Lambda (На данный момент Laconia поддерживает только AWS)
Покажи мне код! 💻
Установить бессерверную версию глобально
npm install -g serverless
Создадим папку, где будем работать.
mkdir laconia-example && cd laconia-example
Создайте сервис с помощью бессерверного CLI с шаблоном aws-nodejs.
serverless create --template aws-nodejs --name laconia-example
Начать проект npm
npm init -y
Laconia доступна в виде нескольких пакетов на NPM, установите @laconia/core
и получите остальные в зависимости от ваших потребностей.
npm install --save @laconia/core
- core: фреймворк внедрения микрозависимостей
- адаптер: преобразует события AWS во входные данные вашего приложения.
- adapter-api: преобразует события прокси-сервера шлюза API во входные данные вашего приложения.
- event: анализирует и реагирует на входящие события
- invoker: вызывает лямбда-выражения как обычные функции.
- config: Экстернализует секрет приложения и конфигурацию.
- пакет: считывает большое количество записей без ограничения по времени.
- middleware-lambda-warmer: интегрирует Lambda с lambda-warmer.
- middleware-serverless-plugin-warmup: короткое замыкание Lambda запускается при вызове serverless-plugin-warmup
Сделаем рефрактор, создадим папку src и внутри добавим три папки application, domain,иинфраструктура (DDD)
На уровне приложения он будет отвечать за определение и оркестровку лямбда-выражений, передавая им необходимые зависимости.
Уровень domain будет отвечать за наличие только лямбда-выражений. обработчики и бизнес-логика.
Уровень инфраструктуры — это то, что предоставляет реализации, поддерживающие ранее определенные уровни, внешние зависимости, сервисы.
├── .gitignore ├── package.json ├── package-lock.json ├── serverless.yml └── src ├── application │ └── app.js ├── domain │ └── getPosts.js └── infrastructure ├── dependencies │ └── postsService.js └── httpServices ├── postService │ ├── keys.js │ └── Post.js └── request.js
Одним из преимуществ Laconia является зависимость инъекция 🌈
Laconia явно разделяет ответственность за создание объектов и выполнение функции Lambda.
Ваш app.js будет отвечать за то, чтобы обернуть вашу лямбду необходимыми зависимостями с помощью метода .register(), который получает объект.
const laconia = require('@laconia/core'); // Lambda const getPosts = require('../domain/getPosts'); // Dependencie const postsService = require('../infrastructure/dependencies/postsService'); module.exports.getPosts = laconia(getPosts).register(postsService)
Создайте postsService.js
const Post = require('../httpServices/postService/Post'); const postInstance = () => { const postsService = new Post(); return { postsService } }; module.exports = postInstance;
иPost.js
const api = require('./keys') const request = require('../request') class Post { constructor() { this.posts = 'posts', this.request = request } getPosts() { const url = `${api.baseURL}/${this.posts}`; return this.request(url); } } module.exports = Post;
Наконец, request.js и key.js, я использую node-fetch, но вы можете использовать все, что захотите. а файл key предназначен только для сохранения базы URL.
запрос.js
const fetch = require('node-fetch'); const request = (url) => { return fetch(url) .then(response => response.json()) .then(data => data) .catch(err => err); } module.exports = request
Также установите зависимость node-fetch
npm install --save node-fetch
key.js
const baseURL = 'https://jsonplaceholder.typicode.com'; module.exports = { baseURL }
Ваш getPost.js будет отвечать за бизнес-логику, получать зависимости/службы, которые он использует в качестве параметров.
Прежде всего, давайте прочитаем очень важный момент о Laconia.
Поскольку Laconia меняет подпись вашего обработчика с
event, context, callback
наinput, LaconiaContext
, вы можете задаться вопросом, как можно получить исходный контекст или событие AWS. Эти переменные доступны для доступа через LaconiaContext с ключомcontext
иevent
. Laconia рекомендует не использоватьcontext
илиevent
в ядре вашего приложения, поскольку это зависит от AWS, а вместо этого использовать их в своем адаптере.
Лямбда получает зависимости через LaconiaContext.
getPosts.js
const handler = async (event, { postsService }) => { const data = await postsService.getPosts(); return { statusCode: 200, body: JSON.stringify( { response: data, } ), }; }; module.exports = handler;
И это оно! теперь давайте попробуем это локально 🚀
Отредактируйте файл serverless.yml, удалите все комментарии и добавьте триггер события HTTP.
service: laconia-example provider: name: aws runtime: nodejs12.x functions: getPosts: handler: src/application/app.getPosts events: - http: path: /posts method: get
Теперь давайте установим библиотеку для работы офлайн.
npm install --save aws-sdk
npm install --save-dev serverless-offline
Добавьте плагин в файл serverless.yml.
service: laconia-example plugins: - serverless-offline ...
добавьте скрипт для запуска проекта
{ "name": "laconia-example", "version": "1.0.0", "description": "", "scripts": { "dev": "serverless offline" }, ... }
Проверьте это, вы увидите что-то вроде этого.
npm run dev
И вуаля 🙌
Мы проделали большую работу, продолжим пробовать еще одно преимущество Laconia.
npm install --save @laconia/event
Разбирает и реагирует на входящие события. Помогает вам легко создать свой собственный адаптер.
Вы можете использовать его со шлюзом S3, SNS, SQS, Kinesis и API.
Мы будем использовать шлюз API для получения запроса и ответа.
Создайте apiGateway.js в src/infrastructure/dependencies.
const api = require('@laconia/event').apigateway; const apiGateway = async () => { return { api }; }; module.exports = apiGateway;
в вашем app.jsдобавьте зависимости и зарегистрируйте в обработчике
... const apiGateway = require('../infrastructure/dependencies/apiGateway'); module.exports.getPosts = laconia(getPosts) .register(postsService) .register(apiGateway)
Теперь мы будем получать эту зависимость в обработчике, используйте объект req, чтобы вернуть ответ более чистым способом.
const handler = async (event, { postsService, api }) => { const { req, res } = api; const data = await postsService.getPosts(); return res({ data }, 200); }; module.exports = handler;
Красиво, правда? Давайте добавим еще несколько зависимостей.
npm install --save loggy
Loggy: красочный std stream предельно простой регистратор для node.js
Создайте loggy.js в src/infrastructure/dependencies.
const logger = require('loggy'); const loggy = async () => { return { logger }; }; module.exports = loggy;
в вашем app.js сначала добавьте зависимости и зарегистрируйте в обработчике, почему сначала? Потому что в Лаконии есть нечто, называемое цепной инъекцией 🌈.
Вы можете связать свою инъекцию в Лаконии, вызвав
register
несколько раз. Каждая из зарегистрированных вами фабрик будет вызываться последовательно, и следующие фабрики смогут получить доступ к экземплярам, созданным предыдущими фабриками.
... const logger = require('../infrastructure/dependencies/loggy') module.exports.getPosts = laconia(getPosts) .register(logger) .register(postsService) .register(apiGateway)
Таким образом, в вашем postsService у вас будет регистратор, если вы хотите использовать его для регистрации всех вызовов других служб.
const Post = require('../httpServices/postService/Post'); const postInstance = ({ logger }) => { logger.info('Hi from postService'); const postsService = new Post(); return { postsService } }; module.exports = postInstance;
Вы увидите нечто подобное в консоли.
15:34:19 - info: Hi from postService
И, конечно же, вы можете использовать его в обработчике.
const handler = async (event, { logger, postsService, api }) => { logger.info('Hi from the handler getPosts'); const { req, res } = api; const data = await postsService.getPosts(); return res({ data }, 200); }; module.exports = handler;
Наконец, создайте новую конечную точку, в которой мы используем конкретный пост, добавьте новый обработчик функции в serverless.yml.
getPost: handler: src/application/app.getPost events: - http: path: /posts/{id} method: get
В приложении требуется обработчик getPost, оберните его зависимостями и экспортируйте объект с помощью двух обработчиков.
... const getPost = require('../domain/getPost'); ... module.exports = { getPost: laconia(getPost) .register(logger) .register(postsService) .register(apiGateway), getPosts: laconia(getPosts) .register(logger) .register(postsService) .register(apiGateway), }
Создайте файл getPost.js.
const handler = async (event, { logger, postsService, api }) => { const { req, res } = api; const request = req(event); logger.info('Hello', 'loggy'); const data = await postsService.getPost(request.params.id); return res({ data }, 200); }; module.exports = handler;
В Post.js добавьте новый метод для получения одного сообщения.
... getPost(id) { const url = `${api.baseURL}/${this.posts}/${id}`; return this.request(url); } ...
Попробуйте! 🚀
И все, мы сделали много, но это было основой для всего, что мы хотели сделать, в Лаконии есть гораздо больше, я рекомендую прочитать документацию и поддержать проект.
Наконец, чтобы развернуть это на AWS Lambda, нам нужно установить aws-cli и настроить профиль с вашими acces_key и secret_key. .
Как только вы это сделаете, разверните его!
serverless deploy
Вот репозиторий, где вы можете найти весь код. 🤙🏼