Каков наилучший способ передачи общих переменных в отдельные модули в Node.js?
Я использую отдельные файлы маршрутизатора в качестве модулей для основного приложения и приложения auth. Я не могу получить лучший способ передать переменные (db-клиент) в маршрутизаторы. Я не хочу его жестко кодировать или передавать с помощью:
module.exports = function(app, db) {
Может быть, это лучший способ использовать регистр singleton или использовать глобальную переменную db?
Что вы испытываете с шаблонами дизайна? Какой способ является лучшим и почему?
Ответы
Ответ 1
Я нашел использование инъекции зависимости, чтобы передать вещи, чтобы быть лучшим стилем. Это действительно выглядело бы так, как будто у вас есть:
// App.js
module.exports = function App() {
};
// Database.js
module.exports = function Database(configuration) {
};
// Routes.js
module.exports = function Routes(app, database) {
};
// server.js: composition root
var App = require("./App");
var Database = require("./Database");
var Routes = require("./Routes");
var dbConfig = require("./dbconfig.json");
var app = new App();
var database = new Database(dbConfig);
var routes = new Routes(app, database);
// Use routes.
Это имеет ряд преимуществ:
- Это заставляет вас разделить вашу систему на компоненты с явными зависимостями, вместо того, чтобы скрывать зависимости где-то в середине файла, где они называют
require("databaseSingleton")
или хуже, global.database
.
- Это делает модульное тестирование очень простым: если я хочу изолировать
Routes
, я могу ввести его поддельными параметрами app
и database
и протестировать только сам код Routes
.
- Он объединяет все ваши проводки с объектным графом в одном месте, а именно корневой состав (который в этом случае является
server.js
, точкой входа в приложение). Это дает вам одно место, чтобы посмотреть, как все вписывается в систему.
Одно из лучших объяснений этого, что я видел, - это интервью с Марком Симаном, автор отличной книги Injection Dependency in.NETя > . Он применим так же, как и JavaScript, и особенно к Node.js: require
часто используется как классический сервисный локатор вместо простой системы модулей.
Ответ 2
Я предлагаю вам создать файл настроек с экземпляром db и другими вещами, которые вам нужно использовать глобально, как "singleton".
Например, у меня есть settings.js с моим клиентом redis db:
var redis = require('redis');
exports.redis = redis.createClient(6379, '127.0.0.1');
И в других нескольких модулях я включаю его:
var settings = require('./settings');
setting.redis.<...>
Много времени, включая его, у меня всегда есть один экземпляр соединения db.
Ответ 3
Вы можете сэкономить весь код шаблона для подключения ваших модулей, если вы используете инфраструктуру внедрения зависимостей
В этом ответе перечислены некоторые из них. Я также создал упрощенную схему DI здесь.
РЕДАКТИРОВАТЬ: ниже - это копия формы ответа в случае изменения страницы
require
способ управления зависимостями в Node.js, и, безусловно, он интуитивно понятен и эффективен, но он также имеет свои ограничения.
Мой совет - взглянуть на некоторые из контейнеров для инъекций Dependency, доступных сегодня для Node.js, чтобы иметь представление о том, каковы их плюсы и минусы. Некоторые из них:
Просто, чтобы назвать несколько.
Теперь реальный вопрос: что вы можете достичь с помощью контейнера Node.js DI, по сравнению с простым require
?
Плюсы:
- лучшая тестируемость: модули принимают свои зависимости в качестве входных данных
- Inversion of Control: выберите способ подключения ваших модулей, не касаясь основного кода вашего приложения.
- настраиваемый алгоритм для решения модулей: зависимости имеют "виртуальные" идентификаторы, обычно они не привязаны к пути в файловой системе.
- Улучшенная расширяемость: включена IoC и "виртуальными" идентификаторами.
- Возможны другие причудливые вещи:
- Асинхронная инициализация
- Управление жизненным циклом модуля
- Расширяемость самого контейнера DI
- Может легко реализовать абстракции более высокого уровня (например, АОП)
Минусы:
- В отличие от Node.js "опыта": не используя
require
, определенно кажется, что вы отклоняетесь от образа мышления Node.
- Отношения между зависимостью и ее реализацией не всегда явны. Зависимость может быть разрешена во время выполнения и под влиянием различных параметров. Код становится более сложным для понимания и отладки
- Более медленное время запуска
- Зрелость (на данный момент): ни один из текущих решений не является действительно популярным на данный момент, поэтому не так много обучающих программ, не экосистемы, а не битвы.
- Некоторые контейнеры DI не будут хорошо воспроизводиться с такими модулями, как Browserify и Webpack.
Ответ 4
Он полностью устарел, но вы можете использовать global
в script:
global.foo = new Foo();
в другом script:
foo.bar();
Вы также можете использовать уже существующую константу:
Object.foo = new Foo();
И здесь:
Object.foo.bar();