You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

对象多实例跨文件共享的设计模式选型及MongoClient配置疑问

MongoDB多实例共享与实现方案解析

现有场景需创建两个MongoClient实例:一个连接本地MongoDB服务器,另一个连接远程MongoDB,当前将这两个实例定义在database-connector.js文件中,供gui.js、model.js等文件导入使用。同时有三个疑问:

  1. 当对象需多次创建并在不同文件间共享时,应采用何种设计模式?
  2. 当前的MongoClient配置方式是否正确?
  3. 正确的实现方案是什么?

原代码片段

database-connector.ts

class CatchError {
    static error: string | unknown;

    static save(error: string | unknown) {
        this.error = error;

        const re = new RegExp('ECONNREFUSED', 'i')

        if (re.test(error.message)) {
            // fs.write('system.error.log')
        }
    }
}


class DBClient {
    client: MongoClient;

    constructor(url: string, config: Partial<MongoClientOptions>) {
        this.client = new MongoClient(url, config)
    }

    async connect() {
        try {
            this.client = await this.client.connect();
            return this.client;
        } catch(error) {
            CatchError.parse(error)
        }
    }
}
// for entire data application 
export const localDBClient = initializeDBConnection("mongodb://localhost");

// only for authentication to access application
export const remoteDBClient = initializeDBConnection("mongodb+srv://se132.aws-slcuster.com");

gui.js

import { remoteDBClient } from "database-connector.js";
// use client // 

model.js

import { localDBClient } from "database-connector.js";
// use client //

问题解答

1. 适用的设计模式

当需要在多个文件间共享固定数量的对象实例时,**多例模式(Multiton)**是最优选择——它是单例模式的扩展,允许类根据唯一标识创建并维护多个实例,避免重复初始化资源(比如数据库连接池)。如果只需要单一实例,则用单例模式即可。

2. 当前配置方式的问题

当前实现存在多处错误和隐患:

  • 未定义核心函数:代码中直接调用initializeDBConnection但未实现,会直接报错。
  • 方法调用错误:CatchError类只有save方法,却调用了不存在的parse方法。
  • 异步逻辑处理不当:如果initializeDBConnection是异步操作,导出的localDBClient/remoteDBClient会是Promise对象,其他文件直接使用会出错。
  • 静态错误存储风险:CatchError用静态属性存错误,异步场景下会被覆盖,无法保留历史错误。
  • 连接逻辑不严谨:DBClient的connect方法会覆盖原有client实例,且未处理连接失败后的抛出逻辑,导致调用方无法感知错误。

3. 正确的实现方案

以下是修正后的完整实现,基于多例模式,同时解决异步连接、错误处理等问题:

database-connector.ts
import { MongoClient, MongoClientOptions } from 'mongodb';
import fs from 'fs/promises';

class CatchError {
    // 改用实例属性存储错误,避免静态属性的共享冲突
    error: string | unknown;

    save(error: string | unknown) {
        this.error = error;
        const re = new RegExp('ECONNREFUSED', 'i');
        // 异步写入日志,不阻塞主线程
        if (error instanceof Error && re.test(error.message)) {
            fs.appendFile('system.error.log', `${new Date().toISOString()}: ${error.message}\n`)
                .catch(logErr => console.error('写入错误日志失败:', logErr));
        }
    }
}

class DBClient {
    // 用Map存储不同标识的实例,实现多例模式
    private static instances: Map<string, DBClient> = new Map();
    client: MongoClient | null = null;
    private catchError = new CatchError();

    // 私有构造函数,禁止外部直接实例化
    private constructor(private url: string, private config: Partial<MongoClientOptions> = {}) {}

    // 获取或创建实例的静态入口
    static getInstance(key: string, url: string, config: Partial<MongoClientOptions> = {}): DBClient {
        if (!DBClient.instances.has(key)) {
            DBClient.instances.set(key, new DBClient(url, config));
        }
        return DBClient.instances.get(key)!;
    }

    // 连接数据库,复用已有连接
    async connect(): Promise<MongoClient> {
        // 检查现有连接是否可用,避免重复创建
        if (this.client && this.client.topology?.isConnected()) {
            return this.client;
        }
        try {
            const client = new MongoClient(this.url, this.config);
            this.client = await client.connect();
            console.log(`成功连接到数据库: ${this.url}`);
            return this.client;
        } catch (error) {
            this.catchError.save(error);
            throw error; // 抛出错误让调用方处理,避免静默失败
        }
    }

    // 便捷方法:直接获取数据库实例
    async getDatabase(dbName: string) {
        const client = await this.connect();
        return client.db(dbName);
    }
}

// 初始化两个全局实例,供其他文件导入
export const localDBClient = DBClient.getInstance('local', 'mongodb://localhost');
export const remoteDBClient = DBClient.getInstance('remote', 'mongodb+srv://se132.aws-slcuster.com');
gui.js(使用示例)
import { remoteDBClient } from "./database-connector.js";

async function initAuth() {
    try {
        const db = await remoteDBClient.getDatabase('auth_db');
        // 执行认证相关操作
        const adminUser = await db.collection('users').findOne({ username: 'admin' });
        console.log('查询到管理员:', adminUser);
    } catch (err) {
        console.error('认证数据库操作失败:', err);
    }
}

initAuth();
model.js(使用示例)
import { localDBClient } from "./database-connector.js";

async function fetchAppData() {
    try {
        const db = await localDBClient.getDatabase('app_data');
        // 获取前10条记录
        const records = await db.collection('records').find({}).limit(10).toArray();
        console.log('查询到数据:', records);
    } catch (err) {
        console.error('本地数据库操作失败:', err);
    }
}

fetchAppData();

关键优化点

  • 多例模式保障实例唯一性:用Map存储不同标识的实例,确保每个数据库连接只初始化一次。
  • 异步连接安全处理:通过connect方法确保连接完成后再返回实例,避免导出Promise导致的使用问题。
  • 错误处理更健壮:改用实例化的CatchError存储错误,异步写入日志,同时抛出错误让调用方感知。
  • 连接复用:检查现有连接状态,避免重复创建连接池,提升性能。

内容的提问来源于stack exchange,提问作者imagesck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 15:48:05