A injeção de dependência é um padrão de design de software utilizado por alguns frameworks, como Angular e Spring, que nos permite a inversão de controle entre tipos por meio do uso de uma abstração em vez de uma implementação, o que resulta em um código mais modular e fácil de testar. Em Go, o pacote fx, um sistema de injeção de dependência, permite-nos construir uma aplicação sem a necessidade de passar objetos para cada chamada de construtor.
Mas uma estrutura organizada de pastas é importante para facilitar o desenvolvimento de um projeto, e o seguinte código busca mostrar um uso básico de fx e uma estrutura inicial de pastas que serve como exemplo de uso do sistema de injeção de dependência em Go. Tipos de interface não serão criados, uma vez que o objetivo principal aqui é o de exemplificar o uso de fx.
/cmd main.go /internal /app app.go module.go /controller controller.go module.go /usecase usecase.go module.go
Para cada pasta, há um arquivo module.go para facilitar a configuração de fx, o que irá evitar toda a configuração do sistema de dependência em um único arquivo. Assim, é importante que cada tipo seja configurado em sua respectiva pasta do projeto no arquivo module.go. Em seguida temos o código da configuração da aplicação de fx, em /internal/app.go:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | package app import ( "context" "github.com/labstack/echo/v4" "go.uber.org/fx" ) func NewServer() *echo.Echo { return echo.New() } func StartServer(lc fx.Lifecycle, e *echo.Echo) { lc.Append(fx.Hook{ OnStart: func(context.Context) error { go func() { if err := e.Start(":1323"); err != nil { e.Logger.Fatal(err) } }() return nil }, OnStop: func(ctx context.Context) error { return e.Shutdown(ctx) }, }) } func StartApp() { fx.New( fx.Provide( NewServer, ), Modules, fx.Invoke(StartServer), ).Run() } |
Para requisições HTTP, usamos o framework echo. Do código acima seguem as partes mais importantes:
- Linha 32: "fx.Provide registra qualquer número de funções usadas como construtor, ensinando a aplicação como instanciar os vários tipos." - da documentação; a chamada da função NewServe, linha 33, intancia *echo.Echo por meio de fx.Provide, que será injetado por fx como argumento da função StartServer, quando chamada por fx.Invoke;
- Linha 35: a variável Modules (definida no próximo exemplo de código) são registros de construtores que realizamos nos arquivos module.go de cada pasta do projeto;
- Linha 36: fx.Invoke registra funções que são executadas assim que a aplicação inicia; "argumentos para estas invocações são construídos usando os construtores registrados por fx.Provide" - da documentação.
1 2 3 4 5 6 7 8 9 10 11 12 | package app import ( "github.com/labtrilhadev/go-tutorials/di-fx/internal/controller" |
O último trecho de código será o da configuração do controller, pois tem uma proposta de código que acredito ser melhor que configurar todas as rotas da aplicação em um único arquivo, normalmente chamado routes.go. A experiência de uso do Spring Framework mostra que ter a rota configurada no próprio arquivo do controller facilita na identificação do caminho de URLs e nos verbos HTTP utilizados. Segue o código do arquivo /internal/controller/controller.go:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package controller import ( "net/http" "github.com/labstack/echo/v4" "github.com/labtrilhadev/go-tutorials/di-fx/internal/usecase" |
Seguem notas sobre as linhas mais importantes do código anterior:
- Linha 10: a função RegisterAnyController tem os seus parâmetros injetados por fx, e permite manter a configuração das rotas de um controller dentro do mesmo arquivo.
- Linha 18: o construtor NewAnyController será configurado por fx através de fx.Provide, e dessa maneira o framework de injeção de dependência pode injetar a instância do controller como argumento de uma função.
1 2 3 4 5 6 7 8 | package controller import "go.uber.org/fx" var Module = fx.Options( fx.Provide(NewAnyController), fx.Invoke(RegisterAnyController), ) |
Do código anterior, a função fx.Provide configura o objeto de AnyController por meio de seu construtor, e a função fx.Invoke, no início da aplicação, já invoca a função RegisterAnyController para configurar as rotas.
Conclusão
A injeção de dependência nos permite simplificar a passagem de argumentos para construtores e facilita os testes, uma vez que operamos sobre abstrações. O framework fx, após a configuração da construção de objetos de tipos, realiza a passagem de parâmetros para os construtores e para a invocação de métodos. Para visualizar o código do use case, assim como todo o código do tutorial, acesse o repositório no github.
Comentários
Postar um comentário