0

I really don't love the design pattern I've established here. It's kind of DI but not really, and the alternative is more constructor based DI, but I also hate it. Maybe it is because it is JS and that's just the way she goes, but I was hoping for a second opinion or a better pattern.

Basically I have an app, that has a database connection, and some sub modules. Those sub modules need a reference to a collection found within the database, not the database itself, but should not make their own. They are useless without that collection. The current pattern is as such

class App {
    constructor() {
        this.database = new Database();
        this.module = new Module();
    }

    async initialize() {
        await this.database.connect();
        const collections = await this.database.getCollections();
        this.module.collection = collections.somecollection;
    }

}

The alternative is

class App {
    constructor() {
        this.database = new Database();
        this.module = null;
    }

    async initialize() {
        await this.database.connect();
        const collections = await this.database.getCollections();
        this.module = new Module(collections.somecollection);
    }

}

I guess I don't love having a known object type (ie Module) set to null in the constructor, when it can easily be created. But I do love passing in the collection in this case to the module constructor. However, I cannot do that in the app constructor since I cannot await for a database connection in a constructor.

Could there be a better design pattern here?

2
  • 5
    However, I cannot do that in the app constructor since I cannot await for a database connection in a constructor. -- Then write a simple Factory method instead of a constructor. Jun 21, 2021 at 2:56
  • 1
    +1 for @RobertHarvey's comment. Dependency injection is essentially nothing more than an automatic factory which uses the constructor as its point of configuration. If you don't like using the constructor this way, then simply revert back to a more rudimentary factory pattern.
    – Flater
    Jun 21, 2021 at 10:24

2 Answers 2

2

You can do the following:

class App {
    constructor(database, module) {
        this.database = database
        this.module = module
    }
}
async createApp() {
    const database = new Database()
    await database.connect()
    const collections = await this.database.getCollections()
    const module = new Module(collections.somecollection)

    return new App(database, module)
}

const app = await createApp()

However it seems wrong to have module (high level) and database connection (low level) mixed in the same class... The problem is probably in the way you thought about the App. App as the highest level object should not concern itself with the database. You should have some sort of repository in the middle. Also, you have modules. I again do not know what this module does, but it seems like it would have been better to do something like:

class App {
  constructor() {
    this.module = createModule(createSQLModuleRepository())
  }

  ...
}

class Module {
  constructor(moduleRepository) { ... }

  ...
}

class ModuleRepository {
  constructor(databaseConnection) { ... }

  ...
}

If you dislike the idea of a repository, at least pass the database connection into modules.

1

I had a similar problem with a login. Classes needed an injected client, client non-functional until the user has typed username and password etc.

I solved with events. eg. your Module class could have an UpdateCollection() method which is triggered after the database has connected. ie

database
{
   constructor(Module[] modules) {...}
   promise getCollections() {
      //get collections
      foreach(m in modules)
      {
         m.UpdateCollection(collection)
      }
   }
}

Of course you have to rejig your Module so that an empty collection doesn't cause errors while you wait

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.