Passo a passo de como criar seu Pokedex com Spring WebFlux

Ana Beatriz - Apr 21 '20 - - Dev Community

Oláaa neste artigo você irá aprender a criar uma aplicação de Create, Read, Update e Delete usando Spring Webflux, com os dados salvos num banco MongoDB e por fim hospedado no Heroku!

Repositório do projeto criado: https://bit.ly/pokedex-reativo

Ao final deste tutorial, você terá seu próprio pokedex, e poderá atualizar, deletar e criar seus pokémons no seu próprio repositório, com conceitos de reatividade usando Events Streams, e Endpoints Funcionais de forma simples e usual, um projeto bem bacana pra ter como portfólio!
Curtiu? Então vamos lá... (ɔ◔‿◔)ɔ

Setting up the project

O que você precisa ter instalado antes de começar:

  • JDK 8 ou 9
  • Uma IDE para o desenvolvimento, eu vou usar o IntelliJ
  • O Heroku CLI
  • Possuir o Maven 3 com suas variáveis de ambiente configuradas
  • Postman

É muito importante que você tenha o Maven 3, pois o Heroku CLI só roda nessa versão... Caso você não o tenha Clique aqui e instale o maven na versão correta para fazer sua aplicação subir corretamente ;)

Agora você deve acessar o site Spring Initialzr , e selecionar as seguintes dependências:

  • Spring Reactive Web
  • Spring Data Reactive MongoDB
  • Embedded MongoDB Database

No Group usaremos: com.pokedex
No Artifact: reactiveweb

O package name é gerado automaticamente!

Depois de gerado todas as variáveis seu Spring Initializr deve ficar assim:
tela do spring initializier com todas as configurações que o usuário escolheu de acordo com o tutorial

Prontinho, agora é só clicar em GENERATE CRTL+, e o spring initializr irá gerar um arquivo em ZIP e você deve extrair esse arquivo num diretório que você consiga localizar depois.

Importando o projeto

Depois de extraído o arquivo você deve abrir sua IDE, no caso vou abrir o IntelliJ e cliar em import project

imagem do intellij idea logo que abrimos

Clicando em import project você verá todos seus diretórios, basta localizar onde estar o projeto que você baixou do Spring Initialzr, e importá-lo clicando em ok
imagem dos diretórios no windows

Irá aparecer uma janela com duas opções, você deverá escolher: Maven, pois nosso projeto será importando através de um modelo que já existe
escolhendo maven como projeto base
Agora você pode clicar em finish e será carregado todo seu projeto com as variáveis de ambiente! :p

Configurando o pom.xml

Para nossa aplicação subir corretamente, precisamos colocar na ordem correta a dependência do mongo embbeded, ele deve ficar antes do <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
, seu pom.xml ficará da seguinte forma:

pom.xml
basta exlcuir o <scope>test</scope> e colocar a dependency do mongo embeded logo em seguida.

Isso fará com que nossa aplicação suba na ordem correta, se essa dependency estiver depois igual estava, nossa aplicação não iria subir no servidor, pois a mesma não iria encontrar, sua depedendy deverá ficar da seguinte forma:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
</dependency>

Subindo pela primeira vez a aplicação

Vamos ver se nossa aplicação está subindo, no canal do youtube da womakerscode temos um vídeo onde explicamos sobre o servidor Netty, caso tenha alguma dúvida só dar uma passadinha por lá.
Subindo a aplicação iremos ver nosso servidor em ação na porta 8080.

Vou colocar um "hello world womakerscode" padrão na classe main()

Você verá que que no console nosso System.out.println irá mostrar a mensagem, e também será sinalizado a porta que nossa aplicação está
console da aplicação

Criando um Model

Para realizarmos os testes no banco de dados precisamos que nossa aplicação tenha os dados para serem salvos, e também para que os métodos do MongoDB sejam encontrados. Nele ficará salvos nossas variáveis.

Para criar o package clique com o botão direito em com.pokedex.reactiveweb > New > Package > com.pokedex.reactiveweb.model > enter
package model sendo gerado no intellij

Dentro dele crie uma nova classe com o nome Pokemon , dentro dessa classe será colocado as informações que os pokemons terão, tais como Id, Nome, Poder etc... Dentro dessa classe coloque:

Agora só precisa gerar os getter e setters , boolean equals , hashcode , e o toString , você pode fazer tudo isso apertando ALT + INSERT > getter/ setter > equals() hashcode() > toString() > Override Methods > Constructor

Pode ir fazendo um seguido do outro, o IntelliJ ou IDE que você estiver utilizando irá gerar todos esses métodos pra você!

Criando um repository

No repository é onde fica a persistência dos dados, para criá-lo clique com o botão direito em com.pokedex.reactiveweb > New > Package > com.pokedex.reactiveweb.repository > enter

Dentro desse package será criado uma interface com o nome de PokemonRepository

Interface é uma tipo de abstração que usamos para especificar o tipo de comportamento que as classes devem implementar

Dentro nessa interface coloque o seguinte código:

