Primeiros passos com Micronaut
Na história anterior expliquei a motivação para o surgimento do Micronaut e o que esse framework trás de inovador para competir com gigantes como o Spring. Caso ainda não tenha lido essa artigo, recomendo a leitura (link) para entender melhor alguns conceitos por trás desse framework que vem se destacando no mundo Java.
Agora que já temos vários conceitos apresentados, vamos fazer alguns experimentos simples para descobrir na prática se o Micronaut consegue ser tão simples como os seus concorrentes.
Caso queira pular toda a explicação e ir direto ao código, aqui está o link do projeto no GitHub.
Pré-requisitos
Antes de começarmos, certifique-se de ter instalado na sua máquina as seguintes ferramentas:
- JDK 1.8+
- Maven(preferencialmente) ou Gradle
- Docker
- Package Manager: Homebrew (preferencialmente) ou SDKMAN
- IDE com suporte para Java
Tudo pronto? Então vamos para a prática
Micronaut CLI
Para facilitar a criação de novos projetos, o Micronaut dispõe de uma Command Line Interface (CLI) bem simples porém bastante útil.
A instalação dessa ferramenta, em sistemas UNIX, pode ser feita de forma extremamente simples através do Homebrew ou SDKMAN.
Instalação via Homebrew:
$ brew install micronaut
Instalação via SDKMAN:
$ sdk install micronaut
Para confirmar que o Micronaut CLI foi instalado corretamente, execute o seguinte comando no terminal:
$ mn --version
O comando acima deverá retornar a versão do Micronaut e a versão da JVM instaladas em seu computador. Algo semelhante a isso:
| Micronaut Version: 1.1.2| JVM Version: 1.8.X_XXX
Caso tenha dúvidas na instalação, este link para a documentação pode te ajudar.
Podemos ver a lista dos principais comandos da CLI através do seguinte comando:
$ mn --help
Segue a lista de comandos , em destaque, e de suas respectivas descrições, exibidos ao executar a instrução acima:
- create-app : Creates an application
- create-cli-app : Creates a command line application
- create-federation : Creates a federation of services
- create-function : Creates a serverless function application
- create-profile : Creates a profile
- help : Prints help information for a specific command
- list-profiles : Lists the available profiles
- profile-info : Display information about a given profile
É necessário que toda instrução seja iniciada com "mn", da mesma forma que fizemos anteriormente para verificar a versão e para listar os principais comandos da CLI.
Caso seja executado apenas o comando mn no terminal, é possível executar o Micronaut CLI em modo interativo, que é recomendado para execução de comandos para geração de código, além de possuir auto-complete através da tecla TAB para comandos específicos do Micronaut.
$ mn
| Starting interactive mode…
| Enter a command name to run. Use TAB for completion:
mn>
Hello, Micronaut!
Chegou a hora de dar os primeiros passos com o Micronaut. E nada mais natural do que começar com o tradicional Hello World.
Neste exemplos simples vamos criar um novo projeto utilizando Java com linguagem, Maven para gerenciamento das dependências e build do projeto e por fim vamos criar uma imagem utilizando o Dockerfile gerado pelo Micronaut para rodar nossa aplicação em um container docker.
Para criar nossa aplicação usaremos o seguinte comando
$ mn create-app com.jvoliveira.hello-world -f management,micrometer -b maven
Explicando um pouco do comando acima:
- create-app: cria um projeto web-app
- com.jvoliveira.hello-world: o projeto terá como package base 'com.jvolivera' e o nome do projeto será 'hello-world'.
- -f : também poderia ser escrito como --feature, indica quais dependências serão adicionadas ao projeto. Nesse caso incluimos o management para gerenciar alguns endpoints providos pelo micronaut e o micrometer para adicionar algumas informações de métricas relevantes.
- -b: também poderia ser escrito como --build, indica que utilizaremos o maven para gerenciar as dependências e realizar o build do nosso projeto. Lembrando que o por padrão, caso o comando -b não seja informado, o padrão do Micronaut é utilizar o Gradle.
Após o maven instalar as devidas dependências ao executar o comando acima, será criada uma nova pasta chamada 'hello-world', que é o nome que demos ao nosso projeto. Entrando na pasta do projeto, você encontrará uma estrutura semelhante a da imagem abaixo.
Agora vamos criar um Controller para atender requisições HTTP utilizando o método GET no endpoint /greetings. Para realizar essa tarefa, poderíamos simplesmente criar uma nova classe e escrever nosso método, porém a CLI do Micronaut pode nos ajudar nessa tarefa. Na raiz do projeto hello-world, execute o seguinte comando:
$ mn create-controller com.jvoliveira.resource.GreetingsController -l java
Esse comando irá gerar uma classe chamada GreetingsController no pacote com.jvoliveira.resource. A classe gerada será semelhante a esta abaixo:
Vamos subir nossa aplicação e verificar se o nosso controller funciona. Como muita coisa no Micronaut é resolvida em tempo de compilação, conforme apresentado no artigo anterior é recomendado compilar os fontes da aplicação antes de executar a mesma. Utilize o seguinte comando na raiz do projeto para subir a aplicação.
$ mvn compile exec:exec
Será feito o build do projeto e o mesmo irá subir na porta 8080. Para alterar a porta na qual a aplicação irá subir, basta definir/alterar a seguinte propriedade no arquivo application.yml
micronaut: server: port: 8080
Agora que nossa aplicação está sendo executado e ouvindo requisições HTTP na porta 8080, vamos realizar uma requisição simples através de uma outra janela no terminal para verificar se o nosso controller está mesmo funcionando. Execute o seguinte comando no terminal:
$ curl -v http://localhost:8080/greetings
A saída deve ser algo semelhante a imagem abaixo:
Note que o retorno da nossa requisição contém o status 200 OK, justamente o que foi configurado para ser retornado pelo GreetingsController, conforme linha 12 desta classe.
Além da criação do GreetingsController, o comando create-controller do Micronauto CLI ainda nos dá como bônus a classe de teste GreetingsControllerTest.
Ao executar os testes é importante observar que é iniciada uma instância da aplicação para executar o testes nessa instância. Esse comportamento pode gerar conflito de portas uma vez que a porta utilizada para o teste é a mesma para execução da aplicação realizada previamente. Antes de rodar os testes verifique se não existe nenhuma instância da aplicação em execução.
Agora vamos evoluir um pouco nosso GreetingsController. No momento temos uma única rota que retorna apenas um status 200. Vamos criar uma nova rota /greetings/{nome} que receberá requisições do tipo GET e irá retornar uma mensagem de boas vindas, para o nome passado na URL.
Vamos definir nossa nova rota em uma nova interface que será implementada pelo GreetingsController.
Criamos a interface acima para definir novas rotas para o GreetingsController. Temos uma nova rota acessada pelo método GET recebe um parâmetro via URL chamada name. Podemos acessar esse parâmetro programaticamente pelo argumento name do método greeting, onde o Micronaut já faz o binding dos valores de forma automática por terem o mesmo nome. Nosso método greeting retonar um objeto do tipo HttpResponse que recebe uma String como generics. O objeto HttpResponse permite configurar diferentes aspectos da resposta dada pelo server, como status e cabeçalhos.
O próximo passo é fazer com que o GreetingsController implemente a interface GreetingsControllerRoutes.
Agora GreetingsController implementa o método greeting definido na interface GreetingControllerRoutes. A implementação desse método é bem simples, apenas retornar um objeto HttpResponse com o status 200 OK e o corpo do response é formado por uma concatenação de Strings.
Um ponto ainda não explicado até aqui o atributo greeting definido na classe GreetingsController. Você deve estar se perguntando de onde veio esse valor, pois bem, a anotação @Value permite acessar valores definidos no arquivo application.yml.
Note que no application.yml foi definida a propriedade greeting.message, a mesma que está sendo acessada no anotação value.
Vamos executar novamente nossa aplicação e fazer uma nova requisição. Novamente irei utilizar o curl mas caso prefira usar alguma ferramenta para realizar requests como o Postman, deve funcionar da mesma forma.
Com a aplicação em execução, execute o seguinte comando:
$ curl -v http://localhost:8080/greetings/joao
O resultado deve ser algo semelhante a este:
Note que na última linha é retornada a mensagem "Hello, joao!". Nosso controller agora tem o comportamento que desejamos, no entanto precisamos evoluir nossos testes para cobrir o novo método criado.
Na classe GreetingsControllerTest que foi gerada automaticamente pelo Micronaut CLI teste o endpoit /greeting criando um client genérico através do EmbeddedServer injetado na classe de testes. Isso é interessante porque pela primeira vez estamos falando sobre como é feita injeção de dependências no Micronaut, porém o processo de criar um client genérico dessa forma é um pouco mais verboso.
Vamos tirar proveito da nossa interface GreetingsControllerRoutes para criar um client específico e de forma muito mais simples, que irá facilitar nosso teste.
Vamos começar a análise pelos novos atributos. Foi criado o atributo greetingMessage anotado como Value, nada diferente do que fizemos anteriormente. O destaque aqui fica para a interface GreetingsControllerRoutes que foi injetada no nosso teste. Mas espera aí, não existe uma implementação para essa interface! Pois bem, o Micronaut cria a implementação dessa interface durante a compilação, e utiliza como referência a anotação Client, que literalmente cria um client, nesse caso associado a rota /greeting. Essa forma de criar clients é interessante porque incentiva a separação da definição de rotas em interfaces, gerando "contratos"pra API de forma separada de sua implementação. Sem dúvida, uma boa prática.
Por último, temos nosso novo teste greetingsByNameTest. Agora com a instância do client já em mãos, fica fácil fazer uma requisição ao método greetings passando um nome como argumento do método e obtendo um objeto HttpResponse cujo corpo do response é uma String.
Monitoramento
No momento que criamos nosso projeto, adicionamos algumas dependências que não foram abordadas até o momento. O Management e o Micrometer nos dão outros endpoints que podemos utilizar para obter informações sobre a nossa aplicação.
Segue lista de endpoints disponíveis:
/beans
— Informações sobre os beans já carregados/info
— Informações estáticas da aplicação providas ao implementar um bean para InfoSource/health
— Disponibilidade da aplicação/metrics
— Metricas (disponibilizado pelo Micrometer)/refresh
— Recarrega Beans (@Refreshable
)/routes
— Informações de rotas disponíveis/loggers
— Informações sobre log.
É possível ver a lista de features disponíveis para o Micronaut utilizando o seguinte comando:
$ mn profile-info base
Docker e Micronaut
Agora que nossa aplicação está finalizada, vamos criar uma imagem dela e subir um container com o Docker.
O boilerplate do projeto já nos dá um Dockerfile pronto para criamos nossa imagem. Para esse exemplo não vamos modifica-lo, mas para exemplos reais é interessante fazer modificações de fino ajuste, como configuração de memória disponível, porta onde a aplicação será executada ou adição de algum parâmetro específico para executar a aplicação.
Antes de gerar nossa imagem, é necessário gerar um executável da aplicação, um .jar, que será utilizado pelo Dockerfile para construção da imagem. Para isso, execute o seguinte comando na raiz do projeto.
$ mvn clean package
Isso irá executar os testes existentes na aplicação e caso os testes passem irá gerar o executável hello-world-0.1.jar dentro da pasta target. Agora podemos criar a imagem docker da nossa aplicação utilizando o seguinte comando:
$ docker build -t jvoliveira/hello-world:0.1 .
Para verificar se a imagem foi mesmo criada, poderá consultar as imagens utilizando o comando:
$ docker images
Com a imagem criada, agora só falta iniciar um container utilizando a nossa nova imagem. O comando abaixo realiza esse procedimento:
$ docker run -d -p 8090:8080 --name micronaut jvoliveira/hello-world:0.1
Nesse comando estamos expondo nossa aplicação na porta 8090 do host, então para acessar os endpoints da nossa aplicação, vamos utilizar o curl novamente
$ curl -v http://localhost:8090/greetings/joao
Se tudo deu certo, será exibido a resposta do servidor com o status 200 OK e na última linha será exibida a mensagem "Hello, joao!"
Conclusão
Esse foi apenas um exemplo bem simples de como criar rapidamente uma API com testes e executar dentro de um container Docker, além de apresentar o Micronaut CLI e algumas features disponibilizadas pelo Micronaut.
Quer ver o código na integra? Aqui está o link para o GitHub.
Em breve, novos artigos falando um pouco mais sobre Micronaut.
Referências
- Cloud Native with Micronaut — https://medium.com/@mesirii/cloud-native-with-micronaut-733c9784850f
- Micronaut Documentation — https://micronaut.io/documentation.html
- Micronaut Guides — https://guides.micronaut.io/