Microservices: Não crie problemas

Marcos Stefani Rosa
Bemobi
Published in
7 min readSep 2, 2020

--

Microservices já se tornou uma realidade no mercado de desenvolvimento. Em tempos onde tudo está na nuvem e as plataformas como AWS, GCP e Azure (para não dizerem que eu esqueci da Microsoft) tem ganhado cada vez mais o mercado com suas soluções. Consequentemente, a utilização de microservices vem ganhando mais forma e mais maturidade. Empresas como Netflix, Amazon e Nubank vem se mostrado à frente do seu tempo e têm se posicionado no mercado como influenciadoras nesse assunto.

No entanto, Microservices não é uma bala de prata. Estamos vindo de anos e anos de projetos monólitos (apesar de não serem chamados assim em sua época) e temos trazido todos os hábitos e padrões de arquitetura no nosso DNA. Muitas vezes também precisamos lidar com a convivência de mais de um modelo de arquitetura e essa convivência requer muito cuidado, principalmente no que tange a não utilização de hábitos e de alguns padrões legados quando vamos trabalhar com microservices. Por isso, resolvi levantar aqui alguns cuidados necessários para evitar que o uso de microservices não se torne um problema (boas lembranças do meu eterno Tech Leader Mauricio Pelissari).

Responsabilidade de Negócio:

Não há como falar de microservices sem iniciarmos o assunto falando de responsabilidades, afinal, o que mais facilita o bom uso e desenvolvimento com microservices é exatamente a responsabilidade de cada aplicação.

Esse tópico é o que define o ponto de corte que dirá o quão “micro” um microservice deve ser. Por incrível que pareça esse é o ponto mais difícil de se estabelecer: “Até onde vai a responsabilidade de um microservice para começar a de outro”.

Ao contrário do que muitos pensam, microservice não é separar uma parte do seu sistema, por mais que no final ele pareça estar fazendo isso. O mau uso desses conceitos pode transformar sua aplicação em um micro-monólito ou a ser tão micro ao ponto de inviabilizar a manutenção da arquitetura. Vejam o exemplo abaixo que conheci a partir de uma palestra de Eduardo Souza:

Estrelas da Morte: Representação comunicação Microservices Amazon e Netflix

Quem sou eu na fila do pão para criticar a arquitetura de ambas as empresas acima?! Estou trazendo essa imagem com o intuito de mostrar como que uma estrutura de microservices pode se tornar complexa a longo prazo, o que torna extremamente importante tomar conhecimento da granularidade dos seus serviços, facilitando a comunicação e evitantado a desordem e o caos.

Exemplo Prático:

Supomos que vamos desenvolver um gateway de pagamento, onde teremos comunicação com diversas empresas que farão a cobrança propriamente dita com as operadoras de cartão. Porém, preciso controlar qual empresa utilizarei com base em algumas regras cadastradas. No caso, teríamos o seguinte cenário:

Imagine que temos um microservice para cada empresa, um para administração das regras e um para cadastro de cliente. Só nessa brincadeira teríamos que construir sete microservices para uma única solução. Agora levante o custo com suporte e melhorias dessa solução mantendo N nós para cada microservice. É fato que dependendo do cenário isso se faz necessário, mas será que eu devo utilizar microservice mesmo para todos os cenários? Será que eu realmente necessito impor esse nível de complexidade na minha solução?

Recentemente, Sam Newman nos trouxe em uma apresentação da QCon 2020 (Fonte InfoQ) alguns questionamentos nesse sentido. No caso, ele relatou no aspecto da decomposição do monólito mas também nos trouxe questionamentos sobre a real necessidade de utilizar monólito para tudo, e como pontuou Edilson Azevedo em seu Linkedin sobre esse assunto “Newman aconselhou as pessoas a se concentrarem no resultado e não na tecnologia”.

Uso de Conectores:

Uma solução muito perspicaz para esse tipo de problema é a utilização de conectores que são nada mais do que bibliotecas. Sim, parece uma ideia muito antiga mas que faz todo o sentido quando utilizamos algo que tem total relação com um terceiro e/ou parceiro. Com Maven por exemplo, podemos criar uma biblioteca de comunicação para cada empresa. Essa biblioteca se preocupa em apenas se comunicar com a empresa terceira, assim temos vários ou apenas um conector (dependendo do cenário) que pode ser importado pelo próprio arquivo pom de configuração. Dessa maneira, construiremos apenas um microservice que receberá as requisições e decidirá qual conector utilizar de acordo com as regras que esse microservice é responsável.

Segue aqui um link do site oficial com explicação sobre multi módulos dentro de uma aplicação: https://maven.apache.org/guides/mini/guide-multiple-modules.html

Banco de Dados Exclusivo:

Uma das definições mais polêmicas sobre microservices é a utilização exclusiva de um banco de dados. Alguns torcem o nariz para esse tipo de solução por ter o conceito muito forte de comunicação via API como problema pra futuras consultas. Pois bem, esse é um dos principais motivos que se deve respeitar um banco de dados exclusivo por microservice. Quando plugamos relatórios e outras fontes de dados ao banco abrimos espaço para sobrecarga desse banco dependendo do cenário. E também não resolve colocarmos databases diferentes em um mesmo banco de dados pois isso também pode prejudicar muito e causar outros tipos de problemas. O ideal é separar mesmo e deixar o microservice trabalhar de maneira independente.