O Spring Data já facilita muito nossa vida, sendo assim, a única coisa que precisamos colocar nessa interface é uma especificação que ela irá funcionar com o objeto Pokemon String.

Se você clicar no ReactiveMongoRepository irá ver o que o ele tras por debaixo dos panos, tais como métodos mono e flux, findByID, finalAll etc..

Agora sim podemos Inicializar nosso banco de dados (͠≖ ʖ͠≖)👌

Inicializando o MongoDB

Estamos usando o Mongo Embeded, sendo assim, não é necessário que você crie um conta ou etc, esta tudo local na nossa máquina.

Na classe Main() criaremos uma chamada do nosso banco de dados, usando o CommandLiner que é uma interface funcional que recebe variável de argumentos em cadeia.. o mesmo deve estar anotado com o @Bean para o spring boot entender que aquilo deve ser processado, enfim, o resultado deve ser o seguinte código:

Aqui vemos algo de diferente, nao estamos trazendo os métodos padrões (findAll, findById etc) por que estamos trabalhando com um banco de dados embutido, mas caso você queira usar um Mongo Atlas por exemplo, o ReactiveMongoOperations gera essa série de métodos pra você.

Nesse código o .flatMap consulta o findAll e imprime os dados, e o thenMany faz isso também só que de forma assíncrona, e o subscribe imprime na tela também

Agora se a executarmos veremos que o banco de dados subiu, e os dados inseridos serão mostrados na tela, você terá algo assim:
console mostrando os dados que foram salvos

Criando o Controller

Controller é nossa classe de controller, nela estarão nossos métodos de criação, update, delete e leitura dos nossos pokémons.

Para criá-lo clique com o botão direito em com.pokedex.reactiveweb > New > Package > com.pokedex.reactiveweb.controller > enter
E repetir esse mesmo processo para criar uma classe

Agora bora criar os métodos que já estamos chegando no resultado final (•◡•) /

Buscando todos os pokemons

Agora você deve criar uma instância e dentro dela terá o método para trazer todos os pokémons, ficará assim:

Bucando o Pokémon por id

Para buscar por id, colocaque o método dentro da instância, igual o anterior:

Você que nesse método está o Mono tipo pokémon? Isso significa que queremos apenas que 1 id seja chamado, e quando esse id não for encontrado, trazemos uma reposta HTTP.

O defaultIfEmpty é usado para quando o Id é chamado numa entidade vazia, ele retornará um status NOTFOUND

Lembre-se de que o .map() deve retornar uma função síncrona e o resultado será capturado em um editor Mono, pois findById retorna um Mono

Criando e salvando um novo Pokémon

Para criar um novo e salvar pokémon basta digitar o código abaixo

Retornará um Mono por que é o que será retornado no repositório.

Atualizando um Pokémon

Agora vamos combinar tudo o que você já viu até aqui para atualizar um Pokémon. Para atualizarmos um pokémon receberemos um id, e uma instância do pokémon com novos valores a serem atualizados.

Vamos começar procurando por id, e se ele for encontrado será atualizado ao contrário será recebido um status NOTFOUND

A segunda parte do código um operador map para essa transformação, e assim se um Mono vazio for retornado com defaultEmpty ele retornará um status NOTOFUND, esse bloco de código poderia também ser feito com if/ else, mas o jeito que fizemos foi do modo declarativo

Excluindo um Pokémon

O método para exlcuir é semelhante aos anteriores também, para deletar quando criar um método que deleta um pokémon por id:

E outro método para deletar TODOS os Pokémons

Prontinho, agora temos nosso CRUD completo onde criamos de maneira reativa e funcional, agora precisamos criar nosso Event Stream (parte mais legal o/)

