Currently when defining arguments using injected, one has to pass in the constructor as a first argument. This kinda removes a lot of the flexibility a DI container can give you. Let's say we want to do the following:
const TOKENS = {
readCredentials: token<string>("readCredentials"),
writeCredentials: token<string>("writeCredentials"),
readConnection: token<DatabaseConnection>("readConnection"),
writeConnection: token<DatabaseConnection>("writeConnection"),
listTodosUseCase: token<ListTodosUseCase>("listTodosUseCase"),
saveTodoUseCase: token<SaveTodoUseCase>("saveTodoUseCase"),
};
class DatabaseConnection {
constructor(private credentials: string) {}
}
class ListTodosUseCase {
constructor(private connection: DatabaseConnection) {}
}
class SaveTodoUseCase {
constructor(private connection: DatabaseConnection) {}
}
container.bind(TOKENS.readCredentials).toConstant(config.db.credentials.read);
container.bind(TOKENS.writeCredentials).toConstant(config.db.credentials.write);
container.bind(TOKENS.readConnection).toInstance(DatabaseConnection);
container.bind(TOKENS.writeConnection).toInstance(DatabaseConnection);
/**
* Perfect so far, we can give the different use cases the appropriate connections.
*/
injected(ListTodosUseCase, TOKENS.readConnection);
injected(SaveTodoUseCase, TOKENS.writeConnection);
/**
* Here we run into an issue, we can't inject different connection strings into the same constructor.
*/
injected(DatabaseConnection, TOKENS.readCredentials);
injected(DatabaseConnection, TOKENS.writeCredentials);
You could say well, just create a read and write connection class and let them implement the same interface. But this defeats the purpose. The implementation of these classes will be the same, resulting in code duplication.
A great thing about some DI containers is that they give you flexibility to have the same class with different arguments injected, registered under different keys. (that's why the injected function should probably also not be in the same file as the class definition, this removes that flexibility).
I first thought conditional bindings with tags would solve this, but they are also bound directly to the constructor. That just moves the issue one level up.
My proposed solution is as follows, let's define the injected arguments per token instead of per constructor:
injected(TOKENS.listTodosUseCase, TOKENS.readConnection);
injected(TOKENS.saveTodoUseCase, TOKENS.writeConnection);
injected(TOKENS.readConnection, TOKENS.readCredentials);
injected(TOKENS.writeConnection, TOKENS.writeCredentials);
I can imagine, having the injected function as a separate function is then not even necessary anymore. We could do something like:
container.bind(TOKENS.readCredentials).toConstant(config.db.credentials.read);
container.bind(TOKENS.writeCredentials).toConstant(config.db.credentials.write);
container
.bind(TOKENS.readConnection)
.toInstance(DatabaseConnection);
.withArguments(TOKENS.readCredentials);
container
.bind(TOKENS.writeConnection)
.toInstance(DatabaseConnection)
.withArguments(TOKENS.writeCredentials);
I think this would greatly improve the flexibility of this library. Let me know what you think!
Currently when defining arguments using
injected, one has to pass in the constructor as a first argument. This kinda removes a lot of the flexibility a DI container can give you. Let's say we want to do the following:You could say well, just create a read and write connection class and let them implement the same interface. But this defeats the purpose. The implementation of these classes will be the same, resulting in code duplication.
A great thing about some DI containers is that they give you flexibility to have the same class with different arguments injected, registered under different keys. (that's why the
injectedfunction should probably also not be in the same file as the class definition, this removes that flexibility).I first thought conditional bindings with tags would solve this, but they are also bound directly to the constructor. That just moves the issue one level up.
My proposed solution is as follows, let's define the injected arguments per token instead of per constructor:
I can imagine, having the injected function as a separate function is then not even necessary anymore. We could do something like:
I think this would greatly improve the flexibility of this library. Let me know what you think!