Mas então, como faço com os dados para meus relatórios?

Atualmente as principais plataformas (GCP, AWS, etc) já possuem uma solução para migração de dados de bancos. Recentemente tive o prazer de trabalhar com o DMS da AWS e fiquei muito feliz com o resultado. No caso, replicamos todos os dados quase em tempo real, de um banco para outro. Além disso existem outras soluções também como o Debezium, citado por Bruno Santos em seu artigo aqui do medium. Dessa forma, você pode criar um banco apenas para esse fim, e se possível, já transformar o dado de forma que no banco de destino já possua os dados formatados e distribuídos evitando um excesso de JOINs em tabelas gigantescas e fazendo a consulta dos dados mais performática.

Ok, e como fazer com consulta de muitos dados via API, não vai impactar na minha performance?

Fato é que, se você precisa buscar uma gama muito grande de dados de um determinado domínio você certamente tem problemas com o primeiro tópico deste artigo. Se você precisa tratar muita informação da fonte (real time) é um sinal claro de que as responsabilidades de negócio não estão bem distribuidas e possivelmente você necessitará migrar parte do código para dentro do microservice responsável. Mas, caso seja necessário uma gama de consultas grande para geração de processos, sugiro também a solução anterior onde terá o dado quase em tempo real em um banco para consultas. Leve isso como regra evitando impactos no fluxo principal de responsabilidade do microservice.

Versionamento de APIs:

Versionar API é um hábito muito importante, porém, deve ser usado de forma consciente e clara. Se questionar o que leva o uso do versionamento de uma API é a chave para evitar que seu microservice se torne um micro-monólito com diversas versões para se fazer a mesma coisa.

O versionamento de API deve sim ser utilizado, com ele você pode manter versões rodando em paralelo e evitar impactos ao consumidor dessa API. Mas é sempre importante lembrar de algumas dicas:

Sempre acorde um prazo de uso da API substituída.

Toda mudança deve ser documentada e o uso da versão pode ajudar os consumidores a terem um tempo de migração, porém essa migração deve ter um prazo pré-estabelecido, mesmo que seja longo. Manter duas versões rodando pode custar mais caro em termos evolutivos pois necessitará sempre levar em consideração para novos desenvolvimentos contemplando todos os cenários possíveis.

Se o contrato for totalmente diferente, crie um microservice novo

Sim, pode parecer estranho, mas se você vai mudar padrão de comunicação onde as transformações serão incompatíveis com a que você tem ou que vão gerar muito codigo desnecessário, é melhor criar um novo serviço. Por exemplo, se você está migrando de uma comunicação onde você recebia um XML via SOAP e agora vai fazer uma nova comunicação via REST, faça o novo desenvolvimento em um novo microservice para isso pois possivelmente as transformações e a manipulação de dados são totalmente diferentes e usam bibliotecas totalmente diferentes. Para esse caso especificamente, dois microservices podem acessar o mesmo banco de dados mas levando em consideração de que a questão do contrato de validade do microservice antigo seja aplicada a curto prazo.

Em comunicações assíncronas use Filas no lugar de chamadas a API:

O uso de filas é algo muito interessante quando falamos de comunicações assíncronas. Isso porquê você garante que a mensagem gerada na fila será processada e que haverá um controle da vazão que essa fila irá processar. Isso é benéfico em diversos aspectos inclusive no controle de consumo de memória do seu serviço quando se trata de processamento de grande volume de informação.

Estude os tipos de Controller do Kubernetes:

Sim, parece básico que entender como funciona algo antes de usar é saudável, mas é isso mesmo que quero dizer. O Kubernetes possui vários tipos de Controllers e cada uma tem seu benefício. Digo isso pois já presenciei ecossistemas inteiros em Clusters onde tudo era Deployment e já assisti alguns palestrantes que citam isso como insignificante, mas saiba que com a maturidade de suas aplicações isso pode se tornar um problema. Um exemplo clássico é o uso do Controller Job para execuções extremamente pontuais, afinal se você necessita rodar algo apenas em momentos específicos não há necessidade de deixar uma máquina ligada com alto grau de disponibilidade sabendo que não será utilizada.

Mais sobre os Controllers: https://kubernetes.io/docs/concepts/workloads/controllers/

Conclusão:

Claro que, em todos os pontos citados acima, me baseei em fatos que já vivenciei e que fazem parte do meu aprendizado e sei que cada um pode ser tema de outro artigo se formos entrar nos pormenores, mas o fato é que, alguns deles só foram identificados com a dor real do problema e achei importante levantar essa questão. Microservices é algo fantástico e que resolve muita coisa, porém é necessário ter cuidado para que a solução não cause novos problemas, ou se causarem, que não sejam maiores dos que o seu ecossistema já possui.

--

--

Marcos Stefani Rosa
Bemobi
Writer for

Systems Developer for more than 15 years, postgraduate in Software Engineering with Agile Methods, has relevant experience in Java, Python, JavaScript & others