Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject correct service based on parameter

Let's assume I have a two modules which are exporting BService and CService where both of those services extends AService

So code looks like this:

abstract class AService {
    public run() {}
}

@Injectable()
export class BService extends AService {}

@Injectable()
export class CService extends AService {}

@Module({
    providers: [BService],
    exports: [BService],
})
export class BModule {}


@Module({
    providers: [CService],
    exports: [CService],
})
export class CModule {}

@Injectable()
class AppService {
    constructor(protected readonly service: AService) {}

    public run(context: string) { // let's assume context may be B or C
        this.service.run();
    }
}


@Module({
    imports: [CModule, BModule],
    providers: [{
        provide: AppService,
        useFactory: () => {
            return new AppService(); // how to use BService/CService depending on the context?
        }
    }]
})
export class AppModule {}

But the key is, I cannot use REQUEST (to inject it directly in useFactory) from @nestjs/core as I'm using this service in cron jobs and with the API call

I also don't think Factory pattern is useful there, I mean it would work but I want to do it correctly

I was thinking about property based injection.

But I'm not sure how to use it in my case

like image 299
hejkerooo Avatar asked Jan 01 '23 10:01

hejkerooo


1 Answers

In my opinion, the factory approach is exactly what you need. You described that you need a different service based on the context which is a great for for the factory approach. Let's try this:

Create an injectable factory:

import { Injectable } from '@nestjs/common';
import { AService } from './AService';
import { BService } from './BService';
import { CService } from './CService';

@Injectable()
export class ServiceFactory {

    public getService(context: string) : AService {

        switch(context) {
            case 'a': return new BService();
            case 'b': return new CService();
            default: throw new Error(`No service defined for the context: "${context}"`);
        }
    }
}

Now import that factory into your app module:

import { ServiceFactory } from './ServiceFactory';
import { AService } from './AService';

@Module({
    providers: [AppService, ServiceFactory]
})
export class AppModule {}

Now your app service will get the factory as a dependency which will create the appropriate service based on the context:

import { ServiceFactory } from './ServiceFactory';
import { AService } from './AService';

@Injectable()
class AppService {

    constructor(readonly serviceFactory: ServiceFactory) { }

    public run(context: string) {
        const service: AService = this.serviceFactory.getService(context);
        service.run();
    }
}
like image 181
nerdy beast Avatar answered Jan 05 '23 16:01

nerdy beast