Дата публикации:

В этой публикации я описываю основные компоненты ядра платформы TeqFW. Teq-приложение — это прежде всего nodejs-приложение и одновременно модульная система из teq-плагинов, то есть npm-модулей.

Таким образом, задачи у Ядра платформы следующие:

Bootstrap

Точка входа в приложение — скрипт ./bin/tequila.mjs. Он фиксирует текущее положение приложения в файловой системе, загружает DI-контейнер, настраивает контейнер для работы с Ядром, достаёт из Контейнера объект TeqFw_Core_Back_App$ , инициирует его и запускает. Этот скрипт не входит в Ядро и является частью пользовательского приложения, но его код не меняется от приложения к приложению:

#!/usr/bin/env node
'use strict';
import $path from 'path';
import Container from '@teqfw/di';

const version = '0.11.0';
/* Resolve paths to main folders */
const url = new URL(import.meta.url);
const script = url.pathname;
const bin = $path.dirname(script);
const root = $path.join(bin, '..');

try {
  /* Create and setup DI container */
  /** @type {TeqFw_Di_Container} */
  const container = new Container();
  const srcCore = $path.join(root, 'node_modules/@teqfw/core-app/src');
  const srcDi = $path.join(root, 'node_modules/@teqfw/di/src');
  container.addSourceMapping('TeqFw_Core', srcCore, true, 'mjs');
  container.addSourceMapping('TeqFw_Di', srcDi, true, 'mjs');

  // Bootstrap configuration object for 'TeqFw_Core_Back_App'
  /** @type {typeof TeqFw_Core_Back_App.Bootstrap} */
  const Bootstrap = await container.get('TeqFw_Core_Back_App#Bootstrap');
  /** @type {TeqFw_Core_Back_App.Bootstrap} */
  const bootstrap = new Bootstrap({version, root});
  container.set('bootstrap', bootstrap);

  /* Request Container to construct App then run it */
  const app = await container.get('TeqFw_Core_Back_App$');
  await app.init();
  await app.run();
} catch (e) {
  console.error('Cannot create or run TeqFW application.');
  console.dir(e);
}

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

TeqFw_Core_Back_App

Инициализация

На этапе инициализации core-приложение выполняет следующее:

Запуск

Core-приложение выполняет запрошенную консольную команду (как правило, запуск web-сервера). Если никакой команды не запрашивалось, то выводится список доступных команд (пример для @flancer32/pwa_bwl) и выполнение core-приложения завершается:

Usage: tequila [options] [command]

Options:
  -h, --help      display help for command

Commands:
  app-db-reset     Reset database structures and initialize test data.
  app-db-upgrade   Backup data, drop-create tables then restore data.
  core-version     Get version of the application.
  http2-start      Start the HTTP/2 server.
  http2-stop       Stop the HTTP/2 server.
  help [command]   display help for command

Завершение

Core-приложение предоставляет метод stop() для вызова из любой команды для своего завершения (например, закрытия соединений с БД).

TODO: необходим механизм, позволяющий плагинам добавлять в этот метод свой функционал.

Логгер

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

Конфигурация

На данный момент конфигурация приложения только локальная — TeqFw_Core_Back_Config. Конфиг-объект считывает JSON из файла .cfg/local.json и через DI предоставляет доступ к конфигурационным данным для остальных объектов приложения.

Сканер плагинов

Teq-плагином считается любой npm-пакет, у которого в корне находится дескриптор ./teqfw.json. В дескрипторе, как минимум, указывается маппинг namespace’а плагина (namespace выбирается разработчиком плагина) на локальную файловую систему плагина:

{
  "autoload": {
    "ns": "Vendor_Project_Plugin",
    "path": "./src"
  }
}

Сканер плагинов (TeqFw_Core_Back_Scan_Plugin) пробегает по всем подкаталогам в ./node_modules/ в поисках teq-дескрипторов (./teqfw.json) и фиксирует найденные npm-пакеты в качестве teq-плагинов в реестре TeqFw_Core_Back_Scan_Plugin_Registry. После чего core-приложение добавляет найденные namespace’ы и их маппинг на файловую систему в DI-контейнер.

Остальные части приложения получают доступ к плагинам и их дескрипторам через этот реестр (также через DI).

Команды

По сути, core-приложение (объект TeqFw_Core_Back_App$) представляет собой обёртку над commander’ом, которая анализирует teq-плагины приложения и добавляет к нему команды, прописанные в teq-дескрипторах (./teqfw.json):

{
  "commands": [
    "TeqFw_Core_Back_Cli_Version"
  ]
}

В узле commands прописываются идентификаторы es6-модулей, экспортирующих по-умолчанию фабрики для создания таких структур (TeqFw_Core_Back_Api_Dto_Command):

class TeqFw_Core_Back_Api_Dto_Command {
  /** @type {Function} */
  action;
  /** @type {string} */
  desc;
  /** @type {string} */
  name;
  /** @type {string} */
  realm;
}

Вот код типичной фабрики для создания команды:

export default function Factory(spec) {
  // EXTRACT DEPS
  /** @type {TeqFw_Core_Defaults} */
  const DEF = spec['TeqFw_Core_Defaults$'];
  /** @type {TeqFw_Core_Back_App.Bootstrap} */
  const cfg = spec['TeqFw_Core_Back_App#Bootstrap$'];
  /** @type {Function|TeqFw_Core_Back_Api_Dto_Command.Factory} */
  const fCommand = spec['TeqFw_Core_Back_Api_Dto_Command#Factory$'];

  // DEFINE INNER FUNCTIONS
  const action = async function () {
    console.log(`Application version: ${cfg.version}.`);
  };

  // COMPOSE RESULT
  const res = fCommand.create();
  res.realm = DEF.BACK_REALM;
  res.name = 'version';
  res.desc = 'Get version of the application.';
  res.action = action;
  return res;
}

Core-приложение добавляет команды в commander при помощи DI-контейнера, который запускает соответствующую фабрику для создания действия (action) и мета-информации для commander’а:

const cmd = await container.get(`${factoryName}$`);
const fullName = (cmd.realm)
  ? `${cmd.realm}-${cmd.name}`
  : cmd.name;
commander.command(fullName)
  .description(cmd.desc)
  .action(cmd.action);

Резюме

core-плагин в TeqFW содержит функционал для обнаружения teq-плагинов в общей массе npm-модулей, а также для нахождения консольных команд в teq-плагинах и выполнения запрошенной пользователем команды. Инициирует объекты логгера и локальной конфигурации приложения и помещает их в DI-контейнер.

Image 2