1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/pantao-getting-started-seneca

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Seneca: Введение в микросервисы с использованием NodeJS

Seneca — это набор инструментов для быстрого создания систем микросервисов на основе сообщений. Вам не требуется знать, где именно расположены различные сервисы, сколько их существует или что они конкретно выполняют. Любые службы вне области вашей бизнес-логики (например, базы данных, кэширование или интеграция с третьими сторонами) скрыты за микросервисами.

Эта декомпозиция позволяет легко строить и обновлять ваши системы непрерывно. Seneca обеспечивает это благодаря своим трём ключевым функциям:

  1. Шаблонное соответствие: вместо хрупкого открытия служб, шаблонное соответствие предназначено для того, чтобы сообщить миру, какие сообщения вам действительно важны;
  2. Независимое передвижение: вы можете отправлять сообщения между службами различными способами, все эти способы скрыты за вашей бизнес-логикой;
  3. Компонентность: функциональность представлена как набор плагинов, которые могут объединяться вместе для создания микросервисов.

В Seneca сообщение представляет собой JSON-объект с любой внутренней структурой, который может быть передан через HTTP/HTTPS, TCP, очереди сообщений, публикацию/подписку или любым другим способом передачи данных. Как производитель сообщений, вы просто отправляете его, не заботясь о том, какие службы принимают его.Затем вы хотите сообщить миру, что вы хотите получать некоторые сообщения. Это также очень просто: достаточно выполнить некоторую конфигурацию шаблонного соответствия в Seneca. Шаблоны просты: это список пар ключ-значение, используемых для соответствия определенным атрибутам JSON-сообщений.

В следующих разделах мы будем создавать несколько микросервисов с использованием Seneca.

Шаблоны (Patterns)

Давайте начнем с очень простого примера кода. Мы создадим два микросервиса: один будет выполнять математическое вычисление, другой же вызовет этот первый.

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

seneca.add('role:math, cmd:sum', (msg, reply) => {
  reply(null, { answer: (msg.left + msg.right)});
});

seneca.act({
  role: 'math',
  cmd: 'sum',
  left: 1,
  right: 2
}, (err, result) => {
  if (err) {
    return console.error(err);
  }
  console.log(result);
});

Сохраните вышеупомянутый код в файл .js, затем запустите его. Вы должны увидеть что-то подобное в консоли:

{"kind":"notice","notice":"hello seneca 4y8daxnikuxp/1483577040151/58922/3.2.2/-","level":"info","when":1483577040175}
(node:58922) DeprecationWarning: 'root' is deprecated, use 'global'
{ answer: 3 }

До сих пор всё происходило в одном процессе без генерации сетевой активности, а вызовы функций внутри процесса осуществлялись через передачу сообщений.

Метод seneca.add добавляет новый шаблон действия (Action Pattern) к экземпляру Seneca. Он имеет два параметра:

  1. pattern: шаблон для совпадения JSON-тела сообщений в экземпляре Seneca;

  2. action: действие, которое выполняется при совпадении шаблона.Метод seneca.act также принимает два параметра:

  3. msg: входящее сообщение, представленное как чистый объект;

  4. respond: обратный вызов для получения и обработки ответа.

Представим, что мы пройдемся еще раз по всему коду:

seneca.add('role:math, cmd:sum', (msg, reply) => {
  reply(null, { answer: (msg.left + msg.right)});
});

Функция Action в вышестоящем коде суммирует значения двух свойств left и right в теле совпадающего сообщения. Нет необходимости создавать ответ для всех сообщений, но в большинстве случаев это требуется. Для ответа сообщению предоставляется обратный вызов.

Шаблон совпадения role:math, cmd:sum совпадает с следующим телом сообщения:

{
  role: 'math',
  cmd: 'sum',
  left: 1,
  right: 2
}

И получает результат:

{
  answer: 3
}

Свойства role и cmd ничего особенного собой не представляют; они просто используются для совпадения шаблонов.

Затем метод seneca.act отправляет сообщение, имеющее два параметра:

  1. msg: основной контент сообщения;
  2. response_callback: обратный вызов, который будет выполнен, если сообщение имеет ответ.

Обратный вызов для ответа может принимать два параметра: error и result. В случае возникновения ошибки (например, если отправленное сообщение не совпалось ни с одним шаблоном), первый параметр будет объектом типа Error. Если программа работает так, как нам это требуется, второй параметр будет содержать результат ответа. В нашем примере мы просто выводим этот результат в консоль.```javascript seneca.act({ role: 'math', cmd: 'sum', left: 1, right: 2 }, (err, result) => { if (err) { return console.error(err); } console.log(result); });


Пример файла [sum.js](https://github.com/pantao/getting-started-seneca/blob/master/sum.js) демонстрирует, как определить и создать действие и как его вызвать, но всё это происходит в рамках одного процесса. В ближайшее время мы покажем, как разделить это на несколько частей и процессов.

# Как работают шаблоны?

Шаблон позволяет вам расширять или усиливать вашу систему легче, чем с использованием сетевых адресов или сессий. Это делает добавление новых микросервисов проще.

Теперь давайте добавим новую функциональность в нашу систему — вычисление произведения двух чисел.

Мы хотим отправлять сообщение такого типа:

```javascript
{
  role: 'math',
  cmd: 'product',
  left: 3,
  right: 4
}

И получить ответ такого типа:

{
  answer: 12
}

Понятно? Вы можете создать операцию role: math, cmd: product, аналогичную role: math, cmd: sum:

seneca.add('role:math, cmd:product', (msg, reply) => {
  reply(null, { answer: (msg.left * msg.right)});
});

Затем вызвать эту операцию:

seneca.act({
  role: 'math',
  cmd: 'product',
  left: 3,
  right: 4
}, (err, result) => {
  if (err) {
    return console.error(err);
  }
  console.log(result);
});

Выполните скрипт product.js, чтобы получить нужный результат.

Объединив эти два метода, код будет выглядеть так:

const seneca = require('seneca')();
```seneca.add('role:math, cmd:sum', (msg, reply) => {
  reply(null, { answer: msg.left + msg.right });
});

seneca.add('role:math, cmd:product', (msg, reply) => {
  reply(null, { answer: msg.left * msg.right });
});

seneca.act({ role: 'math', cmd: 'sum', left: 1, right: 2 }, console.log)
      .act({ role: 'math', cmd: 'product', left: 3, right: 4 }, console.log);

Выполнив [sum-product.js](https://github.com/pantao/getting-started-seneca/blob/master/sum-product.js), вы получите следующий вывод:

```bash
null { answer: 3 }
null { answer: 12 }

В объединённом выше коде мы видим, что seneca.act можно использовать в цепочке вызовов. API Seneca предоставляет возможность последовательного выполнения запросов, но они могут выполняться параллельно, поэтому порядок получения результатов может отличаться от порядка вызова.

Расширение шаблонов для добавления новых функций

Шаблоны позволяют легко расширять функциональность программы. В отличие от использования конструкций if...else..., вы можете добавлять новые шаблоны для достижения аналогичной функциональности.

Допустим, мы хотим расширить операцию role: math, cmd: sum, которая принимает только целые числа. Как это сделать?

seneca.add({ role: 'math', cmd: 'sum', integer: true }, function (msg, respond) {
  var sum = Math.floor(msg.left) + Math.floor(msg.right);
  respond(null, { answer: sum });
});

Теперь рассмотрим следующее сообщение:

{ role: 'math', cmd: 'sum', left: 1.5, right: 2.5, integer: true }

Это сообщение вернет следующий результат:

{ answer: 3 }  // == 1 + 2, десятичные части были удалены
```Код доступен в файле [sum-integer.js](https://github.com/pantao/getting-started-seneca/blob/master/sum-integer.js).

Теперь ваши два шаблона существуют в системе и имеют пересечение. Какой шаблон будет выбран Senecой в конечном итоге? Принцип таков: шаблон с большим количеством совпадающих атрибутов имеет более высокий приоритет.

[pattern-priority-testing.js](https://github.com/pantao/getting-started-seneca/blob/master/pattern-priority-testing.js) предоставляет более наглядное тестирование:

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

seneca.add({role: 'math', cmd: 'sum'}, function (msg, respond) {
  var sum = msg.left + msg.right;
  respond(null, {answer: sum});
});

// Эти два сообщения совпадают с ролью: math, командой: sum

seneca.act({role: 'math', cmd: 'sum', left: 1.5, right: 2.5}, console.log);
seneca.act({role: 'math', cmd: 'sum', left: 1.5, right: 2.5, integer: true}, console.log);

setTimeout(() => {
  seneca.add({role: 'math', cmd: 'sum', integer: true}, function (msg, respond) {
    var sum = Math.floor(msg.left) + Math.floor(msg.right);
    respond(null, {answer: sum});
  });

  // Это сообщение также совпадает с ролью: math, командой: sum
  seneca.act({role: 'math', cmd: 'sum', left: 1.5, right: 2.5}, console.log);

  // Но также совпадает с ролью: math, командой: sum, целочисленной частью: true
  // Однако, поскольку больше атрибутов совпадают, его приоритет выше
  seneca.act({role: 'math', cmd: 'sum', left: 1.5, right: 2.5, integer: true}, console.log);
}, 100);

Результат вывода должен выглядеть следующим образом:

null {answer: 4}
null {answer: 4}
null {answer: 4}
null {answer: 3}
```В приведённом выше коде, поскольку в системе существует только один шаблон с ролью: math, командой: sum, все сообщения совпадают с ним. Однако после того как через 100 миллисекунд мы добавляем в систему новый шаблон с ролью: math, командой: sum, целочисленной частью: true, результаты становятся другими, и шаблон с большим количеством совпадающих атрибутов имеет более высокий приоритет. Эта архитектура позволяет нашей системе легко добавлять новые функции как в среде разработки, так и в среде производства. Вы можете обновлять новые службы без изменения существующего кода. Вам просто нужно подготовить новую службу и запустить её.## Кодовая переиспользование на основе шаблонов

Шаблоны могут вызывать другие операции, что позволяет нам достичь цели переиспользования кода:

```javascript
const seneca = require('seneca')()

seneca.add('role: math, cmd: sum', function (msg, respond) {
  var sum = msg.left + msg.right
  respond(null, {answer: sum})
})

senega.add('role: math, cmd: sum, integer: true', function (msg, respond) {
  // Переиспользует role:math, cmd:sum
  this.act({
    role: 'math',
    cmd: 'sum',
    left: Math.floor(msg.left),
    right: Math.floor(msg.right)
  }, respond)
})

// Соответствует role:math,cmd:sum
senega.act('role: math, cmd: sum, left: 1.5, right: 2.5', console.log)

// Соответствует role:math,cmd:sum,integer:true
senega.act('role: math, cmd: sum, left: 1.5, right: 2.5, integer: true', console.log)

В приведенном выше примере кода мы используем this.act, а не senega.act. Это потому, что контекстный объект this внутри функции действия ссылается на текущий экземпляр senega, позволяя вам получить доступ ко всему контексту вызова действия из любой функции действия.

В этом коде используется сокращенная форма JSON для описания шаблонов и сообщений. Например, это объект-литерал:

{role: 'math', cmd: 'sum', left: 1.5, right: 2.5}

Сокращённый шаблон выглядит следующим образом:

'role: math, cmd: sum, left: 1.5, right: 2.5'

Формат jsonic предоставляет удобный способ представления объектов через строковые литералы, что позволяет создавать более простые шаблоны и сообщения.

Вышеупомянутый код хранится в файле sum-reuse.js.## Уникальность шаблонов

Каждый определённый вами шаблон действий уникален и может триггерировать только одну функцию. Правила парсинга шаблонов следующие:

  • Шаблоны с большим количеством свойств имеют приоритет
  • При одинаковом количестве свойств, шаблоны сравниваются по алфавитному порядку

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

  • a: 1, b: 2 имеет приоритет перед a: 1, так как он содержит больше свойств;
  • a: 1, b: 2 имеет приоритет перед a: 1, c: 3, так как буква b стоит раньше буквы c;
  • a: 1, b: 2, d: 4 имеет приоритет перед a: 1, c: 3, d: 4, так как буква b стоит раньше буквы c;
  • a: 1, b: 2, c: 3 имеет приоритет перед a: 1, b: 2, так как он содержит больше свойств;
  • a: 1, b: 2, c: 3 имеет приоритет перед a: 1, c: 3, так как он содержит больше свойств.

Часто полезно иметь возможность расширить функциональность существующего действия без необходимости полностью переписывать его. Например, вы можете захотеть добавить более сложную проверку атрибутов сообщения, отслеживать статистику сообщений, добавлять дополнительные данные из базы данных или контролировать скорость потока сообщений.В следующем примере кода операция сложения ожидает, что атрибуты left и right будут окончательными числами. Кроме того, полезно добавлять проверки и информацию для отладки, используя следующий код:```javascript const seneca = require('seneca')()

