Mostrando las entradas con la etiqueta Fx. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Fx. Mostrar todas las entradas

lunes, 27 de febrero de 2023

Inyección de dependencia en Go usando Fx parte 2

 


Seguimos con fx, ahora vamos a hacer otro ejemplo con fx y Go basados en este ejemplo hecho con spring y java.

Primero vamos a hacer una interfaz que nos permita obtener un saludo : 

type Saludor interface {

GetSaludo() string

}

Luego vamos a implementarla en español e ingles : 


type SaludorEs struct {

}

func (saludoEs SaludorEs) GetSaludo() string {

return "Hola !!"

}


type SaludorEn struct {
}

func (saludoEn SaludorEn) GetSaludo() string {
return "Hi !!"
}

Y vamos a hacer métodos que nos permitan crear estas implementaciones : 

func CrearSaludadorEs() (Saludor, error) {
return SaludorEs{}, nil
}

func CrearSaludadorEn() (Saludor, error) {
return SaludorEn{}, nil
}

Y ahora vamos a hacer un método que sea polimorfico que imprima el saludo : 


func Saludar(saludor Saludor) {
fmt.Println(saludor.GetSaludo())
}

Como ven referencia a la interfaz por lo tanto se puede usar con el SaludadorEs y el SaludadorEn porque implementan esta interfaz. 

Y por ultimo vamos hacer 2 app de fx una en español y la otra en ingles : 


func main() {
appEs := fx.New(

fx.Provide(
CrearSaludadorEs,
),

fx.Invoke(Saludar),
)

ctxEs, cancelEs := context.WithCancel(context.Background())
defer cancelEs()
if err := appEs.Start(ctxEs); err != nil {
log.Fatal(err)
}

appEn := fx.New(

fx.Provide(
CrearSaludadorEn,
),

fx.Invoke(Saludar),
)

ctxEn, cancelEn := context.WithCancel(context.Background())
defer cancelEn()
if err := appEn.Start(ctxEn); err != nil {
log.Fatal(err)
}
}

y la salida es la siguiente : 

[Fx] PROVIDE    main.Saludor <= main.CrearSaludadorEs()
[Fx] PROVIDE    fx.Lifecycle <= go.uber.org/fx.New.func1()
[Fx] PROVIDE    fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
[Fx] PROVIDE    fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
[Fx] INVOKE             main.Saludar()
Hola !!
[Fx] RUNNING
[Fx] PROVIDE    main.Saludor <= main.CrearSaludadorEn()
[Fx] PROVIDE    fx.Lifecycle <= go.uber.org/fx.New.func1()
[Fx] PROVIDE    fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
[Fx] PROVIDE    fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
[Fx] INVOKE             main.Saludar()
Hi !!
[Fx] RUNNING

Y listo!!



Inyección de dependencia en Go usando Fx


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: