Ir para o conteúdo

Injeção de dependências e FX

De Wikiversidade

Injeção de dependências (Dependency Injection)

[editar | editar código]

Injeção de dependências é um padrão de design que trata da maneira como objetos e seus dependentes são criados e conectados. Em vez de um objeto instanciar diretamente suas dependências, estas são fornecidas (ou "injetadas") por um componente externo, geralmente um framework ou contêiner de injeção.

Este padrão é especialmente útil para grandes aplicações, mas deve-se considerar suas vantagens e desvantagens.

Vantagens

[editar | editar código]
  • Desacoplamento: facilita a substituição de componentes (muito útil para testes, pois facilita o uso de mocks).
  • Melhor organização e legibilidade do código.
  • Facilita a manutenção de grandes bases de código.
  • Facilita o desenvolvimento concorrente.

Desvantagens

[editar | editar código]
  • Interfaces que demandam detalhes de configuração.
  • Dificulta a depuração ao separar comportamento e construção.
  • Encoraja dependência em relação a frameworks.

Frameworks

[editar | editar código]

Este tutorial será focado em injeção de dependências em Go utilizando o framework FX. A lista abaixo indica ferramentas que podem ser utilizadas para injeção de dependências em outras linguagens:

  • Java: Spring, Guice, Dagger
  • Python: dependency-injector, Injector
  • C# (.NET): Microsoft.Extensions.DependencyInjection
  • JavaScript/TypeScript: InversifyJS
  • Ruby: dry-container, Mutant
  • C++: Fruit, Boost.DI
  • Rust: shaku
  • Kotlin: Koin, Dagger (via kapt)

FX: Injeção de Dependências para Go

[editar | editar código]

O FX é um framework desenvolvido pela Uber para resolver a complexidade da inicialização e composição de grandes serviços em Go, facilitando a injeção de dependências automática através do uso de construtores e grafos de dependência. É distribuído como software livre sob a licença MIT.

Principais características do FX:

  • Usa construtores para declarar dependências.
  • Cuida da ordem de inicialização dos componentes.
  • Suporta ciclos de vida com hooks (ex: OnStart, OnStop).
  • Facilita composição modular (ex: fx.Provide, fx.Invoke).
  • Ideal para aplicações grandes e com muitos serviços e módulos.

Mini tutorial de FX (Go)

[editar | editar código]

O tutorial abaixo foi elaborado com base no tutorial da JetBrains, mas foi traduzido e generalizado para não depender da IDE GoLand. Para aprofundamento, a documentação oficial do FX apresenta um tutorial mais longo e detalhado na seção Get started.

Pré-requisitos

[editar | editar código]
  1. Caso ainda não tenha instalado, instale o Go: https://go.dev/dl/
  2. Crie um diretório para seu projeto, e.g. fxdemo, e inicialize com go mod init <path>/fxdemo — Atenção: este path não é o path do diretório no seu computador, mas um path que você está definindo para o seu módulo. Geralmente, para módulos públicos (como o FX), esse path é a URL do repositório. Neste exemplo, você pode usar qualquer coisa. Isso vai criar um arquivo go.mod dentro do diretório do seu projeto.
  3. Adicione o FX usando o comando: go get go.uber.org/fx

Estruturando o projeto

[editar | editar código]

No diretório do seu projeto, crie um arquivo main.go com os componentes necessários:

package main

import "fmt"

type User struct {
    name string
}

// NewUser - Cria uma nova instância de User
func NewUser(name string) User {
    return User{name: name}
}

// NewUserName - Retorna uma string que fornece um nome para um novo user
func NewUserName() string {
    return "Livia"
}

// Get - Um método que tem user como dependência
func (u *User) Get(message string) string {
    return fmt.Sprintf("Hello %s - %s", u.name, message)
}

// Run - Depende do user e chama o método Get que estende User
func Run(user User) {
    result := user.Get("It's nice to meet you!")
    fmt.Println(result)
}

func main() {
    // Vamos inserir o código do FX aqui
}

Injetando dependências com o FX

[editar | editar código]

Na sua função main(), forneça as dependências ao FX e invoque a função Run:

func main() {
    fx.New(
        fx.Provide(
            NewUserName,
            NewUser,
        ),
        fx.Invoke(Run),
    ).Run()
}

Não se esqueça de adicionar o FX aos imports:

import (
	"fmt"
	"go.uber.org/fx"
)

Executando

[editar | editar código]

No terminal, execute seu programa com os seguinte comando:

go run main.go

O output esperado é:

[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] PROVIDE    string <= main.NewUserName()
[Fx] PROVIDE    main.User <= main.NewUser()
[Fx] INVOKE             main.Run()
[Fx] RUN        provide: main.NewUserName() in 25.25µs
[Fx] RUN        provide: main.NewUser() in 2.458µs
Hello Livia - It's nice to meet you!
[Fx] RUNNING

Para testar seu código, crie um arquivo main_test.go no diretório do seu projeto (mesmo nível do main.go). Os testes unitários abaixo usam o pacote testing.

package main

import (
    "fmt"
    "testing"
)

func TestNewUser(t *testing.T) {
    name := "Livia"
    expected := User{name: name}
    actual := NewUser(name)

    if actual != expected {
        t.Error("Expected User is not same as actual user")
    }
}

func TestUser_Get(t *testing.T) {
    name := "Livia"
    user := NewUser(name)
    message := "Hello there!"

    expected := fmt.Sprintf("Hello %s - %s", user.name, message)
    actual := user.Get(message)

    if actual != expected {
        t.Error("Expected User is not same as actual user")
    }
}

Referências

[editar | editar código]

Sobre injeção de dependências

[editar | editar código]

Sobre o FX

[editar | editar código]

Documentação

[editar | editar código]

Tutoriais

[editar | editar código]

Nota: depois de escrito com base nas referências citadas, este texto foi revisado utilizando o ChatGPT.