# Kotlin - Rest Api completa com Spring Boot, Swagger, Mysql e docker #02

### Parte 02 - Criando nossa classe de serviço e repositório

Agora que já temos nossa aplicação configurada, vamos falar um pouco de como iremos criar a estrutura de busca e manipulação dos dados. Em outro post vou abordar mais afundo sobre o tema, mas a ideia aqui é termos camadas separadas.

Antes de mais nada, precisamos criar o nosso modelo, a classe que vai conter as informações de um Produto. Criaremos o package `model` e dentro dele a classe em kotlin `Product`. Usaremos aqui o conceito de  [data class](https://kotlinlang.org/docs/data-classes.html) existente no Kotlin, economizando boas linhas que gastaríamos com Java por exemplo. Para nos auxiliar, vamos anotar essa classe como uma Entidade, com o `@Entity` e utilizar a anotação `@Table` para que quando iniciarmos a aplicação, ele identifique a tabela e crie se necessário.
```
package com.kotlin.crudexample.model

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id
import javax.persistence.Table

@Entity
@Table(name = "Product")
data class Product(
    @Id @GeneratedValue val id: Long,
    val name: String,
)
```

Com nossa entidade criada, agora podemos trabalhar com o repositório. Criaremos um novo package chamado `repository` e vamos criar uma nova interface chamada `ProductRepository`:
```
package com.kotlin.crudexample.repository

import com.kotlin.crudexample.model.Product
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.stereotype.Repository

@Repository
@EnableJpaRepositories
interface ProductRepository : JpaRepository<Product, Long>
```
Veja que anotamos a interface com `@Repository` e `@EnableJpaRepositories` onde o spring nos auxilia com toda lógica por trás trabalhando como uma class de  [repositório JPA](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#reference). Notamos também que expandimos a classe utilizando `JpaRepository`, passando um objeto e um `id`. No caso, iremos passar nossa entidade `Product` e dizer que o id é `Long`.

Agora, vamos tentar chamar esse Repositório e ver como o Spring se comporta. Iremos injetar ele em nosso Controller e ver quais métodos podemos acessar. Veja como o Kotlin deixa mais fácil injetarmos a interface de repositório. Basta passarmos como construtor e já podemos utilizar em nossas funcões:
```
package com.kotlin.crudexample.controller

import com.kotlin.crudexample.model.Product
import com.kotlin.crudexample.repository.ProductRepository
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class ProductController(val productRepository: ProductRepository) {

    @GetMapping("/products")
    fun getAll(): List<Product> = productRepository.findAll()
}
```
Em nosso método `getAll()`, alteramos o seu retorno chamando o nosso repositório de produto com o método de buscar todos itens da entidade `findAll()`. Se rodar a aplicação, veremos que agora teremos um retorno de uma lista vazia, já que não temos informações criadas em nossa base de dados. Rode a aplicação e veja nosso retorno vazio `[]` chamando `http://localhost:8081/products`.

Vamos inserir alguns dados? Então vamos criar nosso CRUD básico!
Antes de prosseguir, aconselho ter instalado o  [Postman](https://www.postman.com/) ou outro API Cliente de sua preferência para nos auxiliar nas chamadas das rotas (calma que logo mais chegamos no Swagger :D).

```
package com.kotlin.crudexample.controller

import com.kotlin.crudexample.model.Product
import com.kotlin.crudexample.repository.ProductRepository
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController

@RestController
class ProductController(val productRepository: ProductRepository) {

    @GetMapping("/products")
    fun getAll(): List<Product> = productRepository.findAll()

    @GetMapping("/product/{id}")
    fun getById(@PathVariable(value = "id") id: Long): Product = productRepository.getById(id)

    @PutMapping("/product/{id}")
    fun update(@PathVariable(value = "id") id: Long, @RequestBody product: Product) {
        if (productRepository.existsById(id))
            productRepository.save(product)
    }

    @PostMapping("/product")
    fun create(@RequestBody product: Product) = productRepository.save(product)

    @DeleteMapping("/product/{id}")
    fun delete(@PathVariable(value = "id") id: Long) =
        takeIf { productRepository.existsById(id) }
            .let { productRepository.delete(productRepository.getById(id)) }
}
```
Agora nosso controller já consegue fazer algumas chamadas básicas. Veja que temos:
- `getAll()` -> chamando nosso repositório e buscando todos itens da tabela *Product*;
- `getById(id)` -> Passamos um *id* através da nossa rota (exemplo: /product/1) e então buscamos em nosso repositório o produto com o id que passamos;
- `update(id, body)` -> Passamos um *id* junto com um corpo de mensagem. Esse corpo, a princípio vamos deixar como nossa entidade *Product*, então devemos passar o Id e Name no corpo (iremos refatorar mais a frente)
- `create(body)` -> Chamamos o método *save* do repositório para criarmos o nosso novo produto;
- `delete(id)` -> Iremos refatorar mais a frente, mas só para mostrar um pouco de como podemos trabalhar com Kotlin, podemos usar o takeIf para validarmos e termos um retorno boolean, se for true ele executa a próxima ação.

Em seu Postman, antes de realizar as chamadas, adicione aos seus Headers o Content-Type - application/json, conforme imagem

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1631667817254/9oSW79gow.png)

Então vamos inserir alguns dados, chamando a url `http://localhost:8081/product` como `Post`, passando em `Body` o novo produto `Janela`
![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1631667757602/HuHeTBJrY.png)
Veja que o retorno foi o `Status 200` e teremos a Janela criada com o Id 1.
Adicione mais um produto chamado `Porta`, que irá retornar o Id 2. Perceba que com a anotação ` @GeneratedValue` no Id de nossa Entidade, os Produtos são criados com o Id automaticamente, de forma crescente.

Ao chamarmos novamente nosso `GET` que lista os produtos `http://localhost:8081/products`
 teremos os nossos novos Produtos: Janela e Porta.
```
[
    {
        "id": 1,
        "name": "Janela"
    },
    {
        "id": 2,
        "name": "Porta"
    }
]
```
Vamos agora testar se o método de *alterar* está funcionando corretamente. Iremos fazer uma requisição `PUT` em `http://localhost:8081/product/2` passando no corpo um novo nome, como por exemplo, *Porta de Alumínio* e o Id 2. (**Obs:** Você deve ter reparado que não ficou muito bacana essa função mas fique tranquilo pois Iremos refatorar mais pra frente)

Se fizemos uma requisição `GET` para listar a *Porta*, chamando `http://localhost:8081/product/2` a aplicação deverá retornar exatamente o que solicitamos, *Porta de Alumínio*. 

Para finalizarmos os testes iniciais, vamos testar se a aplicação está deletando corretamente um Produto. Vamos fazer a requisição `DELETE` no Postman, passando a url `http://localhost:8081/product/2`. Agora se tentarmos buscar o produto de Id 2, a aplicação irá exibir erro 500. 

**Legal! Tudo funcionando até aqui! Mas será que pode melhorar?** Sempre!

Essas chamadas ao repositório direto na controller pode não ser um problema nesse momento mas caso nossa aplicação precise de algumas regras dentro das funções o controller vai acabar ficando grande e isso acaba não sendo uma boa prática. Uma solução, seria criarmos uma camada de Serviço, onde podemos criar funções específicas com algumas regras dentro. Por exemplo `buscarProdutoPorNome` ou outra função específica. Vamos então ao nosso próximo passo, criando o **Service**.

Crie um package chamado `service` e vamos criar um contrato para nosso serviço, vamos criar uma `Interface` do Kotlin chamada `ProductService`. Vamos anotar a nossa interface com `@Service`
```
package com.kotlin.crudexample.service

import com.kotlin.crudexample.model.Product
import org.springframework.stereotype.Service

@Service
interface ProductService {
    fun getAll(): List<Product>
    fun getById(id: Long): Product
    fun create(product: Product): Product
    fun update(product: Product): String
    fun delete(id: Long): String
}
```
Agora vamos para a implementação dessa interface, vamos criar nesse mesmo package a classe kotlin `ProductServiceImpl`. Aqui, nossa IDE pode nos ajudar ao implementar a interface  `class ProductServiceImpl : ProductService`, podemos implementar os metodos da interface, ficando assim:
```
package com.kotlin.crudexample.service

import com.kotlin.crudexample.model.Product

class ProductServiceImpl : ProductService {
    override fun getAll(): List<Product> {
        TODO("Not yet implemented")
    }

    override fun getById(id: Long): Product {
        TODO("Not yet implemented")
    }

    override fun save(product: Product): Product {
        TODO("Not yet implemented")
    }

    override fun update(product: Product): String {
        TODO("Not yet implemented")
    }

    override fun delete(id: Long): String {
        TODO("Not yet implemented")
    }
}
```
Agora vamos injetar o repositório e fazer as chamadas nas funções
```

package com.kotlin.crudexample.service

import com.kotlin.crudexample.model.Product
import com.kotlin.crudexample.repository.ProductRepository
import org.springframework.stereotype.Service

@Service
class ProductServiceImpl(val productRepository: ProductRepository) : ProductService {

    override fun getAll(): List<Product> = productRepository.findAll()

    override fun getById(id: Long): Product = productRepository.getById(id)

    override fun create(product: Product): Product = productRepository.save(product)

    override fun update(product: Product): String {
        if (!productRepository.existsById(product.id))
            return "Produto não existe na base de dados"

        productRepository.save(product)

        return "Produto id ${product.id} alterado com sucesso"
    }

    override fun delete(id: Long): String {
        if (!productRepository.existsById(id))
            return "Produto não existe na base de dados"

        productRepository.deleteById(id)

        return "Produto removido com sucesso!"
    }
}
```

Repare que para obtermos os produtos ou criarmos, não colocamos regra, já para update e delete, coloquei uma validação e um retorno, informando se o Produto não existir na base ou se alterado/deletado com sucesso. 

Você vai perceber que o código poderá quebrar aqui, pois teremos que ajustar nossa controller para ficar dessa maneira:

```
@RestController
class ProductController(val productService: ProductService) {

    @GetMapping("/products")
    fun getAll(): List<Product> = productService.getAll()

    @GetMapping("/product/{id}")
    fun getById(@PathVariable(value = "id") id: Long): Product = productService.getById(id)

    @PutMapping("/product")
    fun update(@RequestBody product: Product) = productService.update(product)

    @PostMapping("/product")
    fun create(@RequestBody product: Product) = productService.create(product)

    @DeleteMapping("/product/{id}")
    fun delete(@PathVariable(value = "id") id: Long) = productService.delete(id)
}
```

Agora nossa aplicação já tem as rotas necessárias para criar, ler, alterar ou deletar produtos. Em nosso controller, fizemos a chamada para nosso service, que por sua vez poderá conter algumas regras antes de chamar o repository. No exemplo mostrado, o `update` e o `delete` no service, retorna uma mensagem fixa mas podemos retornar um Status Http ou alguma outra mensagem parametrizada (podemos deixar em variaveis separadas), enfim, dá para alterar de acordo com a nossa necessidade. Aqui, tentei deixar o mais simples possível, mas em um post futuro podemos dar uma turbinada nessa aplicação.

Agora partiremos para a documentação da api com o  [Swagger](https://swagger.io/), então nos vemos no próximo post que vc pode acessar clicando aqui [https://rafaelmoura.dev/kotlin-rest-api-completa-03](https://rafaelmoura.dev/kotlin-rest-api-completa-03)

E lembre-se, irei postando o código no Github, acesse [aqui](https://github.com/rafmoura25/rafaelmoura.dev) .

Dúvidas até aqui? Reclamações, sugestões? Pode mandar uma mensagem :D 

