Package Exports
- @lskjs/module
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@lskjs/module) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@lskjs/module
Манифест модульной архитектуры
Основные цели
- модульность
- асинхронность
Module
- Основополагаюший элемент архитектуры
- Из модулей будет собираться приложение, у модулей могут быть подмодули
Modules stages
- null - после конструктора
- init - стадия инициализации, по сути это асинхронный конструктор, нужно сделать все необходимые require. на этой стадии работаем внутри себя, остальные сестренские объекты (модели, модули, сокеты, файлы) еще не существуют.
- run - стадия запуска, делаем все необоходимые внешние подключения, тут можно обращяться к внешний модулям системы: модели, модули
- stop - выключаем модуль, или для остановки приложения, или для переконфигурирования, или просто чтобы выключить соединения и очистить память
Modules stage statuses
- null - хуй знает что с ним, еще не запускался
- initing - стадия инициализации
- inited - уже инициализировался
- runing - стадия запуска
- runned - уже запустился
- stopping - стадия остановки
- stoped - уже остановился
Modules public methods
- .init() - как консструктор но асинхронный, делает необходимые импорты и устанвливет правильное состояние модуля
- .run() - запускает все процессы в модуле
- .start() - init and run and some magic?
Как я хочу использовать модули
Просто взять и юзать
const theModule = new TheModule();
await theModule.start()Прокидывать props в конструктор
const theModule = new TheModule({
mode: 'private',
config: {
value: 1
},
});
await theModule.start()
theModule.mode === 'private'
theModule.config.value === 1Прокидывать подмодули, разным образом
Как props в конструкторе
- как класс через require
- как промис через асинхронный import
- как функцию при вызове которой будет происхолить асинхрронный import
const theModule = new TheModule({
modules: {
permit: require('@lskjs/permit/server'),
upload: import('@lskjs/upload/server'),
mailer: () => import('@lskjs/mailer/server'),
}
});
await theModule.start()Прокидывать подмодули с параметрами конструктора
const theModule = new TheModule({
modules: {
permit: [require('@lskjs/permit/server'), { value: 1 }],
upload: [import('@lskjs/upload/server'), { value:2 } ],
mailer: () => [import('@lskjs/mailer/server', { value: 3 })],
}
});
await theModule.start()как field в классе
class TheModule extends Module {
modules = {
permit: require('@lskjs/permit/server'),
upload: import('@lskjs/upload/server'),
mailer: () => import('@lskjs/mailer/server'),
}
}как расширения getter'a в классе
class TheModule extends Module {
async getModules() {
return {
...await super.getModules(),
permit: require('@lskjs/permit/server'),
upload: import('@lskjs/upload/server'),
mailer: () => import('@lskjs/mailer/server'),
}
}
}как создаем модуль
class TheModule extends Module {
name = 'TheModule2'; // можно переопределить имя, чтобы это имя было в логгере например
async init() {
await super.init();
// делаем то, что нам нужно для инициализации
this.client = new Telegraf(this.config)
}
async run() {
await super.run();
// делаем то, что нам нужно для запуска модуля
this.client.launch();
}
}Еще не решенные вопросы
- проблема провайдеров
- у модуля должен быть логгер
- у модуля должен быть event emitter
- проблема конфигов, централизованных конфигов
- проблема моделей и модуля db (в init нужен mongoose)
модуль подмодули провайдеры модели ee
getModules
modules={}
fropmt
TheModule
new TheModule(, {providers: {}})
как прокидывать конфиги дальше?
Гипотетическое использование
class Module {
async getModules() {
return this.modules;
}
async init() {
this.name = this.constructor.name;
console.log('init', this.constructor.name)
}
async run() {
console.log('run', this.name)
}
}
class TheModule extends Module {
async run() {
await super.run();
console.log('run2', this.name)
}
}
const m = new TheModule()
m.run()
console.log(m.name === 'TheModule');
Всякие ссылки
https://v8.dev/blog/fast-async
При переинициализации мы должны получать тот же самый объект
=======================================
Модули и конфиги
Case 0 – empty config
class SomeModule extends Module { }
const some = await SomeModule.create();
some.config === {};
Case 1 – default config
class SomeModule extends Module {
defaultConfig = {
a: 1,
};
}
const some = await SomeModule.create();
some.config === {
a: 1,
};Case 2 - config while creation
class SomeModule extends Module {}
const some = await SomeModule.create({
config: {
a: 11,
}
});
some.config === {
a: 11,
};Case 3 - merging default and top config
class SomeModule extends Module {
defaultConfig = {
a: 1,
b: 2,
};
}
const some = await SomeModule.create({
config: {
a: 11,
c: 33
}
})
some.config === {
a: 11,
b: 2,
c: 33,
}Case 4 - merging default and top config
class SomeModule extends Module {
config = {
a: 1,
b: 2,
};
}
const some = await SomeModule.create({
config: {
a: 2,
c: 3
}
})
some.config === {
a: 2,
b: 2,
c: 3,
}
Case 5 - async config from db
class SomeModule extends Module {
config = {
a: 1,
b: 2,
};
async getConfigFromDb() {
return {
fields: true,
from: true,
db: true,
}
}
async getConfig() {
const localConfig = await super.getConfig();
const dbConfig = await this.getConfigFromDb().catch(err => {
this.log.error('something wrong', err);
if (neededFallApp) throw err
// throw err;
return {}
})
return {
...localConfig,
...dbConfig,
};
}
}
const some = await SomeModule.create({
config: {
a: 11,
c: 33
}
})
// if all ok
some.config === {
a: 11,
b: 2,
c: 33,
fields: true,
from: true,
db: true,
}
// if all error not ok
some.config === {
a: 11,
b: 2,
c: 33,
}Case 6 - deep config merge
class Module {
async getConfig() {
return {
...this.defaultConfig,
...this.config,
}
}
}
class Module {
async getConfig() {
return {
...this.defaultConfig,
...this.config,
...this.__config,
deep: {
...this.defaultConfig.deep,
...this.config.deep,
...this.__config.deep,
deepest: {
...this.defaultConfig.deep.deepest,
...this.config.deep.deepest,
...this.__config.deep.deepest,
}
}
}
}
// or
async getConfig() {
return mergeDeep(
this.defaultConfig,
this.config,
this.__config,
}
}
defaultConfig = {
a: 1,
b: 2,
};
}
const some = await SomeModule.create({
config: {
a: 2,
c: 3
}
})
some.config === {
a: 2,
b: 2,
c: 3,
}