Criando um Event Stream ᕙ(`▿´)ᕗ

Para criarmos nosso método de eventos no controller, precisamos criar a classe onde estão os atributos do evento, para isso dentro do package model crie uma classe PokemonEvent , com os seguinte código:

`

public class PokemonEvent {
    private Long eventId;
    private String eventType;
}

Enter fullscreen mode Exit fullscreen mode


`

Da mesma forma que foi feito com o classe Pokémon dentro do package Model logo acima, só vai precisa gerar os getter e setters , boolean equals , hashcode , e o toString , você pode fazer tudo isso apertando ALT + INSERT > getter/ setter > equals() hashcode() > toString() > Override Methods > Constructor

showww, agora vamos voltar na classe PokemonController , e criar nosso método de eventos, que ficará da seguinte forma:

`

 @GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<PokemonEvent> getPokemonEvents() {
        return Flux.interval(Duration.ofSeconds(5))
                .map(val ->
                        new PokemonEvent(val, "Product Event")
                );
    }

Enter fullscreen mode Exit fullscreen mode


`

O interval traz o tanto de segundos que o meu evento será mostrado, nesse caso de 5 em 5 segundos um evento novo é gerado!

Estamos quase acabando, agora só falta testar nossos endpoints ( ͡~ ͜ʖ ͡°)

Testando a aplicação

Para testar nossa aplicação vamos ver se tudo irá subir certinho, para isso vá até o método main() e starte ele, e fique de olho no console para ver se não surge nenhum erro, a resposta deve voltar mais ou menos assim:
mensagem de êxito no console

Agora bora pro postman testar os endpoints

Buscando todos os Pokémons

O primeiro método que vamos testar é o GET, que vai trazer todos os pokémons que foram salvos, no postman ira trazer os seguintes resultados:
resultado da busca GET dos pokemons

Veja que na busca optamos por GET, pois estamos "pegando" todos os pokémons, para testar os demais como: "PUT", "POST" ... etc, basta mudarmos essa opção, então bora lá

Buscando um Pokémon por id

Para buscar por id, basta copiar e colar um id que foi gerado na resposta do GET
buscando um pokémon por id

Inserindo um novo Pokémon

No próprio postamn mude a requisição para POST e no Body selecione raw > JSON pois queremos salvar em formato json, e digite:

`

   {
        "nome": "IbySaur",
        "categoria": "Semente",
        "habilidades": "OverGrow",
        "peso": 13.0
    }

Enter fullscreen mode Exit fullscreen mode


`

Após isso, clique em SEND e seu pokémon será salvo!
Deverá retornar um STATUS 201* o que mostra que foi criado com êxito, para ter certeza, vamos voltar no GET , copiar e colar o id gerado, que ele mostrará nosso pokémon criado..

O id não precisa colocar pois ele é gerado automaticamente pelo MongoDB

Deletando um Pokémon

Agora vamos deletar os métodos deleteALL e deleted, sendo assim no postman vamos mudar a requisição para DELETE

deletando um pokémon no postman

Vejam que eu coloco o id de algum pokémon que eu queira excluir

Se eu tirar esse id ele vai exlcuir TODOS OS POKÉMONS

Testando o Events Stream

Vimos que nosso CRUD está no esquema já, ou seja, está funcionando bem lindão...
Com nossa aplicação iniciada ainda, vamos testar o events stream, para isso vá até um browser se sua preferência e digite:

http://localhost:8080/pokemons/events

E você vai ver a mágica acontecer a cada 5 segundo estará no retornando um evento de pokémons

resposta do browser na consulta de pokemons

Se você abrir essa mesma requisição em outra janela, você vai ver que no events stream vai permitir que outra requisição seja chamada sem esperar que a anterior termine, isso é uma chama não-bloqueando, típica do Spring Webflux.

Deploy no Heroku

Shoooow agora você já tem uma aplicação topzera, bora subir ela na nuvem?

Para subir nossa aplicação, precisamos colocar o plugin do heroku no nosso pom.xml:

`

<project>
  ...
  <build>
    ...
    <plugins>
      <plugin>
        <groupId>com.heroku.sdk</groupId>
        <artifactId>heroku-maven-plugin</artifactId>
        <version>3.0.2</version>
      </plugin>
    </plugins>
  </build>
</project>

Enter fullscreen mode Exit fullscreen mode


`

Agora vá até a pasta da sua aplicação, usando um terminal (eu vou usar o gitbash) , e digite heroku login , para acessar sua conta no heroku

Irá aparecer uma janela assim pra você:
login heroku

Clique em Login e já pode fechar essa janela que você estará com o login ativo no Heroku!

Agora é só esperar o terminal processar
terminal processando o login no heroku

Depois que terminar de carregar, iremos criar um app para hospedar nossa aplicação, faremos isso com o comando heroku create

E estamos quase lá, seu app foi criado, no terminal ele vai te gerar um link e essa mensagem:
mensagem de que o app foi criado com sucesso

Estamos prontos pro Deploy

Se seu aplicativo é um standalone (e, portanto, requer um tipo de processo), você pode implantar com este comando: mvn clean heroku:deploy
Só esperar um segundinhos e verificar os logs...

Se a mensagem BUILD SUCESS aparecer significa que tudo deu certo, pode respirar de alívio!
build feito com sucesso

PARABÉNSSS sua aplicação subiuuu
Will Smith dançando

Para acessar basta digitar heroku open
dashboard do heroku

Conclusão

Spring Webflux é um módulo muito bacana no SpringBoot, com ele além de criamos um CRUD podemos criar uma sequência de eventos, mas isso não quer dizer que sempre devemos usar aplicações reativas, tudo depende do seu cenário e da viabilidade

...

E se você chegou até aqui parabéns você é um vencedor, acredito que não existe nada mais recopensador do que ver a nossa aplicação funcionando né? Então compartilhe também, e coloque no github para que mais e mais pessoas possam aprender assim como você! ʕ•́ᴥ•̀ʔっ

O código desta aplicação está no meu Github ->
https://bit.ly/pokedex-reativo
Dê uma estrelinha lá no github se te ajudou <3

Referências:
Heroku DevCenter
Canal do Youtube da Michelli Brito

Nos vemos na próxima, dêem likesss e compartilhem, e vejam os outros artigos da womakerscode quem quiser me seguir nas redes socias @anabneri #tamojunto

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .