Введение в Seneca.JS

в 15:02, , рубрики: architecture, framework, javascript, Microservices, node.js, nodejs

Seneca — microservices toolkit для Node.JS. Он предоставляет плагины, которые заботятся об основах вашего приложения. Это позволяет сосредоточиться на реальной бизнес-логике. Нет необходимости беспокоиться о том, какую базу данных использовать, как структурировать компоненты или как управлять зависимостями. Просто начните писать код.

Вы описываете всё как действия. Действия вызываются всякий раз, когда они соответствуют набору свойств. Ваш вызывающий код не знает и не заботится, какое действие выполняет работу. Один объект JavaScript входит, а другой выходит, асинхронно.

Чем Seneca.js не является

Я люблю конкретные сравнения, и увы я быстро делаю поспешные обобщения, которые могут в конечном итоге причинить мне боль.
Чтобы этого избежать, вот список того, что нет у Seneca:

  • Seneca — это не “строгий” фреймворк. Seneca не навязывает вам ни архитектуру приложения ни каких-либо подходов. Хотите разместить все свое приложение в одном действии? Вы можете. Хотите сделать каждое действие всего в 5 строк кода? Вы тоже можете это сделать. Seneca невероятно надежен и будет работать практически со всеми архитектурными решениями, которые вы принимаете.
  • Seneca не является дружественным к новичкам генератором приложений. Нет никакой «Просто добавь воды» магии, на которую новички могут опираться для мгновенного создания приложений.Только после достаточного изучения использование Seneca может быть эффективным. Если вы знаете, что делаете, Seneca невероятно полезен. Если вы не уверены, что вы делаете, Seneca не решит ваши проблемы за вас. Он так же хорош, как программист, использующий его.

  • Seneca не является небольшой библиотекой-утилитой. Seneca не похож на библиотеку без зависимостей как lodash, который вы можете подключить и посыпать по всему вашему приложению. Seneca довольно развесистый и очень привлекательный под капотом. Seneca весит между Hapi и Express с точки зрения размера (сравнивая выпуски gzipped и размеры каталогов в node_modules). Это вполне обоснованно, если учесть, что Seneca должен решить очень сложные проблемы, связанные с pattern matching, порядком выполнения действий и взаимодействием процессов.

Так что же такое Seneca.js?

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

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

Это определение является упрощением всего, что дает Seneca, но это ядро того, что выполняет Seneca. Единственным двусмысленным термином в этом определении является слово «действие», которое является базовым элементом приложения Seneca, а также самая важная абстракция, предоставляемая инструментарием.

Actions

Action — это функция, которая идентифицируется объектом JSON. Действия лежат в основе Seneca, и для использования Seneca разработчик должен уметь мыслить в терминах небольших функций, которые могут быть вызваны из любого места их идентификаторами JSON. Действия создаются с использованием метода seneca.add:

seneca.add({role:'inventory', cmd:'find_item'}, function(args, done) {
  const itemId = args.id;

  // find item using any means necessary
  const item = byAnyMeansNecessary(itemId);

  done(null, item);
});

Действия могут иметь любую детализацию и любой шаблон JSON. У вас могут быть действия из {application:'myApp', accomplish:'everything'} в { module:'addition', perform:'onePlusOne', because:'reasons'}. Как персональный стиль и соглашение, найденное во всех плагинах Seneca, я стараюсь сохранять свои действия в формате {role: 'namespace', cmd: 'action'}, где "namespace" является логической группировкой нескольких действий и " action "- это название конкретного действия, которое я хочу определить.

Вызывающие действия можно выполнить с помощью метода seneca.act:

seneca.act({role:'inventory', cmd:'find_item', id:'a3e42'}, function(err, item) {
  if (err) return err;

  console.log(item);
  // Perform other actions with item
});

Z предпочитаю определять связанные действия в одном файле с той же ролью (например, файл todo-list.js будет иметь все задачи с ролью todo_list). Как правило, я стараюсь, чтобы этот файл не превышал 150 строк, и стараюсь, чтобы каждое определение действия было достаточно маленьким, чтобы читать без скролла. У других людей могут быть разные предпочтения, но этот размер мне удобен.

