Introdução aos microservices com Spark: retorno de dados dos correios

A arquitetura dos projetos tem sofrido uma tremulação na força com o advento da organização em microserviços. Já discutimos bastante sobre os conceitos de microserviços, o Raphael nos mostrou como montar um bom projeto e depois falamos como melhorar a forma de publicação em um servidor de aplicação.

Várias teorias e casos de sucessos já são um tanto conhecidas em artigos e vídeos de apresentações. Estamos recebendo um bom tanto de informações sobre os benefícios de organizar as nossas aplicações em microserviços. O que quero mostrar aqui é um caso bem simples e palpável que utilizei em um projeto que está em produção.

Um dos problemas quando tem-se um hype sobre uma técnica é que podemos estar fadado ao uso em demasiado dessa técnica, mesmo em casos que não faça sentido. Sou do ponto de vista que partir desde o começo para uso de microserviço em um projeto pode ser um problema. O monolito pode ser uma boa estratégia inicial e migrar microservicos que façam sentido depois (claro que tem discussões sobre isso). Foi a estratégia que seguimos.

Criamos a app web monolítica e estava encaminhando bem, até que os stackholders decidiram iniciar outro projeto, menor, mas em paralelo com o atual. Reorganizamos a equipe e começamos o novo projeto. Havia algumas semelhanças entre os dois, e uma delas, é que ambos necessitam de dados sobre correios.

  • Precisamos da listagem de estados.
  • Através de um estado, precisamos das cidades.
  • Através do IBGE, precisamos da cidade relativa.
  • Com um CEP, precisamos dos dados de endereços.

Isso é bem comum em várias aplicações, e poderia fazer sentido para uma terceira que possa aparecer. Com isso, centralizamos em um ponto único, que foi desenhado como um microserviço. Assim, dentro desse projeto contém:

  1. Regras, dados e acessos a qualquer domínio sobre Correios.
  2. Telas HTML.
  3. Banco de dados.
  4. Porta de entrada REST com retorno JSON.

Como um microserviço  é parte de um todo (que é o sistema), acredito que deve fazer parte da mesma rede interna em que estão publicados os demais serviços ou projetos. Assim, neste caso, não há acessos externos e questões de segurança foram designados a outros serviços. Ainda, publica-lo tem que ser uma tarefa fácil e rápida.

Dentre as opções de frameworks, escolhemos o Spark Java e a documentação dele é simples e rápida. O Spark utiliza um Jetty embedded para subir a aplicação, então, bastamos executar a classe Main para deixá-lo no ar. Ele utiliza métodos estaticos para suas configurações, e ainda, usamos Java 8 para deixar mais claro as configurações dos endpoints.

Na classe principal executora, podemos configurar as portas e os endpoints.

import static spark.Spark.*;

public class Main {
        public static void main(String[] args) {
                port(4567);
                new Initializer().init();
        }
}

A classe Initializer possui configurações extras e inicializa os endpoints. Segue um exemplo.

import br.com.jopss.microservico.correios.infra.CorreiosEndpoints;
import br.com.jopss.microservico.correios.infra.JsonTransformer;
import br.com.jopss.microservico.correios.dominio.ICorreiosService;
import static spark.Spark.*;

public class CorreiosEndpoint extends CorreiosEndpoints {

        public void publish() {
                get("/:versao/correios/ufs", "text/plain", (req, resp)) {
                        ICorreiosService servico = super.getServico(req, ICorreiosService.class);
                        return super.retornar(servico.buscarUfs(), resp);
                }, new JsonTransformer());
        }
}

O Spark disponibiliza método estáticos get e post para adicionarmos as nossas regras. Esses métodos possuem três parâmetros:

  1. Uma String contendo a URL.
  2. Outra String contendo o Content-Type.
  3. Um objeto da interface Route, que possui um único método com Request e Response, e como tal, podemos usar o Lambda do Java 8.

A indicação que iremos retornar como resultado um JSON é feita pela classe JsonTransformer. Há dois detalhes a serem vistos nesse código acima, que foi feito para o nosso projeto.

Primeiro é a identificação da versão na URL. Essa versão irá verificar internamente qual ação corresponde aquela versão. Assim, podemos ter publicado, para um mesmo Service, duas ou três versões distintas. Quando faço

super.getServico(req, ICorreiosService.class)

Internamente ele identifica qual versão daquele serviço será injetado (utilizamos o Spring IoC para isso).

O segundo detalhe é o retorno. A chamada a

super.retornar(servico.buscarUfs(), resp);

Cria o retorno de um objeto único Resposta, a ser encapsulado os dados e mensagens.

