javascript - předat typ do kontejneru závislostí
Podporuji více databází bez použití nástrojů ORM. Pro tento příklad ukážu příklad pro Postgres a MSSQL UserQueries
Mám rozhraní IDataBase s několika databázemi, které toto rozhraní implementují.
@injectable()
export class MssqlDatabase implements IDatabase {
public async connect(): Promise<void> {
}
public async disconnect(): Promise<void> {
}
}
Dále mám svůj IUserQueries rozhraní, které definuje dotazy
export interface IUserQueries {
fetchUsers(): Promise<QueryResult>;
}
QueryResult je vlastní třída, která zajišťuje, že každý dotaz do databáze vrací stejný datový objekt pro mé API. Mé API volá konkrétní dotaz z cílového rozhraní. Pokud je mssql identifikátor v konfiguračním souboru, moje aplikace by měla vytvořit spojení s databází MSSQL. Ale mé API soubory o tom nemusí vědět a ani je to nezajímá (samozřejmě, přesně to dělá rozhraní).
Základním příkladem by bylo
@injectable()
export class UserRepository implements IUserRepository {
public userQueries: IUserQueries;
constructor(@inject(IoCTypes.IUserQueries) userQueries: IUserQueries) {
this.userQueries = userQueries;
}
}
Teď to zde začne být složité. Můj DI kontejner:
const container: Container = new Container();
container.bind<IUserQueries>(IoCTypes.IUserQueries).to(/* what? */);
export { container };
DI kontejner neví, který soubor dotazů vybrat. Může to být MSSQLUserQueries nebo PostgresUserQueries. Jak mohu tento problém vyřešit? Je možné vybrat správné dotazy při spouštění serveru (app.ts), ale pokud jsem to správně pochopil, tento DI kontejner je zcela nezávislý na aplikaci a pouze funguje jako konfigurační soubor, takže bych neměl předávat abstraktní třídu UserQueries.
Odpověď č. 1
Můžete využít návrhový vzor továrny:
interface IUserQueryFactory {
get(): IUserQueries;
get(type: string): IUserQueries;
}
class UserQueryFactory implements IUserQueryFactory {
get(): IUserQueries {
const defaultValue = config.database; // or whatever returns your "mssql" from config
return this.get(defaultValue);
}
get(type: string): IUserQueries {
switch (type) {
case "mssql":
return new MSSQLUserQueries();
case "postgresql":
return new PostgresUserQueries();
default:
return null;
}
}
}
const container: Container = new Container();
container.bind<IUserQueryFactory>(IoCTypes.IUserQueryFactory).to(UserQueryFactory);
container.bind<IUserQueries>(IoCTypes.IUserQueries).toDynamicValue((context: interfaces.Context) => { return context.container.get<IUserQueryFactory>(IoCTypes.IUserQueryFactory).get(); });
export { container };
Nejsem si jistý, jestli mám celou syntaxi správně, protože to nebylo testováno, ale myslím, že myšlenku chápeš:
IUserQueryFactoryvracíIUserQueriesna základě vstupního typu nebo vrací výchozí (ten definovaný v konfiguraci), pokud typ není poskytovatel- Výchozí implementace pro
IUserQueriesje v podstatě implementována prostřednictvímIUserQueryFactory, což znamená, že celý zdroj vytvářeníIUserQueriesje na jednom místě a snadno udržovatelný.
Odpověď č. 2
Pokud tomu rozumím správně, váš problém spočívá ve výběru jedné z konkrétních implementací (MSSQLUserQueries, PostgresUserQueries) nějakého rozhraní (IUserQueries) za běhu programu - na základě nějaké konfigurace. Nejjednodušší přístup je použít továrnu, která zná konfiguraci a vrací správnou implementaci IUserQueries.
container.bind<interfaces.Factory<IUserQueries>>(IoCTypes.IUserQueriesFactory).toFactory<IUserQueries>((context: interfaces.Context) => {
return () => {
const config = context.container.get('configuration');
//if config.isPostgresql return PostgresUserQueries
//if config.isMssql return MSSQLUserQueries
};
});