seneca .add( 'role:math,cmd:sum', function(msg, respond) { var sum = msg.left + msg.right respond(null, { answer: sum }) })

// Переопределяем role:math,cmd:sum с добавлением дополнительной функциональности .add( 'role:math,cmd:sum', function(msg, respond) {

// Возвращаем ошибку, если есть проблема
if (!Number.isFinite(msg.left) ||
  !Number.isFinite(msg.right)) {
  return respond(new Error("Значения left и right должны быть числами."))
}

// Вызываем старую версию действия role:math,cmd:sum
this.prior({
  role: 'math',
  cmd: 'sum',
  left: msg.left,
  right: msg.right,

}, function(err, result) {
  if (err) return respond(err)

  result.info = msg.left + '+' + msg.right
  respond(null, result)
})

})

// Расширенная версия role:math,cmd:sum .act('role:math,cmd:sum,left:1.5,right:2.5', console.log // выводит { answer: 4, info: '1.5+2.5' } )


Инстанс `seneca` предоставляет метод `prior`, который позволяет вызвать старое действие внутри текущего действия.

Метод `prior` принимает два параметра:

1. `msg`: тело сообщения
2. `response_callback`: обратный вызов

В приведенном выше примере показано, как можно модифицировать входные и выходные параметры; эти изменения являются необязательными, и вы можете добавить новые переопределения для внесения дополнительных изменений, таких как логирование. Приведённый выше пример также демонстрирует лучшие способы обработки ошибок. Мы проверяем корректность данных до выполнения действий, чтобы сразу вернуть сообщение об ошибке при некорректных входных данных.
> Сообщения об ошибках следует использовать только для описания неправильных входных данных или внутренних сбоев системы. Например, если вы выполнили запрос в базу данных и получили пустой ответ, это не является ошибкой, а просто отражает текущее состояние базы данных. Однако, если соединение с базой данных было установлено неверно, то это уже будет являться ошибкой.

Вышеупомянутый код можно найти в файле [sum-valid.js](https://github.com/pantao/getting-started-seneca/blob/master/sum-valid.js).

## Организация паттернов действия с помощью плагинов

Инстанс `seneca` представляет собой набор различных паттернов действий. Вы можете организовать эти паттерны с использованием пространства имён, как показано в предыдущих примерах, где мы использовали `role: math`. Для помощи в логгировании и отладке, `Seneca` предоставляет возможность использования минималистичных плагинов.

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

Кратко говоря, Seneca-плагин — это всего лишь функция с одним параметром опций, которую вы передаёте методу `seneca.use`, вот минимальный пример Seneca-плагина:

```javascript
function plugin(options) {
    // Логика плагина
}

Параметр options обычно содержит конфигурационные данные, такие как пути к файлам, адреса серверов и другие настройки, необходимые для работы плагина.```javascript function минимальный_плugин(опции) { console.log(опции); }

require('seneca')() .use(минимальный_плugин, {foo: 'bar'});


Метод `seneca.use` принимает два параметра:

1. `plugin`: функция определения плагина или название плагина;
2. `options`: конфигурационные опции плагина;

После выполнения приведённого выше кода, вывод в консоли будет следующим:

```bash
{"kind":"notice","notice":"hello seneca 3qk0ij5t2bta/1483584697034/62768/3.2.2/-","level":"info","when":1483584697057}
(node:62768) DeprecationWarning: 'root' is deprecated, use 'global'
{ foo: 'bar' }

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

node minimal-plugin.js --seneca.log.all

Неожиданно? Конечно, вы можете также фильтровать логи:

node minimal-plugin.js --seneca.log.all | grep plugin:define

Из логов видно, что Seneca загружает множество встроенных плагинов, таких как basic, transport, web и mem-store. Эти плагины предоставляют базовые возможности для создания микросервисов. Также вы должны заметить плагин minimal_plugin.

Теперь давайте добавим несколько режимов работы этому плагину:

function math(опции) {

  this.add('role:math,cmd:sum', function (msg, respond) {
    respond(null, { answer: msg.left + msg.right })
  });
```  этот.add('роль:math,cmd:произведение', function (msg, respond) {
    respond(null, { ответ: msg.left * msg.right })
  })

}

require('seneca')()
  .use(математика)
  .act('роль:math,cmd:сумма,left:1,right:2', console.log)

Запустите файл math-plugin.js, чтобы получить следующий вывод:

null { ответ: 3 }

Посмотрите на запись в логах:

{
  "actid": "7ubgm65mcnfl/uatuklury90r",
  "msg": {
    "роль": "math",
    "cmd": "сумма",
    "left": 1,
    "right": 2,
    "meta$": {
      "id": "7ubgm65mcnfl/uatuklury90r",
      "tx": "uatuklury90r",
      "pattern": "cmd:сумма,роль:math",
      "action": "(bjx5u38uwyse)",
      "plugin_name": "math",
      "plugin_tag": "-",
      "prior": {
        "chain": [],
        "entry": true,
        "depth": 0
      },
      "start": 1483587274794,
      "sync": true
    },
    "plugin$": {
      "name": "math",
      "tag": "-"
    },
    "tx$": "uatuklury90r"
  },
  "entry": true,
  "prior": [],
  "meta": {
    "plugin_name": "math",
    "plugin_tag": "-",
    "plugin_fullname": "math",
    "raw": {
      "роль": "math",
      "cmd": "сумма"
    },
    "sub": false,
    "client": false,
    "args": {
      "роль": "math",
      "cmd": "сумма"
    },
    "rules": {},
    "id": "(bjx5u38uwyse)",
    "pattern": "cmd:сумма,роль:math",
    "msgcanon": {
      "cmd": "сумма",
      "роль": "math"
    },
    "priorpath": ""
  },
  "client": false,
  "listen": false,
  "transport": {},
  "kind": "act",
  "case": "OUT",
  "duration": 35,
  "result": {
    "ответ": 3
  },
  "level": "debug",
  "plugin_name": "math",
  "plugin_tag": "-",
  "pattern": "cmd:сумма,роль:math",
  "when": 1483587274829
}

