TeqFW: Core

Дата публикации: 2021-06-25

В этой публикации я описываю основные компоненты ядра платформы 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 commandCommands:

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