uber-go / fx

A dependency injection based application framework for Go.
https://uber-go.github.io/fx/
MIT License
5.72k stars 289 forks source link

Support Multiple ParamTags? #1190

Closed imprint-turing-sonu closed 5 months ago

imprint-turing-sonu commented 5 months ago

Is your feature request related to a problem? Please describe. I've found a use case of using multiple ParamTags with ParamTags,

func NewKafkaConsumer(
    kafkaStream *kafkastream.Config,
    udf runners.Processor,
    logger logging.API,
    stats *stats.Client) (runners.KafkaConsumer, error) {
}

In this scenario, udf and kafkaStream can vary depending on the consumer. For instance, if there are 10 Kafka consumers, there will be 10 udfs and 10 kafkaStreams, but only one instance each of logger and stats objects.

I attempted to utilize multiple ParamTags, but it doesn't seem to work as intended.

Describe the solution you'd like I'd like this to support multiple ParamTags. If there are more than one ParamTags, we should attempt to find those objects and utilize them accordingly.

var New = fx.Options(
    fx.Provide(
        fx.Annotate(application.NewApplicationV2StreamWithParams,
            fx.ResultTags(`name:"applicationStreamProcessor"`),
            fx.As(new(runners.Processor)))),
    fx.Provide(fx.Annotate(
        kafka_consumer_fx.NewKafkaConsumer,
        fx.ParamTags(`name:"applicationStreamConfig"`),
        fx.ParamTags(`name:"applicationStreamProcessor"`),
        fx.ResultTags(`name:"v2KafkaConsumer"`),
    )),
    fx.Invoke(
        fx.Annotate(
            func(runner runners.Runner, lc fx.Lifecycle) {
                lc.Append(fx.Hook{
                    OnStart: func(ctx context.Context) error {
                        return runner.Start(context.Background())
                    },
                    OnStop: func(ctx context.Context) error {
                        return runner.Stop(ctx, time.Second)
                    },
                })
            },
            fx.ParamTags(`name:"v2KafkaConsumer"`),
        ),
    ),
)

Actually, the invoke isn't necessary here. Once we can begin utilizing fx.Lifecycle within NewKafkaConsumer, it could look something like this.

func NewKafkaConsumer(
    kafkaStream *kafkastream.Config,
    udf runners.Processor,
    logger logging.API,
    stats *stats.Client,
    lc fx.Lifecycle) (runners.KafkaConsumer, error) {
}

var New = fx.Options(
    fx.Provide(
        fx.Annotate(application.NewApplicationV2StreamWithParams,
            fx.ResultTags(`name:"applicationStreamProcessor"`),
            fx.As(new(runners.Processor)))),
    fx.Provide(fx.Annotate(
        kafka_consumer_fx.NewKafkaConsumer,
        fx.ParamTags(`name:"applicationStreamConfig"`),
        fx.ParamTags(`name:"applicationStreamProcessor"`),
        fx.ResultTags(`name:"v2KafkaConsumer"`),
    )),
)

Describe alternatives you've considered Currently, I've eliminated the use of fx for this particular use case and transitioned to a traditional argument-based approach.

Is this a breaking change? NO

Additional context What challenges are we facing in supporting it? I'm considering submitting a pull request to address these issues. Additionally, I've come across cases where we've explicitly tested and disallowed this behavior.

imprint-turing-sonu commented 5 months ago

NVM, found a way to do it by supplying more than one tag in ParamTags method.