Почему действия?

Так что теперь, когда у вас есть базовое представление о базовом элементе Seneca, вы вероятно задаетесь вопросом о цели разбить систему на определения действий и вызовы действий. Цель здесь-создать допустимые границы между компонентами вашего приложения, которые заставят вас задуматься над более модульной архитектурой и избежать желания бросить все в один главный файл. Как мы увидим в ближайшее время, после того, как приложение было разделено на действия, Seneca предоставляет обилие инструментов, чтобы раскрыть действия, как в виде HTTP микросервисов так и URL-адресов веб-серверов.

Организация действий

Ранее я упоминал, как я храню все связанные действия в одном файле, но не описывал, как я прикрепил эти действия к экземпляру Seneca в другом файле. Seneca содержит seneca.use метод, который находит соответствующий файл или модуль и включает его в экземпляр Seneca. Например, если бы у меня было несколько действий, которые все имели отношение к инвентаризации, я бы создал inventory.js и определил все действия там:

/* inventory.js */
module.exports = function(options) {
  const seneca = this;

  seneca.add({role:'inventory', cmd:'find_item', find_item);
  seneca.add({role:'inventory', cmd:'create_item', create_item);
  //... other action definitions

  function find_item(args, done) {
    const itemId = args.id;
    // ... perform find
    done(null, item);
  }

  function create_item(args, done) {
    const itemName = args.name;
    // ... perform item creation
    done(null, item);
  }
}

/* server.js */

const seneca = require('seneca')();

seneca.use('./inventory.js');

seneca.act({role:'inventory', cmd:'create_item', name:'apple'}, function(err, item) {
  console.log(item);
}

Обратите внимание, что через this мы получаем ссылку на экземпляр seneca в inventory.js. После добавления действий в экземпляр мы можем вызвать эти действия в server.js. Распространенный формат на примере inventory.js — вызываем seneca.add в верхней части и далее определяем методы. Это не обязательно, просто это соглашение соблюдают несколько реализованных плагинов.

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

Следующий уровено: несколько процессов

В приведенном примере уже можно запустить приложение с помощью node server.js и все будет происходить в одном процессе.
Однако, что делать если требуется выполнить несколько процессов, один из которых просто обрабатывал бы действия инвентаризации и другой процесс, использующий их. Взаимодействие между процессами может быть сложным для NodeJS приложений, но Seneca позволяет получить это поведение только двумя изменениями в коде.

Мы оставим inventory.js без изменений, используя действия как плагин Seneca. Однако мы создадим два новых файла, один из которых inventory-service.js, который будет запускать сервис на порту 10101 с помощью seneca.listen и другой файл inventory-client.js, который будет использовать seneca.client для доступа к сервису и использования его действий.

/* inventory-service.js */

const seneca = require('seneca')();

seneca.use('./inventory.js');

seneca.listen();

Теперь мы можем запустить node inventory-service.js в отдельном процессе и он будет слушать 10101 порт. В inventory-client.js мы можем написать:

/* inventory-client.js */

const seneca = require('seneca')();

seneca.client();

seneca.act({role:'inventory', cmd:'create_item', name:'apple'}, function(err, item) {
  console.log(item);
}
// …

Запуск node inventory-client.js будет выполнять тоже что и server.js в прошлом примере, только теперь мы имеем возможность масштабировать наше приложение горизонтально по нескольким процессам. Это является истинным преимуществом использования Seneca: поскольку мы разделили наше приложение на небольшие действия, эти действия могут быть объявлены в различных файлах или даже различных процессах. С помощью плагинов, доступных для веб-интеграции, действия могут выполняться даже на отдельных хостах.

Ссылки

Если будет интерес, то готов описать как все это проинтегрировать с Express, завернуть в Docker и отмасштабировать.

Автор: Алексей Клеандров

Источник

Поделиться

* - обязательные к заполнению поля