Хотелось бы найти такой стек технологий для разработки микросервисов, который обеспечил такую же самодокументируемость программного кода при разработке «традиционных» REST-API микросервисов. И он, как оказалось, уже существует.
Определим действующих лиц и исполнителей, которые будут задействованы в нашем небольшом примере.
ArangoDB — гибридная, документо+граф-ориентированная база данных.
UPD. Более детальное знакомство с этой базой данных стало поводом для еще одного разочарования. Как оказалось, после превышения базой данных некоиторого лимита, который ограничен свободной оперативной памятью — база начинает не то что тормозить — она просто останавливается вместе с сервером. При этом высказываись робкие предположения о том что переход на новые движки хранения все будет работать лучше, которые пока не подтвердились.
UPD1. Хорошо иногда возвращаться и перепроверять обновленные версии продукта. Вскоре после написания этой статьи вышла версия 3.4, которая по заверению разработчиков пофиксила проблему с производительностью (фактически полную остановку) после превышения коллекцией размера оперативной памяти. Но я еще лично это не перепроверял. После соответствующих экспериментов сделаю очередной апдейт этой статьи. Рад, что arangodb возможно преодолела эту проблему и в этом случае она снова очень привлекательна для разработки приложений на ее основе.
Foxx — микросервисный фреймверк, встроенный в базу данных ArangoDB. Работает на движке JavaScript, который (в отличие от nodejs) может одновременно выполняться в неограниченном количестве параллельных потоков (не блокирующих друг друга), вследствие чего нет необходимости в конструкциях promise/than/canch и async/await. В отличие от mongodb, в которой не рекомендуется злоупотреблять серверными процедурами, и реляционных баз данных, в которых хранимые процедуры используются также осторожно и уж точно не взаимодействуют по REST-API с клиентами (браузерами, мобильными приложениями и т.п.) — микросервисный фреймверк Foxx был разработан как раз для разработки микросервисов которые напрямую общаются по http-протоколу с клиентами.
Swagger — это программная среда с открытым исходным кодом, поддерживаемая большой экосистемой инструментов, которая помогает разработчикам разрабатывать, создавать, документировать и потреблять веб-службы RESTful. Хотя большинство пользователей идентифицируют Swagger с Swagger UI, набор инструментов Swagger включает поддержку автоматической документации, генерации кода и генерации тестовых примеров.
То, что Swagger включает поддержку генерации кода это ситуация противоположная той, которую хотелось бы получить — когда код поддерживает генерацию документации. То что предлагает нам ArangoDB+Foxx как раз включает обратный вариант. Когда по коду микросервисов генерируется схема для Swagger-а. Впрочем, теперь в этом можно убедиться самостоятельно проделав минимум работы.
У Вас должна быть установлена ArangoDB, чтобы выполнить дальнейшие действия.
- Входим в админку и выбираем пункт создания нового микросервиса Services->Add service->New.
- Заполняем в открывшейся форме обязательные реквизиты. В поле «Document Collections» добавляем имя коллекции документов которая будет создана при разворачивании микросервиса. Например, cats.
- Кликаем по кнопке install, заносим в поле url, к которому будет примонтирован микросервис.
При инсталяции микросервиса, для каждой коллекции из поля «Document Collections» (см. п.2) будут созданы роуты, реализующие CRUD операции методами POST, GET, PUT и DELETE. Впрочем, это только заготовки методов, которые можно менять, удалять, добавлять новые. Мы выбрали одну коллекцию при содании микросервиса (cats), хотя могли этого и не деать, а все добавить потом вручную.
Тепрь у нашей коллекции cats есть роуты для CRUD операций, и мы можем начать вызывать эти роуты из админки базы данных выбрав вкладку API (Services->[Имя микросервиса]->API). В этом вкладке расположен привычный интерфейс Swagger-а. Также есть возможность опубликовать интерфейс Swagger-а на внешнем роуте доступном не только через админку, но как обычный URL.
Если попробовать добавить документ в коллекцию cats методом POST {«name»: «Tom»}, то получим статус с ошибкой. Т.к. поле name мы нигде еще не определили. Поэтому продолжим дальше работу с админкой ArangoDB.
4. Для более удобной разработки в ArangoDB предусмотрен режим Development, который включается на вкладке Settings (Services->[Имя микросервиса]->Settings-Set development)
Теперь можно менять код миросервиса и сразу наблюдать результат (без дополнительного деплоя). Каталог, в котором расположен программный код микросервиса можно узнать в админке на вкладке Info (Services->[Имя микросервиса]->Info).
Посмотрим как выглядит определение роута POST
'use strict'; const dd = require('dedent'); const joi = require('joi'); const httpError = require('http-errors'); const status = require('statuses'); const errors = require('@arangodb').errors; const createRouter = require('@arangodb/foxx/router'); const Cat = require('../models/cat'); const cats = module.context.collection('cats'); const keySchema = joi.string().required() .description('The key of the cat'); const ARANGO_NOT_FOUND = errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code; const ARANGO_DUPLICATE = errors.ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED.code; const ARANGO_CONFLICT = errors.ERROR_ARANGO_CONFLICT.code; const HTTP_NOT_FOUND = status('not found'); const HTTP_CONFLICT = status('conflict'); const router = createRouter(); module.exports = router; router.tag('cat'); router.post(function (req, res) { const cat = req.body; let meta; try { meta = cats.save(cat); } catch (e) { if (e.isArangoError && e.errorNum === ARANGO_DUPLICATE) { throw httpError(HTTP_CONFLICT, e.message); } throw e; } Object.assign(cat, meta); res.status(201); res.set('location', req.makeAbsolute( req.reverse('detail', {key: cat._key}) )); res.send(cat); }, 'create') .body(Cat, 'The cat to create.') .response(201, Cat, 'The created cat.') .error(HTTP_CONFLICT, 'The cat already exists.') .summary('Create a new cat') .description(dd` Creates a new cat from the request body and returns the saved document. `);
Как валидация так и документирование, основаны на использовании схемы объекта. Внесем в нее небольшие изменения, добавив поле name:
'use strict'; const _ = require('lodash'); const joi = require('joi'); module.exports = { schema: { // Describe the attributes with joi here _key: joi.string(), name: joi.string().description('cat`s name'), // добавили новое поле }, forClient(obj) { // Implement outgoing transformations here obj = _.omit(obj, ['_id', '_rev', '_oldRev']); return obj; }, fromClient(obj) { // Implement incoming transformations here return obj; } };
Перейдя в закладку API, можно убедиться, что схема поменялась и теперь объект с полем name можно добавить в коллекцию cats.
apapacy@gmail.com
12 ноября 2018 года.