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:
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
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
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
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>
, seu pom.xml ficará da seguinte forma:
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
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á
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
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:
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;
}
`
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")
);
}
`
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:
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:
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
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
}
`
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
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
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>
`
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ê:
Clique em Login e já pode fechar essa janela que você estará com o login ativo no Heroku!
Agora é só esperar o terminal processar
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:
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!
PARABÉNSSS sua aplicação subiuuu
Para acessar basta digitar heroku open
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
Top comments (10)
Apesar, de não programar em java, gostei muito do artigo parabéns!
hahaha valeu!!!
Excelente! Sempre bom ver um fluxo de desenvolvimento completo 100% implementado! Top
Bacana né hahaha valeuzão, fico feliz que tenha curtido
Parabéns Ana, mandou muito bem!
App super interessante e fácil de entender como implementar :)
Obrigada pelo feedback Cyn ♥️
Otimo artigo! Já construi a aplicação mas ainda não fiz o deploy no heroku.
Já estou construindo uma aplicação em front para consumir essa api
uaaaaauuu parabéns, quando terminar me marca no linkedIn linkedin.com/in/anabeatrizdev/
Excelente artigo, parabéns Ana.
Top Parabens.