Vamos por parte, que es la inyección de dependencias?
Cualquier aplicación no trivial está formada por dos o más objetos que colaboran entre sí para realizar alguna lógica. Tradicionalmente cada objeto se hacía cargo de obtener sus propias referencias a los objetos a los cuales colaboraba (sus dependencias). Esto lleva a código acoplado y difícil de probar. Y para solucionar esto esta la inyección de dependencias. El contexto ingresa las dependencias de un objeto. Para mayor información pueden ir a : https://emanuelpeg.blogspot.com/2009/07/inyeccion-de-dependencias.html
Y que es Fx? Según la documentación oficial publicada por Uber, Fx es un framework para Go que:
- Facilita la inyección de dependencia.
- Elimina la necesidad de estado global y func init().
Fx usa el patrón de inyección de constructor, tratemos de entender exactamente cómo hace que la inyección de dependencia sea fácil en Go.
Para usar Fx debemos:
El primer paso es instalar la biblioteca a través de go get go.uber.org/fx.
Y luego podemos hacer esto:
package main
import (
"context"
"go.uber.org/fx"
"go.uber.org/fx/fxevent"
"log"
"net"
"net/http"
"os"
"time"
)
func NewLogger() *log.Logger {
logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)
logger.Print("Create NewLogger.")
return logger
}
func NewHandler(logger *log.Logger) (http.Handler, error) {
logger.Print("Create NewHandler.")
return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
logger.Print("Got a request.")
}), nil
}
func NewMux(lc fx.Lifecycle, logger *log.Logger) *http.ServeMux {
logger.Print("Create NewMux.")
mux := http.NewServeMux()
server := &http.Server{
Addr: "127.0.0.1:8080",
Handler: mux,
}
lc.Append(fx.Hook{
OnStart: func(context.Context) error {
logger.Print("Starting HTTP server.")
ln, err := net.Listen("tcp", server.Addr)
if err != nil {
return err
}
go server.Serve(ln)
return nil
},
OnStop: func(ctx context.Context) error {
logger.Print("Stopping HTTP server.")
return server.Shutdown(ctx)
},
})
return mux
}
func Register(mux *http.ServeMux, h http.Handler) {
mux.Handle("/", h)
}
func main() {
app := fx.New(
fx.Provide(
NewLogger,
NewHandler,
NewMux,
),
fx.Invoke(Register),
fx.WithLogger(
func() fxevent.Logger {
return fxevent.NopLogger
},
),
)
startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := app.Start(startCtx); err != nil {
log.Fatal(err)
}
if _, err := http.Get("http://localhost:8080/"); err != nil {
log.Fatal(err)
}
stopCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := app.Stop(stopCtx); err != nil {
log.Fatal(err)
}
}
En el ejemplo queremos costruir varias entidades, un *log.Logger, un http.Handler, un servidor *http.ServeMux y queremos llamar a una request. Y fx, nos construye estos objetos.
Si ejecutamos esta aplicación el resultado será :
Create NewLogger.
Create NewMux.
Create NewHandler.
Starting HTTP server.
Got a request.
Stopping HTTP server.
Dejo link: