Uma arquitetura de microserviços trás inúmeras vantagens. Por outro lado, organizar um sistema deste modo traz vários desafios. Nesta apresentação eu trago algumas lições aprendidas que podem ser úteis mesmo para equipes que não pretendem aderir completamente a esta nova tendência.
4. Alguns benefícios…
• Implementações mais rápidas
• Melhor ferramenta para cada problema
• Diferentes estratégias de escalonamento
• Deploys menores e isolados
5. … e alguns desafios
• Mais aplicações para manter
• Mais dependências no runtime
• Mais cenários de falha
• Mais necessidade de coordenação
6.
7.
8. O elefante na sala:
Se já sofremos para criar uma
única aplicação, como é que
microserviços pode nos ajudar?
Pequeno, se contado em LOC, depende muito da linguagem. Algumas centenas de linhas? Implementáveis em até duas semanas? Rule of thumb: código que cabe na cabeça do desenvolvedor.
Independência é a chave. Independência tem que ser tanto de desenvolvimento (requer pouca coordenação) e operação (deploy independente)
Problema específico: de preferência provendo uma capacidade completa
Colaboração é um dos maiores desafios de microserviços, e onde moram os maiores custos para sua implantação
Um serviço novo pode ser implementado em questão de horas ou dias. Por causa de independência entre serviços, é possível utilizar as melhores ferramentas para cada problema. Por exemplo, se o serviço for trabalhar bastante com XML, pode usar Scala que possui bibliotecas boas para isso. Se for fazer cálculos que precisem de performance, pode-se usar C, C++ ou Go. Se quiser ter algo rápido para validar uma idéia, pode usar Ruby ou PHP.
Esta independência também significa que serviços distintos podem ser escalados de maneiras diferentes. Por exemplo, um serviço de envio de email pode ser escalonado em vários servidores fracos, que passarão a maior parte do tempo fazendo IO. Já um serviço de transformação de dados talvez se beneficie de mais memória e CPU.
Outra vantagem dessa arquitetura é que serviços podem ser atualizados de maneira independente, diminuindo o risco de propagação de erros durante deploy. Além disso serviços leves possuem mais chance de poderem ser atualizados frequentemente.
Manter mais aplicações é um desafio, principalmente quando se usa diferentes tecnologias e potencialmente diferentes estratégias de build, deploy
Dependências podem virar um pesadelo se a equipe não tiver o perfil de disciplina e cooperação suficientes
Criar novas funcionalidades muitas vezes afetam vários serviços e dependendo de como as equipes são divididas, coordenar implementações pode ser mais complexo do que em projetos normais
Em resumo, com mais partes móveis, muitos problemas de se desenvolver uma aplicação única são amplificados
Mesmo que sua intenção não seja adotar este tipo de arquitetura, muitos dos desafios de implementá-la exigem que se aprendam novas técnicas que podem beneficiar qualquer projeto
Agora vou falar de algumas boas práticas exigidas numa arquitetura de microserviços.
Dificilmente você vai conseguir manter dezenas de serviços sem abraçar automação de todo o ciclo de vida do sistema. Ainda mais se decidir adotar diferentes tecnologias para cada serviço.
Na maioria dos projetos que vejo atualmente, desenvolvedores estão pondo a mão na massa e trabalhando com testadores e sysadmins para diminuir o tempo que uma alteração qualquer no código leva até chegar em produção.
Uma cultura de DevOps é essencial para essa arquitetura funcionar, e também deveria ser super bem vinda no seu projeto.
Outro assunto que muitos programadores acabam deixando para lá é monitoramento. Com microserviços, como dito anteriormente, os cenários de falhas serão mais frequentes, e se não houver uma disciplina de monitoramento é bem provável que alguns serviços se tornem uma dor de cabeça.
Trazer a definição de métricas para o começo do desenvolvimento ajuda a resolver isso, e é algo que se aplica a qualquer arquitetura. Ainda mais se a equipe tiver a chance de incluir métricas de negócio para seus sistemas de monitoramento. Desta forma se cria uma base para conversa e tomada de decisão valiosa para o projeto.
Quando você possui um serviço interno que é usado por várias outras partes da sua aplicação, um desafio é atualizar este serviço sem quebrar os consumidores. Muitas equipes começam sem nenhum controle nas atualizações. Outras, depois de se queimarem de alguma forma, passam a adotar um versionamento rígido para evitar problemas.
Uma alternativa é trabalhar com Consumer Driven Contracts, que são testes que se exercitam as funcionalidades de um serviço que interessam aos consumidores deles, e que indicam se uma mudança no serviço vai causar problemas no próximo release. Existem ferramentas como Pact, que gera e executa especificações para sistemas REST. Um teste deste tipo rodando como parte de integração contínua é excelente para indicar possíveis problemas.
Agora, para evitar estes problemas é que entra a Lei da Robustez. A Lei da Robustez diz que você precisa ser liberal com as requisições que seu serviço aceita, e conservador nas requisições feitas para outros serviços. Isso significa ser flexível na hora de desenhar APIs, e estar preparado para que elas possam mudar no futuro.
Muita da agilidade de microserviços vai ser perdida se você possui uma separação muito rígida entre analista, programador, testador e operações. Aliás, eu não duvido que seja impossível implementar essa arquitetura com equipes super especializadas que exijam muita coordenação.
O ideal nesse caso é ter uma equipe não apenas multidisciplinar, mas com habilidades sobrepostas, ou seja, com programador que entenda de operações, operações que entenda de programação, testador que entenda dos negócios.