DEV Community

Cover image for Conhecendo o Koin Annotations

Conhecendo o Koin Annotations

Declarando dependências com Koin

Através deste artigo você irá aprender todas as formas de como realizar as declarações de dependências utilizando o Koin - Koin DSL, Constructor DSL e finalmente o poderosíssimo Koin Annotations.

Koin DSL

Desta forma declaramos o módulo Koin e inicializamos os construtores de maneira explícita, invocando o famoso get() do Koin.

Exemplo:

class ClassA()
class ClassB(val a: ClassA)

val myModule = module {
    single { ClassA() }
    single { ClassB(a = get()) }
}
Enter fullscreen mode Exit fullscreen mode

Constructor DSL

Desta maneira o trabalho fica bem mais simples, pois não precisamos declarar de maneira explícita os parâmetros dos construtores.

Exemplo:

class ClassA()
class ClassB(val a: ClassA)

val myModule = module {
    singleOf(::ClassA)
    singleOf(::ClassB)
}
Enter fullscreen mode Exit fullscreen mode

Anotações

E por fim, chegamos onde a mágica realmente acontece, pois podemos deixar de usar (ou não) a declaração de dependências num módulo Koin. Apenas utilizando as anotações nas classes das nossas dependências.

Exemplo:

@Single
class ClassA()

@Factory
class ClassB(val a: ClassA)

@KoinViewModel
class MyViewModel(b: ClassB): ViewModel()

@KoinWorker
class UploadFiles: WorkManager()
Enter fullscreen mode Exit fullscreen mode

Também é possível utilizar escopos e propriedades nas suas anotações, para mais detalhes consulte o guia do Koin Annotations aqui.

Koin Annotations

Até aqui acredito que já tenham percebido o quão poderoso é o Koin Annotations, e também o quão é parecido com outros players do mercado, como o Dagger e o Hilt, certo?

O Koin Annotations funciona de maneira adicional aos projetos Kotlin que fazem o uso do Koin. Sendo integrável a projetos já existentes e também há novos projetos.

Com a adoção do Koin Annotations além de deixarmos o código escrito de uma maneira mais simples por anotações, ainda podemos fazer o uso de um recurso que costuma ser o motivo de muitas queixas contrárias ao uso do Koin, que é a verificação das dependências em tempo de compilação!

brain explosion meme animated gif

Compile Safety

Com este recurso é possível validar todas as dependências em tempo de compilação tal qual o Hilt ou o Dagger faz. Apenas adicionando uma linha de código ksp { arg("KOIN_CONFIG_CHECK", "true") }.

Segundo a Kotzilla, empresa responsável por manter e distribuir o Koin, o tempo de compilação do Koin Annotations é 75% mais rápido que o Dagger.

O compile safety é um recurso ainda experimental. Mas tenho aplicado-o num projeto de Server-Driven UI, no core do SDK_ a ser utilizado pelo client-side android. Em mais de 1 mês de uso não tive problemas.

É válido lembrar que o compile safety é configurável então pode ser habilitado ou desabilitado a qualquer momento via arquivo gradle - o padrão é false/desabilitado.

Um ponto negativo, na minha opinião, é que o compile safety não consegue enxergar as dependências declaradas em Koin DSL ou Constructor DSL.

Abaixo temos um exemplo de erro ao tentar compilar um projeto contendo o recurso habilitado e uma dependência não mapeada anotada.

ksp-classb-missing-classa

Troubleshooting: O Koin não conseguiu satisfazer a dependência de ClassA que era esperada no construtor de ClassB.

Mas e os meus módulos?

Por baixo do capô, o Koin Annotations gerará um módulo que contém todas as dependências mapeadas. E para usar este módulo no seu tradicional startKoin é muito simples, exemplo:

// Usar o módulo default gerado pelo Koin Annotations
import org.koin.ksp.generated.*

fun main() {
    startKoin { defaultModule() }
}

// Cenário com N módulos
fun main() {
    startKoin { modules(defaultModule, moduleA, moduleB, existingModule) }
}
Enter fullscreen mode Exit fullscreen mode

Mas e a organização?

Se você pensar num projeto muito grande, com várias dependências, o módulo default pode não soar como algo muito organizado.
Para evitar que o Koin Annotations gere este módulo basta desativar esta configuração padrão com ksp { arg("KOIN_DEFAULT_MODULE", "false") }.

E neste caso, deveremos passar a declarar o uso dos módulos gerados pelas anotações. Veja os exemplos abaixo.

Módulo Koin com Koin Annotations:

@Module
class MyGiantModule
Enter fullscreen mode Exit fullscreen mode

Utilizando o módulo gerado:

import org.koin.ksp.generated.*

fun main() {
    startKoin { modules(MyGiantModule().module) }
}
Enter fullscreen mode Exit fullscreen mode

Como declarar as dependências de cada @Module?

Agora vejamos mais uma anotação para o seu módulo, a @ComponentScan. Ela fará com que todas as dependências declaradas no mesmo package e subpackages sejam mapeadas para o seu @Module. Veja o exemplo a seguir:

@Module
class MyModule
Enter fullscreen mode Exit fullscreen mode

Você também pode especificar o package que deverá ser observado para mapear as dependências. Da seguinte maneira:

@Module
@ComponentScan("com.my.app")
class MyModule
Enter fullscreen mode Exit fullscreen mode

Ainda é possível declarar dependências diretas no seu módulo anotado utilizando funções do Kotlin.

@Module
@ComponentScan("com.my.app")
class MyModule {

    @Factory
    fun service(retrofit: Retrofit): ServiceAPI {
        return retrofit.create(ServiceAPI::class.java)
    }

}
Enter fullscreen mode Exit fullscreen mode

Mas e se eu precisar de mais um @Module?

A inclusão de módulos funciona com as anotações, da seguinte forma:

@Module
class ModuleA

@Module(includes = [ModuleA::class])
class ModuleB
Enter fullscreen mode Exit fullscreen mode

E neste caso poderíamos utilizar da seguinte forma:

import org.koin.ksp.generated.*

fun main() {
    startKoin {
        modules(
          // Irá carregar o ModuleB e o ModuleA
          ModuleB().module
        )
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusão

O Koin Annotations pode ser utilizados em novos projetos e em projetos já existentes. Além disto ele traz uma maneira bastante idiomática para se trabalhar com injeção de dependência em Kotlin.

Acredito que este era o último passo que faltava para o Koin fazer brilhar os olhos de qualquer desenvolvedor Kotlin!
E só mais uma coisa, o Koin Annotations não é específico da plataforma Android, ele também funciona em projetos multiplataformas.

Próximos Passos

Ainda há mais temas para serem cobertos em relação ao Koin Annotations, por exemplo: especificar o binding de uma dependência, injetar um parâmetro, injetar uma dependência lazy, obter uma lista de dependências de um mesmo tipo, etc.
Para estes e outros temas consulte a documentação oficial do Koin Annotations.

Em breve farei um novo artigo demostrando como adicionar o Koin Annotations em apenas um módulo de um projeto. Desta maneira, demonstrando a flexibilidade que temos para poder evoluir um projeto que já utilize o Koin.

Créditos da imagem da capa

Foto de Pawel Czerwinski na Unsplash

Top comments (0)