使用uber fx提供接口

Using uber fx to provide an interface

我正在尝试使用 uber fx 为一个 go 微服务项目做依赖注入。

因为所有的微服务都需要构建一个基础服务器,并设置各种配置选项(通用中间件、缓冲区大小等)(我正在使用 fiber)。但是这些不同的微服务也有微服务独有的配置选项。可能是数据库连接字符串、jwt 键等

我创建了一个接口,用于创建通用基础应用程序的共享函数,具有通用选项,但是任何需要依赖配置结构的函数都失败了,因为期望该微服务的配置的特定版本。

failed to build *fiber.App: missing dependencies for function "some-path/http".CreateServer (some-path/http/http.go:65): missing type: *http.Config exit status 1

最小示例:

http/http.go

package http

import (
    "time"

    "github.com/gofiber/fiber/v2"
)


type BaseConfig interface {
    GetPort() string
    GetTimeout() int
}

type Config struct {
    Port           string `env:"LISTEN_ADDR" envDefault:":3000"`
    Timeout        uint64 `env:"TIMEOUT" envDefault:"10"`
}

func (c *Config) GetPort() string {
    return c.Port
}

func (c *Config) GetTimeout() int {
    return int(c.Timeout)
}

func CreateServer(config *Config) *fiber.App {
    fiberConfig := fiber.Config{
        ReadTimeout:    time.Second * time.Duration(config.GetTimeout()),
        WriteTimeout:   time.Second * time.Duration(config.GetTimeout()),
    }

    app := fiber.New(fiberConfig)

    // do setup and other stuff

    return app
}

some-service/config/config.go

package config

import (
    "github.com/caarlos0/env/v6"
    "github.com/rs/zerolog/log"
)

type Config struct {
    Port                string        `env:"LISTEN_ADDR" envDefault:":3000"`
    Timeout             uint64        `env:"TIMEOUT" envDefault:"10"`
    // some service specific stuff as well
}

func Parse() (*Config, error) {
    cfg := Config{}

    if err := env.Parse(&cfg); err != nil {
        return nil, err
    }

    return &cfg, nil
}

func (c *Config) GetPort() string {
    return c.Port
}

func (c *Config) GetTimeout() int {
    return int(c.Timeout)
}

some-service/main.go

package main

import (
    "context"
    "time"

    "some-path/http"
    "some-path/config"
    "some-path/controllers"
    "github.com/gofiber/fiber/v2"
    "go.uber.org/fx"
)

func main() {

    opts := []fx.Option{}
    opts = append(opts, provideOptions()...)
    opts = append(opts, fx.Invoke(run))

    app := fx.New(opts...)

    app.Run()
}

func provideOptions() []fx.Option {
    return []fx.Option{
        fx.Invoke(utils.ConfigureLogger),
        fx.Provide(config.Parse),
        fx.Invoke(controllers.SomeController),
    }
}

func run(app *fiber.App, config *config.Config, lc fx.Lifecycle) {
    lc.Append(fx.Hook{
        OnStart: func(ctx context.Context) error {
            errChan := make(chan error)

            go func() {
                errChan <- app.Listen(config.Port)
            }()

            select {
            case err := <-errChan:
                return err
            case <-time.After(100 * time.Millisecond):
                return nil
            }
        },
        OnStop: func(ctx context.Context) error {
            return app.Shutdown()
        },
    })
}

some-path/controllers/some-controller.go

package controllers

import "some-path/config"

func SomeController (config *config.Config) {
    // do stuff
}

您缺少 *http.Config 对象,创建一个 return 该对象的函数,例如NewConfig()

package http

import (
    "time"

    "github.com/caarlos0/env/v6"
    "github.com/gofiber/fiber/v2"
)

type BaseConfig interface {
    GetPort() string
    GetTimeout() int
}

type Config struct {
    Port    string `env:"LISTEN_ADDR" envDefault:":3000"`
    Timeout uint64 `env:"TIMEOUT" envDefault:"10"`
}

func NewConfig() (*Config, error) {
    cfg := Config{}

    if err := env.Parse(&cfg); err != nil {
        return nil, err
    }

    return &cfg, nil
}

func (c *Config) GetPort() string {
    return c.Port
}

func (c *Config) GetTimeout() int {
    return int(c.Timeout)
}

func CreateServer(config *Config) *fiber.App {
    fiberConfig := fiber.Config{
        ReadTimeout:  time.Second * time.Duration(config.GetTimeout()),
        WriteTimeout: time.Second * time.Duration(config.GetTimeout()),
    }

    app := fiber.New(fiberConfig)

    // do setup and other stuff

    return app
}

然后更改您的 provideOptions(),可能像这样:

func provideOptions() []fx.Option {
    return []fx.Option{
        fx.Invoke(utils.ConfigureLogger),
        fx.Provide(config.Parse, http.NewConfig),
        fx.Invoke(controllers.SomeController),
        fx.Provide(http.CreateServer),
    }
}