Все логи данного плагина автоматически получают атрибут plugin.

В мире Seneca мы организуем различные режимы работы с помощью плагинов, что делает логирование и отладку проще, затем вы можете объединять несколько плагинов в различные микросервисы. В следующих разделах мы создадим сервис math.Плагины выполняют некоторые начальные операции, такие как подключение к базе данных, но вам не нужно выполнять эти операции внутри основной функции плагина. Основная функция предназначена для синхронного выполнения, так как она просто определяет плагин. На самом деле, вы не должны вызывать метод seneca.act внутри основной функции; следует использовать только метод seneca.add.

Для инициализации плагина вам нужно определить специальный шаблон init: <plugin-name>, который будет последовательно вызываться для каждого плагина. Функция init должна вызвать её callback-функцию без ошибок, если инициализация плагина завершается неудачей, то процесс Node.js немедленно завершится. Все операции инициализации плагина должны быть завершены до выполнения любых действий.

Чтобы продемонстрировать инициализацию, давайте добавим простое пользовательское логирование в плагин math. Когда плагин запускается, он открывает файл логов и записывает все логи действий в этот файл. Файл должен успешно открыться и быть доступен для записи; если это невозможно, запуск микросервиса должен завершиться неудачей.

const fs = require('fs')

function math(options) {
  
  // Логирующая функция, созданная через init функцию
  var log
  
  // Соединяем все режимы вместе для удобства поиска
  this.add('role:math,cmd:sum',     sum)
  this.add('role:math,cmd:product', product)
}
```  // Это специальное инициализационное действие
  this.add('init:math', init)

  function init(msg, respond) {
    // Логируем в специфический файл
    fs.open(options.logfile, 'a', function (err, fd) {

      // Если файл недоступен для чтения или записи, возвращаем ошибку, что приведёт к неудачному запуску Seneca
      if (err) return respond(err)

      log = makeLog(fd)
      respond()
    })
  }

  function sum(msg, respond) {
    var out = { answer: msg.left + msg.right }
    log('сложение ' + msg.left + '+' + msg.right + '=' + out.answer + '\n')
    respond(null, out)
  }

  function product(msg, respond) {
    var out = { answer: msg.left * msg.right }
    log('умножение ' + msg.left + '*' + msg.right + '=' + out.answer + '\n')
    respond(null, out)
  }
}

функция makeLog(fd) { return function (entry) { fs.write(fd, new Date().toISOString() + ' ' + entry, null, 'utf8', function (err) { if (err) return console.log(err)

    // Убедитесь, что запись лога была синхронизирована
    fs.fsync(fd, function (err) {
      if (err) return console.log(err)
    })
  })
}

} }

require('seneca')() .use(math, { logfile: './math.log' }) .act('role:math,cmd:sum,left:1,right:2', console.log)


В приведённом выше плагине код организован таким образом, чтобы шаблоны были видны сверху, а функции определены чуть ниже. Также обратите внимание, как можно указывать путь к пользовательскому файлу журнала через опции (не забывайте, это не производственный журнал!).Инициализирующая функция `init` выполняет некоторые асинхронные операции системы файлов, поэтому она должна завершиться до выполнения любых действий. В случае ошибки весь сервис может не запуститься. Чтобы увидеть поведение при возникновении ошибки, попробуйте указать недействительный путь к файлу журнала, например `/invalid.log`.Вышеуказанный код находится в файле [math-plugin-init.js](https://github.com/pantao/getting-started-seneca/blob/master/math-plugin-init.js).

# Создание микросервиса

Теперь давайте сделаем из плагина `math` настоящий микросервис. Для начала вам потребуется организовать ваш плагин. Бизнес-логика плагина `math`, то есть те функции, которые он предоставляет, отделена от способа его взаимодействия с внешним миром. Возможно, вы захотите экспонировать веб-сервис или слушать сообщения на мессенджинговой бирже.

Размещение бизнес-логики (то есть определения плагина) в отдельном файле имеет смысл. Это идеально подходит для модулей Node.js. Создайте файл под названием [math.js](https://github.com/pantao/getting-started-seneca/blob/master/math.js) со следующим содержимым:

```javascript
module.exports = function math(options) {

  this.add('role:math,cmd:sum', function sum(msg, respond) {
    respond(null, { answer: msg.left + msg.right })
  })

  this.add('role:math,cmd:product', function product(msg, respond) {
    respond(null, { answer: msg.left * msg.right })
  })

  this.wrap('role:math', function (msg, respond) {
    msg.left = Number(msg.left).valueOf()
    msg.right = Number(msg.right).valueOf()
    this.prior(msg, respond)
  })
}

Затем вы можете добавить этот микросервис в систему, где требуется обращаться к нему, используя следующую конструкцию:

// Ниже приведены два эквивалентных способа (помните ли вы два аргумента метода `seneca.use`, который мы рассматривали ранее?)
require('seneca')()
  .use(require('./math.js'))
  .act('role:math,cmd:sum,left:1,right:2', console.log)
``````markdown
require('seneca')()
  .use('math') // Найдёт './math.js' в текущей директории
  .act('role:math,cmd:sum,left:1,right:2', console.log)

Метод seneca.wrap может совпадать с набором шаблонов и использовать одинаковый расширяемый функционал действий для всех совпавших шаблонов, что аналогично эффекту, который можно достичь путём ручной вызова seneca.add для каждого шаблона. Этот метод требует двух параметров:

  1. pin: Шаблон для совпадения
  2. action: Расширяющая функция действия

Шаблон pin может совпадать с множеством других шаблонов, например, role:math совпадает как с role:math, cmd:sum, так и с role:math, cmd:product.

В примерах выше, в последнем вызове wrap функции, мы гарантируем, что любое сообщение, переданное в role:math, будет иметь значения left и right в виде чисел, даже если были переданы строки, они будут автоматически преобразованы в числа.

Иногда полезно знать, какие операции были переопределены в экземпляре Seneca. Вы можете добавить параметр --seneca.print.tree при запуске приложения. Создайте файл math-tree.js со следующим содержанием:

require('seneca')()
  .use('math')

Затем выполните его:

