Kotlin é mais conhecida por ser a linguagem oficial para desenvolvimento de apps Android mas desde sua versão 1.0 (fevereiro de 2016) o framework Spring oferece suporte a esta linguagem que tem agradado a muitos desenvolvedores aqui na redspark.
Ter o suporte de um framework do tamanho do Spring foi muito importante para o crescimento do Kotlin para backend. Desenvolver uma API REST com Kotlin é uma forma de manter algumas vantagens do Java, como a robustez da JVM (Kotlin também roda em outras plataformas mas isso é assunto para outro post) e poder utilizar uma linguagem moderna e concisa.
Vamos criar nossa primeira aplicação REST utilizando Spring Webflux + Kotlin. Este post é o primeiro de uma série onde abordaremos a criação de uma API REST completa.
Configuração
Acesse https://start.spring.io/ para criar o projeto Spring e utilize as seguintes configurações:
@SpringBootApplication
class GamesApplication
fun main(args: Array<String>) {
runApplication<GamesApplication>(*args)
}
Controller
Nesta aplicação vamos utilizar a arquitetura em camadas MVC. As anotações são as mesmas do Spring com Java:
@RestController
@RequestMapping("games")
class GameController {
@GetMapping
fun getGames(): List{
val gameList = ArrayList()
gameList.add("CS:GO")
gameList.add("Candy Crush")
gameList.add("Age of Empires")
return gameList
}
}
Olhando o método getGames podemos notar que o código está muito parecido com Java. É o que chamamos de “sotaque” Java. Este código funciona, se rodarmos o server e chamarmos http://localhost:8080/games (no browser ou no postman) teremos o retorno os 3 jogos.
Agora que está tudo funcionando, vamos refatorar o nosso código e escrever um código mais conciso. Primeiro podemos utilizar a função listOf() para inicializarmos nossa lista:
@GetMapping
fun getGames(): List{
return listOf("CS:GO", "Candy Crush", "Age of Empires")
}
No Kotlin, quando temos uma função que tem como única instrução o retorno nós podemos fazer uma atribuição direta:
@GetMapping
fun getGames(): List = listOf("CS:GO", "Candy Crush", "Age of Empires")
Também é possível omitir o tipo de retorno (List) pois o Kotlin trabalha com inferência de tipos. É importante ter ciência que Kotlin, assim como o Java é uma linguagem fortemente tipada. De qualquer forma, aqui na redspark nós optamos por manter o retorno explícito pois acreditamos que isso melhora a leitura do código. Se rodarmos no server, teremos a mesma resposta.
Domain
Nós não queremos retornar uma String, no Kotlin continuamos no mundo da orientação a objeto. Nosso jogo terá título e data de lançamento:
class Game {
lateinit var title: String
lateinit var releaseDate: LocalDate
}
Algumas observações sobre esta classe. Agora as classes são public e final por definição. Se quisermos (e geralmente não vamos querer) fazer uma herança de uma classe, precisamos declará-la como “open”. Também não precisamos dos getters e setters de forma explícita (RIP Lombok), o Kotlin funciona com property access então, se necessário, podemos sobrescrever os getters e setters mas eles não precisam ser declarados.
Vamos refatorar esta classe para o tipo data class:
data class Game (
val title: String,
val releaseDate: LocalDate
)
Trocamos o escopo da classe por parênteses e o lateinit var (variável inicializada posteriormente) por val (constante). Este tipo de classe é utilizado para instanciar objetos que guardem dados e tem algumas vantagens como implementar os métodos equals(), hashCode(), toString() e copy().
Service
Vamos implementar a camada de service, aqui é onde ficarão as regras de negócio da nossa aplicação. Como esta classe será instanciada e gerenciada pelo Spring vamos primeiro criar uma interface, assim podemos depender de uma abstração ao invés de uma classe concreta:
interface GameService {
fun getGames(): List
}
Vamos criar a implementação desta classe (dica: Se estiver utilizando o IntelliJ, é só dar alt+enter na interface e pedir pra criar a implementação).
@Service
class GameServiceImpl : GameService {
override fun getGames(): List {
val csGo = Game("CS:GO", LocalDate.of(2012, 8, 21))
val candyCrush = Game("Candy Crush", LocalDate.of(2012, 3, 12))
val ageOfEmpires = Game("Age of Empires", LocalDate.of(1997, 9, 15))
return listOf(csGo, candyCrush, ageOfEmpires)
}
}
Por último vamos utilizar a injeção de dependência para instanciar nosso service no controller:
@RestController
@RequestMapping("games")
class GameController @Autowired constructor(
private val gameService: GameService
){
@GetMapping
fun getGames(): List = gameService.getGames()
}
Conforme a recomendação do pessoal do Spring (http://olivergierke.de/2013/11/why-field-injection-is-evil/), vamos utilizar a injeção de dependência no construtor ao invés da propriedade.
Pronto, se chamarmos nosso endpoint teremos a seguinte resposta:
Por hora é isso pessoal. Aguardem nos próximos posts a implementação da camada de repository, security e mais dicas de Kotlin. O código fonte que criamos está disponível em https://github.com/fabiotadashi/redspark-kotlin-api.