Коллеги, интересует вопрос об использовании архитектурного паттерна "глобальный контекст" в веб-серверном приложении (поправьте меня, если есть более подходящее название).
Я имею в виду readonly объект, в который упаковываются часто используемые сервисы, для которых не нужно создавать отдельные инстансы под каждый пользовательский запрос. В моем случае это конфиг, экземпляры провайдеров БД, мейл-сервиса, шаблонизатора, а так же логгер.
Когда я делал свой первый пет-проект на чистом JS, использование такого объекта было не слишком удобно, т.к. приходилось держать в голове его структуру. С переходом на TS эта проблема решается подсказками IDE.
async function GlobalContextConstructor(): Promise<GlobalContext> {
// Собираю объект глобального контекста
}
GlobalContextConstructor()
.then(g => {
// Затем этот объект передается в конструктор сервера
const server = new FastifyServer(g);
server.start();
})
.catch(err => console.log(err.message));
Теперь объект доступен экземпляру приложения и я могу передавать его в контроллеры, а затем и во все "рабочие" классы. Например, за подготовку html для ответа на get-запрос /about у меня отвечает AboutPage:
// В контроллере создается экземпляр этого класса, в конструктор передается глобальный контекст и контекст http-запроса
const page: IPage = new AboutPage(g, ctx);
const html: string = page.render();
Внутри классов не требуется каждый раз создавать инстанс логгера или импортировать конфиг, т.к. достаточно обратиться к this.g:
this.g.logger.trace('Something happened');
Все это нормально работает, особенно в связке с TS, но меня интересуют две вещи:
1) не является ли такой подход антипаттерном? Есть ощущение, что он противоречит идее модульности.
2) улучшается ли производительность за счет того, что экземпляры классов не пересоздаются? Например, не нужно поднимать инстанс мейлера перед отправкой письма, т.к. экземпляр этой службы создается однократно при старте приложения.
Второй вопрос я задаю потому, что предполагаю наличие в интерпретаторе каких-то кешей для таких случаев.