❯ node math-tree.js --seneca.print.tree
{"kind":"notice","notice":"hello seneca abs0eg4hu04h/1483589278500/65316/3.2.2/-","level":"info","when":1483589278522}
(node:65316) DeprecationWarning: 'root' is deprecated, use 'global'
Seneca action patterns for instance: abs0eg4hu04h/1483589278500/65316/3.2.2/-
├─┬ cmd:sum
│ └─┬ role:math
│   └── # math, (15fqzd54pnsp),
# math, (qqrze3ub5vhl), sum
└─┬ cmd:product
  └─┬ role:math
    └── # math, (qnh86mgin4r6),
        # math, (4nrxi5f6sp69), product
```Вы видите множество пар ключ/значение, отображённых в виде дерева, где все функции действий имеют формат `#plugin, (action-id), function-name`.

Однако до настоящего времени все операции находятся в одном процессе. Давайте создадим файл [math-service.js](https://github.com/pantao/getting-started-seneca/blob/master/math-service.js) со следующим содержанием:

```javascript
require('seneca')()
  .use('math')
  .listen()

Запустив этот скрипт, вы запустите наш микросервис, который начнёт прослушивание HTTP-запросов через порт 10101. Это не веб-сервер; в данном случае HTTP используется как механизм передачи сообщений.

Вы можете получить доступ к следующему адресу: http://localhost:1cq101/act?role=math&cmd=sum&left=1&right=2, чтобы увидеть результат, либо использовать команду curl:

curl -d '{"role":"math","cmd":"sum","left":1,"right":2}' http://localhost:10101/act

Оба способа вернут вам результат:

{"answer":3}

Далее вам потребуется клиент для микросервиса math-client.js:

require('seneca')()
  .client()
  .act('role:math,cmd:sum,left:1,right:2', console.log)

Откройте новый терминал и выполните этот скрипт:

null { answer: 3 } { id: '7uuptvpf8iff/9wfb26kbqx55',
  accept: '043di4pxswq7/1483589685164/65429/3.2.2/-',
  track: undefined,
  time:
   { client_sent: '0',
     listen_recv: '0',
     listen_sent: '0',
     client_recv: 1483589898390 } }
```В Seneca мы создаем микросервис с помощью метода `seneca.listen`, а затем используем метод `seneca.client` для взаимодействия с этим микросервисом. В приведенном выше примере используются значения по умолчанию конфигурации Seneca, такие как использование протокола HTTP для прослушивания порта `10101`. Однако методы `seneca.listen` и `seneca.client` принимают следующие параметры для настройки:

- **address**: Адрес сервера.
- **port**: Номер порта.
- **protocol**: Протокол связи (например, `http`, `https`).

Метод `seneca.listen` также может принимать объект конфигурации, который позволяет настроить различные параметры прослушивания. Метод `seneca.client` используется для создания клиента, который будет взаимодействовать с микросервисом, и также принимает параметры для настройки соединения.- `port`: опциональное число, представляющее номер порта;
- `host`: опциональная строка, представляющая имя хоста или IP-адрес;
- `spec`: опциональный объект, содержащий полную спецификацию;

> **Примечание**: На системах Windows, если значение `host` не указано, по умолчанию будет использоваться `0.0.0.0`, что не имеет практического применения. Вы можете установить `host` равным `localhost`.

Если порты и хосты совпадают, то `client` может общаться с `listen`:

- seneca.client(8080) → seneca.listen( Yöntem:8080)
- seneca.client(8080, '192.168.0.2') → seneca.listen(8080, '192.168.0.2')
- seneca.client({ port: 8080, host: '192.168.0.2' }) → seneca.listen({ port: 8080, host: '192.168.0.2' })

Функция **безопасной передачи данных**, предоставляемая Seneca, позволяет вам сосредоточиться на бизнес-логике без необходимости знать детали передачи сообщений или какие сервисы получают эти сообщения. Эти детали можно указывать в коде или конфигурации сервиса, например, код плагина `math.js` никогда не требует изменения при изменении способа передачи данных. Хотя протокол `HTTP` очень удобен, но он не всегда является лучшим выбором. Другой часто используемый протокол — это `TCP`. Мы можем легко использовать протокол `TCP`, чтобы передавать данные. Примеры двух файлов:

[math-service-tcp.js](https://github.com/pantao/getting-started-seneca/blob/master/math-service-tcp.js):

```javascript
require('seneca')()
  .use('math')
  .listen({type: 'tcp'})

math-client-tcp.js:


Исправлено:

  • Yöntem:8080 заменено на `seneca.listen(8080)````javascript require('seneca')() .client({type: 'tcp'}) .act('role:math,cmd:sum,left:1,right:2', console.log)

По умолчанию, `client/listen` не указывают, какие сообщения отправляются куда. Если локально определены шаблоны, они будут отправлены в локальные шаблоны; в противном случае все сообщения будут отправлены на сервер. Вы можете использовать конфигурацию для определения, какие сообщения отправляются на конкретные службы. Это можно сделать с помощью параметра `pin`.

Давайте создадим приложение, которое будет отправлять все сообщения с ролью `role:math` через `TCP` на службу, а остальные сообщения — на локальную машину.

[math-pin-service.js](https://github.com/pantao/getting-started-seneca/blob/master/math-pin-service.js):

```javascript
require('seneca')()

  .use('math')

  // слушаем сообщения с ролью math
  // важно: должно совпадать с клиентом
  .listen({ type: 'tcp', pin: 'role:math' })

math-pin-client.js:

require('seneca')()

  // локальный шаблон
  .add('say:hello', function (msg, respond) { respond(null, { text: "Привет!" }) })

  // отправляем шаблон role:math на сервис
  // обратите внимание: должно совпадать с сервером
  .client({ type: 'tcp', pin: 'role:math' })

  // удалённая операция
  .act('role:math,cmd:sum,left:1,right:2', console.log)

  // локальная операция
  .act('say:hello', console.log)

Вы можете настроить вывод журнала с использованием различных фильтров для отслеживания потока сообщений. Поддерживаемые конфигурации включают следующие параметры:- date-time: время создания записи журнала;

  • seneca-id: идентификатор процесса Seneca;
  • level: уровень логгирования (DEBUG, INFO, WARNING, ERROR, FATAL);
  • type: тип записи, например act, plugin;
  • plugin: имя плагина, если действие не относится к плагину, то используется root$;
  • case: событие записи: IN, ADD, OUT и т.д.;
  • action-id/transaction-id: идентификатор трассировки, который остаётся постоянным в сети;
  • pin: шаблон действия;
  • message: входящий/выходящий контент сообщения. Если вы запустите указанный выше процесс с использованием --seneca.log.all, то будут отображены все логи. Если же вам требуется видеть только логи плагина math, можно использовать следующий способ запуска сервиса:
node math-pin-service.js --seneca.log=plugin:math

Интеграция веб-сервиса

Seneca не является веб-фреймворком. Однако, вам всё равно потребуется соединить его с вашими веб-службами API. Важно помнить, что не следует открывать внутренние модели поведения внешним пользователям — это плохая практика безопасности. Вместо этого следует определять набор моделей API, таких как использование атрибута role: api. Это позволит вам связывать эти модели с вашими внутренними микросервисами.

Ниже представлен пример нашего определения файла api.js:

module.exports = function api(options) {

  var validOps = { sum: 'sum', product: 'product' };
```  этот.add('роль:api,path:calculate', функция (msg, respond) {
    var операция = msg.args.params.operation
    var левый = msg.args.query.left
    var правый = msg.args.query.right
    этот.act('роль:математика', {
      cmd:   validOps[операция],
      левый:  левый,
      правый: правый,
    }, respond)
  })

  этот.add('init:api', функция (msg, respond) {
    этот.act('роль:веб',{routes:{
      префикс: '/api',
      пин: 'роль:api,path:*',
      карта: {
        calculate: { GET:true, суффикс:'/{operation}' }
      }
    }}, respond)
  })

}

Затем мы используем hapi в качестве веб-фреймворка и создаем hapi-app.js приложение:

const Hapi = require('hapi');
const Seneca = require('seneca');
const SenecaWeb = require('seneca-web');

const конфиг = {
  адаптер: require('seneca-web-adapter-hapi'),
  контекст: (() => {
    const сервер = новый Hapi.Server();
    сервер.connection({
      порт: 3000
    });

    сервер.route({
      путь: '/routes',
      метод: 'get',
      хэндлер: (запрос, ответ) => {
        const маршруты = сервер.table()[0].table.map(маршрут => {
          return {
            путь: маршрут.path,
            метод: маршрут.method.toUpperCase(),
            описание: маршрут.settings.description,
            тэги: маршрут.settings.tags,
            vhost: маршрут.settings.vhost,
            cors: маршрут.settings.cors,
            jsonp: маршрут.settings.jsonp,
          }
        })
        ответ(маршруты)
      }
    });
```    вернуть сервер;
  })();

};

const сенека = Seneca()
  .use(SenecaWeb, конфиг)
  .use('математика')
  .use('апи')
  .готов(() => {
    const сервер = сенека.export('web/контекст')();
    сервер.start(() => {
      сервер.log('сервер запущен по адресу:' + сервер.info.uri);
    });
  });

---

После запуска `hapi-app.js`, обратитесь к <http://localhost:3000/routes>, чтобы увидеть следующую информацию:

```json
[
  {
    "путь": "/routes",
    "метод": "GET",
    "cors": false
  },
  {
    "путь": "/api/calculate/{operation}",
    "метод": "GET",
    "cors": false
  }
]
```Это указывает на то, что мы успешно обновили маршруты в приложении `hapi`. Обращение к <http://localhost:3000/api/calculate/sum?left=1&right=2> приведёт к получению ответа:

```json
{"response":3}

В примере выше мы непосредственно загружаем плагин math в экземпляр seneca. Однако более целесообразно использовать метод client, как показано в файле hapi-app-client.js:

...
const seneca = Seneca()
  .use(SenecaWeb, config)
  .use('api')
  .client({type: 'tcp', pin: 'role:math'})
  .ready(() => {
    const server = seneca.export('web/context')();
    server.start(() => {
      server.log('server started at address:' + server.info.uri);
    });
  });

Мы не регистрируем плагин math, а используем метод client, отправляющий запрос role:math сервису math-pin-service.js через соединение tcp.

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

В методе инициализации используется операция с ролью role:web, которая определяет правила соответствия URL-адреса и модели. У этого есть следующие параметры:

  • prefix: префикс URL
  • pin: набор моделей, которые требуется отобразить
  • map: список свойств шаблонов pin, которые будут использоваться в качестве конечной точки URL

Ваш URL будет начинаться с /api/.роль: api, путь: * этот пин указывает на соответствие любых моделей с ключом роль="api", а также с определенным значением path. В данном случае единственная модель, удовлетворяющая этому условию — роль: api, путь: расчёт. map свойство является объектом, который имеет свойство calculate, соответствующий URL начинается с: /api/calculate.

При этом, значение calculate представляет собой объект, указывающий, что метод HTTP GET разрешён, и URL должен иметь параметризованный суффикс (как в правилах маршрутизации Hapi).

Поэтому ваш полный адрес будет выглядеть как /api/calculate/{operation}.

Значения других сообщений будут получены из объекта запроса URL или JSON-тела запроса. В данном примере, так как используется метод GET, то нет тела запроса.

SenecaWeb будет использовать msg.args для описания запроса, который включает:

  • body: часть payload HTTP-запроса;
  • query: строки запроса;
  • params: параметры пути запроса.

Теперь запустите микрослужбу, которую мы создали ранее:

node math-pin-service.js --seneca.log=plugin:math

И затем запустите наше приложение:

node hapi-app.js --seneca.log=plugin:web,plugin:api

Перейдите по следующим адресам:

Данные и их хранениеДействительно работающая система должна обеспечивать возможность хранения данных. В Seneca вы можете выполнять любую операцию с использованием любого типа базы данных, но почему бы не воспользоваться мощью паттернов и микрослужб, чтобы сделать разработку проще?Паттерны также позволяют отложить споры относительно данных микрослужбы, таких как вопрос о том, должна ли служба "владеть" данными или иметь доступ к общему пулу данных. Паттерны позволяют вам переопределить конфигурацию системы в любой момент времени.

seneca-entity предоставляет простое абстрактное представление данных (ORM), основанное на следующих действиях:

  • load: загрузка сущности по её идентификатору;
  • save: создание или обновление сущности (если предоставлен идентификатор);
  • list: вывод всех сущностей, удовлетворяющих условиям запроса;
  • remove: удаление сущности по её идентификатору.

Соответствующие им шаблоны команд:

  • load: role:entity,cmd:load,name:<entity-name>;
  • save: role:entity,cmd:save,name:<entity-name>;
  • list: role:entity,cmd:list,name:<entity-name>;
  • remove: role:entity,cmd:remove,name:<entity-name>.

Любой плагин, реализующий эти паттерны, может использоваться для предоставления доступа к базе данных (например, MySQL).

Когда сохранение данных осуществляется с помощью одного и того же механизма, что и все остальное, разработка микросервисов становится проще, а этот механизм — это паттерн совпадения сообщений.Поскольку прямое использование паттерна сохранения данных может стать однообразным, объекты seneca также предоставляют более знакомый интерфейс в стиле ActiveRecord. Чтобы создать объект записи, вызовите метод seneca.make(). Объект записи имеет методы load$, save$, list$ и remove$ (все методы имеют суффикс $, чтобы избежать конфликтов с полями данных), а сами поля данных являются просто свойствами объекта.Установите seneca-entity через npm, затем используйте метод seneca.use() для загрузки его в ваш экземпляр seneca.

Теперь давайте создадим простую сущность данных, которая будет хранить детали книги.

Файл book.js

const seneca = require('seneca')();
seneca.use('basic').use('entity');

const book = seneca.make('book');
book.title = 'Действие в Seneca';
book.price = 9.99;

// Отправка сообщения role:entity, cmd:save, name:book
book.save$(console.log);

В примере выше мы также использовали seneca-basic, который является зависимостью для seneca-entity.

После выполнения указанного выше кода вы можете видеть следующий лог:

❯ node book.js
null $-/-/book;id=byo81d;{title:Действие в Seneca, price:9.99}

Внутри Seneca есть mem-store, что позволяет нам выполнять полную операцию сохранения данных без использования каких-либо других баз данных (хотя, это не является истинной персистентностью).

Поскольку сохранение данных всегда происходит с использованием одного и того же набора шаблонов сообщений, можно очень легко взаимодействовать с базой данных, например, вы можете использовать MongoDB во время разработки, а после завершения разработки использовать PostgreSQL в продакшене.Теперь давайте создадим простой интернет-магазин книг, где мы можем быстро добавлять новые книги, получать информацию о книге и совершать её покупку.book-store.js```javascript module.exports = function(options) {

// Из базы данных выбирается книга с ID msg.id, мы используем метод load$ this.add('role:store, get:book', function(msg, respond) { this.make('book').load$(msg.id, respond); });

// В базу данных добавляется новая книга со значениями msg.data, мы используем метод data$ this.add('role:store, add:book', function(msg, respond) { this.make('book').data$(msg.data).save$(respond); });

// Создается новый заказ на покупку (в реальных системах это часто происходит при нажатии кнопки "Купить" на странице товара), // сначала выбирается книга с ID msg.id. Если выбор завершается ошибкой, то она возвращается сразу же, // если же все хорошо, то информация книги копируется в сущность purchase и сохраняется как новый заказ. // После этого отправляется сообщение role:store,info:purchase (хотя ответ на него нам не требуется), // которое информирует всю систему о том, что был создан новый заказ, но нас не интересует, кто его получит. this.add('role:store, cmd:purchase', function(msg, respond) { this.make('book').load$(msg.id, function(err, book) { if (err) return respond(err);

  this
    .make('purchase')
    .data$({
      когда: Date.now(),
      bookId: book.id,
      название: book.title,
      цена: book.price,
    })
    .save$(function(err, purchase) {
      if (err) return respond(err);

      this.act('role:store,info:purchase', {
        заказ: purchase
      });
      respond(null, purchase);
    });
});

}); } ``` // Наконец, реализуется режим role:store, info:purchase, который просто выводит информацию, // объект `seneca.log` предоставляет методы `debug`, `info`, `warn`, `error`, `fatal` для записи логов различных уровней. this.add('role:store, info:purchase', function(msg, respond) { this.log.info('заказ:', msg.заказ); respond(); }); };Далее можно создать простой тестовый скрипт для проверки работы программы:

boot-store-test.js```javascript // Используйте встроенный модуль assert Node const assert = require('assert');

const seneca = require('seneca')() .use('basic') .use('entity') .use('book-store') .error(assert.fail);

// Добавьте книгу addBook();

function addBook() { seneca.act( 'role:store,add:book,data:{title:Action in Seneca,price:9.99}', function(err, savedBook) {

  this.act(
    'role:store,get:book', {
      id: savedBook.id
    },
    function(err, loadedBook) {

      assert.equal(loadedBook.title, savedBook.title);

      покупка(loadedBook);
    }
  );
}

); }

function покупка(book) { seneca.act( 'role:store,cmd:purchase', { id: book.id }, function(err, purchase) { assert.equal(purchase.bookId, book.id); } ); }


Выполнение этого теста:

```bash
❯ node book-store-test.js
["purchase",{"entity$":"-/-/purchase","when":1483607360925,"bookId":"a2mlev","title":"Action in Seneca","price":9.99,"id":"i28xoc"}]

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

book-store-stats.js

const stats = {};

require('seneca')()
  .add('role:store,info:purchase', function(msg, respond) {
    const id = msg.purchase.bookId;
    stats[id] = stats[id] || 0;
    stats[id]++;
    console.log(stats);
    respond();
  })
  .listen({
    port: 9003,
    host: 'localhost',
    pin: 'role:store,info:purchase'
  });

Затем обновите файл book-store-test.js:

const seneca = require('seneca')()
  .use('basic')
  .use('entity')
  .use('book-store')
  .client({port:9003,host: 'localhost', pin:'role:store,info:purchase'})
  .error(assert.fail);

Функция покупка переименована в purchase.

function purchase(book) {
  seneca.act(
    'role:store,cmd:purchase', {
      id: book.id
    },
    function(err, purchase) {
      assert.equal(purchase.bookId, book.id);
    }
  );
}
```Теперь при появлении нового заказа он будет отправлен в сервис мониторинга заказов.

## Интеграция всех служб вместе

После выполнения всех этих шагов у нас уже есть четыре службы:

- [book-store-stats.js](https://github.com/pantao/getting-started-seneca/blob/master/book-store-stats.js) : собирает информацию о заказах в книжном магазине;
- [book-store-service.js](https://github.com/pantao/getting-started-seneca/blob/master/book-store-service.js) : предоставляет функциональность, связанную с книжным магазином;
- [math-pin-service.js](https://github.com/pantao/getting-started-seneca/blob/master/math-pin-service.js) : предоставляет некоторые математические службы;
- [app-all.js](https://github.com/pantao/getting-started-seneca/blob/master/app-all.js) : веб-сервис `book-store-stats` и `math-pin-service` уже существуют, поэтому можно сразу запустить их:

```bash
node math-pin-service.js --seneca.log.all
node book-store-stats.js --seneca.log.all

Теперь нам нужен сервис book-store-service :

require('seneca')()
  .use('basic')
  .use('entity')
  .use('book-store')
  .listen({
    port: 9002,
    host: 'localhost',
    pin: 'role:store'
  })
  .client({
    port: 9003,
    host: 'localhost',
    pin: 'role:store,info:purchase'
  });

Этот сервис принимает любое сообщение с ролью role:store, но одновременно отправляет все сообщения с ролью role:store,info:purchase в сеть, всегда помните, что конфигурация пинов для клиентов и прослушивания должна быть полностью одинаковой.

Теперь мы можем запустить этот сервис:

node book-store-service.js --seneca.log.all

Затем создайте наш app-all.js. В первую очередь скопируйте файл api.js в [api-all.js](_url_), это будет наш API.```javascript module.exports = function api(options) {

var validOps = { sum: 'sum', product: 'product' }

this.add('role:api,path:calculate', function(msg, respond) { var operation = msg.args.params.operation; var left = msg.args.query.left; var right = msg.args.query.right; this.act('role:math', { cmd: validOps[operation], left: left, right: right }, respond); });

this.add('role:api,path:store', function(msg, respond) { let id = null; if (msg.args.query.id) id = msg.args.query.id; if (msg.args.body.id) id = msg.args.body.id;

const operation = msg.args.params.operation;
const storeMsg = {
  role: 'store',
  id: id
};
if ('get' === operation) storeMsg.get = 'book';
if ('purchase' === operation) storeMsg.cmd = 'purchase';
this.act(storeMsg, respond);

});

this.add('init:api', function(msg, respond) { this.act('role:web', { routes: { prefix: '/api', pin: 'role:api,path:*', map: { calculate: { GET: true, suffix: '/{operation}' }, store: { GET: true, POST: true, suffix: '/{operation}' } } } }, respond); }); };


## Последнее

Последней частью является [app-all.js](https://github.com/pantao/getting-started-seneca/blob/master/app-all.js):

```javascript
const Hapi = require('hapi');
const Seneca = require('seneca');
const SenecaWeb = require('seneca-web');

const config = {
  adapter: require('seneca-web-adapter-hapi'),
  context: (() => {
    const server = new Hapi.Server();
    server.connection({
      port: 3000
    });

    server.route({
      path: '/routes',
      method: 'GET',
      handler: (request, reply) => {
        const routes = server.table()[0].table.map(route => {
          return {
            path: route.path,
            method: route.method.toUpperCase(),
            description: route.settings.description,
            tags: route.settings.tags,
            vhost: route.settings.vhost,
            cors: route.settings.cors,
            jsonp: route.settings.jsonp
          };
        });
        reply(routes);
      }
    });
})();
```    return сервер;
  })()
};

const seneca = Seneca()
  .use(SenecaWeb, config)
  .use('basic')
  .use('entity')
  .use('math')
  .use('api-all')
  .client({
    тип: 'tcp',
    пин: 'роль:math'
  })
  .client({
    порт: 9002,
    хост: 'localhost',
    пин: 'роль:store'
  })
  .готов(() => {
    const сервер = senica.export('web/context')();
    сервер.start(() => {
      сервер.log('сервер запущен на: ' + сервер.info.uri);
    });
  });

// Создание примерной книги
seneca.act(
  'роль:store,добавить:книгу', {
    данные: {
      название: 'Действие в Seneca',
      цена: 9.99
    }
  },
  console.log
)

Запустите сервис:

node app-all.js --seneca.log.all

Из консоли вы можете видеть следующее сообщение:

null $-/-/книга;id=0r7mg7;{название:Действие в Seneca,цена:9.99}

Это указывает на успешное создание книги с ID 0r7mg7. Теперь можно получить детали этой книги по её ID, переходя по адресу http://localhost:3000/api/store/get?id=0r7mg7 (ID случайный, поэтому ваш ID может отличаться).

Ссылка http://localhost:3000/routes позволяет просмотреть все маршруты.

Далее можно создать новый заказ покупки:

curl -d '{"id":"0r7mg7"}' -H "content-type:application/json" http://localhost:3000/api/store/purchase
{"когда":1483609872715,"книгаИд":"0r7mg7","название":"Действие в Seneca","цена":9.99,"ид":"8suhf4"}

При доступе к http://localhost:3000/api/calculate/sum?left=2&right=3 можно получить {"ответ":5}.

Лучшие практики структурирования приложений Seneca

Рекомендовано делать так- Разделите бизнес-логику от выполнения, поместив её в отдельные плагины, такие как различные модули Node.js, различные проекты или даже разные файлы внутри одного проекта;

  • Используйте скрипты выполнения для написания вашего приложения, не бойтесь использовать различные скрипты для различных контекстов; они должны выглядеть короткими, например:```javascript var НЕКОТАННАЯ_КОНФИГУРАЦИЯ = process.env.НЕКОТАННАЯ_КОНФИГУРАЦИЯ || 'некоторое-значение-по-умолчанию';

require('seneca')({ некоторые_опции: 123 })

// Уже существующие плагины Seneca .use('сообщественный-плагин-0') .use('сообщественный-плагин-1', { некая_конфигурация: НЕКОТАННАЯ_КОНФИГУРАЦИЯ }) .use('сообщественный-плагин-2')

// Плагины бизнес-логики .use('проектный-модуль-плагина') .use('../репозиторий-плагинов') .use('./lib/локальный-плагин')

.слушает( ... ) .клиент( ... )

.готов( function() { // Самостоятельный скрипт после успешной инициализации Seneca });


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

### Не рекомендовано делать так

-   Объединять запуск и инициализацию Seneca-приложений с запуском и инициализацией других фреймворков; всегда помните, что следует поддерживать простоту транзакций;
-   Передавать экземпляр Seneca как переменную повсюду.

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/pantao-getting-started-seneca.git
git@api.gitlife.ru:oschina-mirror/pantao-getting-started-seneca.git
oschina-mirror
pantao-getting-started-seneca
pantao-getting-started-seneca
master