Essas configurações são passos extras que pode deixar seu microserviço um pouco mais inteligente. A versão e a resposta única padroniza os dados e as regras, melhorando a manutenção. Podemos por exemplo adicionar um novo campo de resposta ou uma nova versão de regra sem quebrar os acessos já existentes.

O exemplo acima mais detalhado com a configuração total pode ser conferido no github.

  1. Clone o projeto: git clone https://github.com/jopss/microservico-correios.git
  2. Execute o maven: mvn clean install.
  3. Configure a base de dados PostgreSQL (“bd_correios_desenv” com o schema “main”).
  4. Importe na sua IDE e execute a classe Main (como uma aplicação java desktop mesmo).
  5. Acesse pelo navegador: http://localhost:4567/

13 Comentários

  1. Renato Freire 26/10/2016 at 09:55 #

    Parabéns pelo post. Spark e micro serviços nasceram um para o outro. (Só um detalhe, tem no texto “Apache Spark” mas na verdade esse framework é o Spark Java, Apache Spark é para computação distribuída. Eles poderiam ter escolhido um nome que não confundisse tanto hehehe)

  2. João Paulo Sossoloti 26/10/2016 at 10:27 #

    Olá Renato. Obrigado pela correção. Alterei no texto 🙂

  3. Felipe Miguel 26/10/2016 at 12:31 #

    Muito bom, parabéns pelo texto.

  4. Luciana 26/10/2016 at 20:56 #

    Ótimo post… Parabéns!!

  5. Fabricio Vallim 01/11/2016 at 11:51 #

    Ótimo post. Simples e conciso!

  6. Nei 03/11/2016 at 18:56 #

    Parabéns pelo post!!!

  7. Nei 03/11/2016 at 19:01 #

    Apenas uma dúvida:

    Como toda solução, esta arquitetura traz algumas desvantagens: como integração entre os módulos, mais deploys (embora possa ser automatizados), questões de segurança nesta nova aplicação entre outras.

    Caso as aplicações que dependam deste código centralizado do correios fossem todas feitas na msm linguagem (java por exemplo), qual a vantagem de ter microservices sobre ter um novo módulo (um novo jar), visto que um novo módulo não teria as desvantagens acima?

    Obrigado

  8. João Paulo Sossoloti 04/11/2016 at 13:03 #

    Olá Nei. Um dos pontos dos microserviços sobre o monolito esta em isolar o acomplamento (e manutenção) de cada módulo. Imagine você poder alterar um módulo com a certeza que não irá impactar nos demais. Ou corrigir uma falha sem parar a aplicação toda. Mas como você disse, quanto mais componentizado seu domímio, mais dificil é a questão da infra, como a publicação.

  9. GUSTAVO LEITÃO 08/11/2016 at 23:17 #

    Olá Amigos, Para facilitar ainda mais o uso do spark framework desenvolvi uma camada acima que chamei de easy-spark. Nessa camada criei anotações para facilitar a criação de controladores bem como preenchimento automático de objetos a partir dos parâmetros das requisições, evitando um trabalhinho chato e recorrente no uso do spark. Quem tiver interesse em conhecer mais e/ou colaborar entra no nosso site!! https://github.com/logiquesistemas/easy-spark

  10. William 02/12/2016 at 20:51 #

    Excelente post João, direto ao ponto e sem enrolação. Vlw pelo conhecimento…

  11. Henrique 11/12/2016 at 14:34 #

    Uma curiosidade: a princípio me parece que seria também um bom caso de uso pro vraptor. Ele foi considerado?

  12. Fideles 15/02/2017 at 13:55 #

    João Paulo, gostei muito do artigo, a minha dúvida é quanto aos testes neste caso, existe alguma solução para rodar testes automatizados para uma solução alicerçada em microserviços? Como garantir a qualidade do software?
    Num mundo ideal onde a granularidade dos microserviços cheguem a classes, ou seja, cada classe tem seu microserviço e responde a suas regras de negocio o problema de duplicidade pode ser contornado, mas como trabalhar com um dicionário de serviços tão extenso, como evitar a duplicidade de serviços?

  13. João Paulo Sossoloti 15/02/2017 at 14:17 #

    Olá.

    Bom, a ideia do microserviço é responsabilizar, agrupar e desacoplar um determinado domínio/regras bem definidas dentro de um mesmo sistema. Fazer um microservico por classe, bem, acho que não seria o caso…

    Quanto a testes, se for unitário e microserviços sendo desacoplados, bastaria testes com JUnit e demais suportes (mocks talvez) como de costume.

    Abs.

Deixe uma resposta