(WIP)
DI tool of GO.
Unlike other approaches to dependency injection, Fx works with plain Go functions: you don't need to use struct tags or embed special types, so Fx automatically works well with most Go packages.
Core of 'fx' is Provide and Invoke.
Provider is a function that creates and returns an instance of 1 or more specific types/objs.
'fx.Provide' is used to register providers with the DI container.
Defining an action.
'fx' will automatically provide necessary dependencies of a func registered in invoke. Then, the func can use the dependencies to do something.
Functions are executed in the order they are registered.
When app starts, 'fx' builds a dependency graph and resolve all dependencies.
'fx' uses Go's reflection pkg to understand inputs of outputs of functions to wire.
Basic Example
config/config.go
package config
type Config struct {
Content string
}
func New() Config {
return Config{Content: "Hello world"}
}
New() is constructor function. Logger doesn't need other dependencies.
'fx' calls constructors lazily, so NewLogger is called only if some other function needs a logger.
Once it is instantiated, it is cahced and reused.(singleton)
package printer
import (
"fmt"
"myapp/config"
)
func Print(cfg config.Config) {
fmt.Println(cfg.Content)
}
Print has formal parameters. 'fx' interpret this a s dependencies. To use Print, Config is needed. If it is cached in app already, 'fx' uses it.
func NewHandlerAndLogger() (*log.Logger, http.Handler, error)
Functions may return multiple objs like this.
main.go
package main
import (
"log"
"go.uber.org/fx"
"myapp/config"
"myapp/printer"
)
func main() {
app := fx.New(
fx.Provide(
config.New,
)
fx.Invoke(
printer.Print,
),
}
// result of ''go run main.go'
// Hello world
private option can be passed as an argument of Provide, to restrict access to the constructors being provided.
Private constructor can only be used within the current module or modules the current module contains.
fx.New(
fx.Module("SubModule", fx.Provide(func() int { return 0 }, fx.Private)),
fx.Invoke(func(a int) {}),
)
//error becuase cannot access to the int because it is private
Annotations are additional info that can be attached dependencies/providers to modify ther behavior or provide context. Tag, names etc.
fx.Provide(
fx.Annotate(NewGateway, fx.ResultTag("custom-gateway")),
)
The name of annot - 'custom-gateway' can be used later to retreive or reference the provider.
Annotate lets you annotate a function's parameters and returns without you having to declare separate struct definitions for them.
type params struct {
fx.In
RO *db.Conn `name:"ro" optional:"true"`
RW *db.Conn `name:"rw"`
}
type result struct {
fx.Out
GW *Gateway `name:"foo"`
}
fx.Provide(func(p params) result {
return result{GW: NewGateway(p.RO, p.RW)}
})
// is equivalent to
func NewGateway(ro, rw *db.Conn) *Gateway { ... }
fx.Provide(
fx.Annotate(
NewGateway,
// get a param with proper name tag
fx.ParamTags(`name:"ro" optional:"true"`, `name:"rw"`),
fx.ResultTags(`name:"foo"`),
),
fx.Invoke(func(foo *Gateway) {
fmt.Printf("ROConn Name: %s\n", foo.ROConn.Name)
fmt.Printf("RWConn Name: %s\n", foo.RWConn.Name)
}),
)
The tags can be used other provider or modules.
Variadic functions
fx.Annotate(func(mux *http.ServeMux, handlers ...http.Handler) {
// ...
}, fx.ParamTags(``, `group:"server"`))
Annotated
Annotated annotates a constructor provided to Fx with additional options.
즐겁게 읽었습니다. 유용한 정보 감사합니다.