Сиквел с синглтоном

Я работаю с Sequelize в node.js, и идея состоит в том, чтобы использовать шаблон Singleton.

Читайте о том, как node работает с кешированием модулей, и некоторые примеры синглтонов

Мой файл на данный момент:

const DBManager = (function () {

  // Instance stores a reference to the Singleton
  let instance: any;

  let db: string = null;
  let user: string;
  let password: string;
  let host: string;
  let sequelize: Sequelize.Sequelize;

  function init(bdName: string) {
    db = bdName;
    user = process.env.MYSQL_DB_USERNAME || 'root';
    password = process.env.MYSQL_DB_PASSWORD || 'root';
    host = process.env.MYSQL_DB_HOST || 'localhost';

    return {
      open: () => {
        sequelize = new Sequelize(db, user, password, {
          host: host,
          dialect: 'mysql',
          pool: {
            max: 5,
            min: 0,
            acquire: 30000,
            idle: 10000
          },
          operatorsAliases: false,
          logging: !process.env.HIDE_LOGS
        });
      },

      testConnection: () => {
        return sequelize.authenticate();
      },

      getManagerObject: () => {
        return sequelize;
      },

      close: () => {
        sequelize.close();
      }
    };
  }

  return {
    // Get the Singleton instance if one exists
    // or create one if it doesn't
    getInstance: (bd?: string) => {

      if (!instance) {
        instance = init(bd);
      }

      return instance;
    }
  };

})();

export default DBManager;

Итак, как и ожидалось, когда мне требуется этот файл в любом месте моего проекта, ссылки остаются теми же и работают должным образом.

Я не уверен, что это правильный способ реализации Singleton pattern, или есть определенный и задокументированный, потому что официальная документация ничего не говорит об этом.


person jose920405    schedule 18.07.2018    source источник


Ответы (1)


Обычно нет необходимости в явной реализации синглтона. Модули JS (в частности модули CommonJS и ES) оцениваются только один раз при нормальных обстоятельствах, экспортируемый экземпляр класса фактически является синглтоном.

Также нет необходимости в IIFE, потому что модули имеют свои собственные области видимости. Поскольку функция init не используется повторно, возможно, в ней тоже нет необходимости. Его можно упростить до:

export default {
  open: ...
  testConnection: ...
  ...
};

Эта абстракция непрактична. Там уже есть экземпляр sequelize, создание методов-оболочек для его собственных методов не служит хорошей цели.

Поскольку соединение можно использовать после его установки, имеет смысл просто экспортировать обещание соединения, аналогично тому, которое показано в этом ответ.

Если конфигурация (имя базы данных) доступна в модуле менеджера баз данных, предпочтительнее просто использовать ее на месте:

// db.js
const dbName = someConfig.db;
const sequelize = new Sequelize(dbName, ...);

export default sequelize.authenticate().then(() => sequelize);

Который используется как:

import dbConnection from './db';

dbConnection.then(sequelize => { /* all code that depends on the connection */ });

Если подключений может быть несколько или конфигурация недоступна при импорте, вместо нее экспортируется заводская функция:

// db.js
export default dbName => {
  const sequelize = new Sequelize(dbName, ...);
  sequelize.authenticate().then(() => sequelize);
}

Экземпляры Singleton естественным образом обрабатываются модулями:

// foo-db.js
import getDbConnection from './db';

export default getFooDbName().then(dbName => getDbConnection(dbName));

И используется как:

import dbConnection from './foo-db';

dbConnection.then(sequelize => { /* all code that depends on the connection */ });
person Estus Flask    schedule 18.07.2018
comment
привет @estus. Я сделал реализацию по вашему примеру, и она выглядит хорошо, но у меня есть вариант использования с некоторыми соображениями. Мой код будет работать с функцией aws-лямбда, поэтому мне нужно закрывать соединение после каждого вызова, чтобы лямбда могла успешно ответить. проблема в том, что если я закрою соединение и снова вызову ту же функцию, она уже будет закрыта, поэтому не сможет запросить БД. Вот почему в моем примере процесс подключения выполняется для функции. Таким образом, я могу вызывать его каждый раз, когда вызывается функция, поэтому соединение создается каждый раз. - person jose920405; 18.07.2018
comment
В вашем исходном коде единственное, что сохраняется как «синглтон», - это имя базы данных. Я не уверен, есть ли лучшие рецепты для AWS Lambda, но вы можете присоединиться к обоим подходам, показанным в ответе, то есть экспортировать фабричную функцию и использовать конфигурацию БД на месте: export default () => { /* get db name from config */ const sequelize = new Sequelize(dbName, ...); sequelize.authenticate().then(() => sequelize); }. Вы можете получить соединение, вызвав фабричную функцию. И закрыть его, позвонив sequelize.close(). - person Estus Flask; 19.07.2018