Microsserviços fora da caixa!
Introdução
Talvez o tema que mais tenha me estressado em toda minha carreira seja este, Microsserviços! Todo lugar que eu passo me perguntam sobre, todo cliente que eu vou, quer implementar, e, de fato, trabalhei efetivamente em apenas três projetos de sucesso com Microsserviços até hoje.
A ideia deste artigo é justamente dismistificar muita coisa que envolve este assunto, colocar os possíveis pontos positivos, negativos e principalmente as mudanças que tem de haver na sua empresa/time para suportar isto tudo.
Solução como um todo
O primeiro ponto em definir uma boa solução é olhar o cenário como um todo. Se você propôe algo novo e não pensa no time que vai implementar isto depois, você já começou errado. Então, se você está pensando em implementar Microsserviços na sua empresa e não tem um dos itens abaixo, talvez seja interessante repensar sobre o assunto:
- Cultura Ágil - Não basta utilizar GIT com deploy automatizado. A agilidade tem que estar no sangue da sua empresa. Se você recebe mudanças com frequência, precisa refatorar itens e isto é um problema para você, neste novo modelo, você está escalando o problema.
- Bom processo de DevOps - Se você precisa parar o time para fazer o deploy de uma aplicação, imagina de 50, ao mesmo tempo! Pois é, sem um bom processo, uma boa automação, sua vida neste cenário pode se tornar um inferno.
- Time Capacitado - Quando falamos de Microsserviços, para mim, a grande vantagem é o desacoplamento entre eles, permitindo que cada um seja escrito em uma linguagem e use uma fonte de dados diferente da outra por exemplo. Se você tem "padrão de projeto/arquitetura", tem um time focado em .NET, que não transita fácil entre tecnologias, você está perdendo a oportunidade de criar um cenário de Microsserviços bom!
- Infraestrutura Moderna - Se subir máquina, descer máquina, criar containers e afins são um problema para você (O que não deveria ser), este pode ser um grande impeditivo. Na dúvida, vai pro Azure e seja feliz <3
Arquitetura de Microsserviços VS Microsserviço
Movendo adiante, temos que entender que Microsserviço pode se referir tanto a arquitetura da solução, quanto a um único serviço. Lembre-se que você pode ter um cenário com múltiplos serviços, se comunicando, sem a necessidade de ter Microsserviços.
Sabendo que um Microsserviço é uma pequena unidade, responsável por uma (ou um conjunto pequeno) de funções, possuindo sua própria fonte de dados, uma arquitetura baseada em Microsserviços nada mais é do que o conjunto de vários destes serviços pequenos, se comunicando entre sí.
Podemos ainda manter o conceito de Contextos Delimitados, criando regiões de Microsserviços, porém um Contexto Delimitado pode conter vários Microsserviços. Deixo esta colocação pois um cenário comum é ter um serviço por Contexto Delimitado, se comunicando entre sí. Este também é um ótimo cenário, mas estamos falando de serviços maiores.
Ao descer ao nível de um Microsserviço, nos referimos as vezes a uma simples função. O Netflix por exemplo tem um Microsserviço com apenas um método, para listar os filmes. Ponto!
Neste modelo, um cenário que podemos assimilar é o FaaS, onde temos funções como serviços. Itens pequenos, de única responsabilidade, fáceis de testar, fáceis de refatorar, escaláveis indepententemente. Você pode ter isso tanto em APIs (node, .NET, PHP...) quanto nas Azure Functions ou Lambdas (AWS).
Em resumo, o que temos são:
- Serviços ou funções pequenas
- Uma (ou pequeno conjunto) de responsabilidade
- Sua própria fonte de dados
- Seu próprio processo de deploy
- Sua própria máquina (Ou container)
- Fácil de alterar
- Fácil de testar
- Fácil de reescrever
- Requer pouco conhecimento de negócio (Específico para aquele ponto)
- Escalonamento independente
DDD, CQRS e afins ainda cabem neste cenário?
Falamos sobre Domain Driven Design acima, quando citei Bounded Contexts, então alguns conceitos do DDD ainda podem ser mantidos aqui, porém como seus serviços serão pequenos, temos que ter um cuidado imenso com Overengeneering.
Quando desenhamos uma arquitetura para um projeto grande, separamos ele em algumas frentes, tais como:
- Leitura
- Escrita
- Cenários Simples (CRUD)
- Cenários Complexos (Regras Complexas, Testes de Unidade... )
Desta forma, quando temos um Microsserviço, nós reduzimos tudo isto. Você não vai ter mais uma aplicação grande, então não precisa mais se preocupar em atender todos os tipos de cenário.
CQRS mesmo é um item que quase cai em desuso, dado que em diversos cenários vamos ter um serviço para leitura e outro para escrita.
O que quero dizer é que em cenários onde você vai criar um Microsserviço onde o mesmo será apenas um CRUD, você pode simplesmente utilizar o Scaffolding do ASP.NET, ou se for mais esperto, utiliza o CosmosDB do Azure que já fornece uma API pronta para CRUD nos bancos SQL Server, Mongo e DocumentDb!
Ao partir para cenários com regras mais complexas, provavelmente você vai separá-los em Microsserviços, tendo por exemplo um serviço apenas para calcular o ICMS. Neste cenário você provavelmente vai usar OOP, criar um código mais testável, mas mesmo assim será pouco em relação ao que você teria em um cenário com serviços maiores.
O ponto é que com serviços menores, você pode focar mais no problema e menos na "arquitetura" do projeto. O que resolve seus problemas hoje? Nos cenários que trabalhei, 80% eram CRUD, dos 20% que sobraram, uma boa OOP + Repository Pattern já resolviam o problema. Foram realmente poucos casos onde precisei criar Commands, ter Agregados, Agregados Raiz e até mesmo injeção de dependência. Keep it stupid simple!
Para resumir, se chegasse hoje uma requisição para você criar uma API que recebe uma lista de produtos/quantidade vendida e dá baixa no estoque, o que você utilizaria para modelar/desenvolver esta aplicação? Qual seria a forma mais simples e eficaz de fazer isto?
Miscigenação de Tecnologias
Este para mim é o ponto mágico dos Microsserviços, e aqui você e sua equipe tem de ter a mente aberta para novas tecnologias, pois este é o lugar de dar asas a nossa criatividade.
Com a criação de serviços independentes, nada impede que criemos um serviço com ASP.NET Core + SQL Server, e do lado um com Node + MongoDb, afinal bancos relacionais e NoSQL tem suas características, assim como ASP.NET e Node e podem atender cenários diferentes. Em um dos casos que atuei, havia uma boa gama de serviços em Java, com alguns novos serviços já sendo feitos em ASP.NET Core, pela velocidade (1,15mi RPS), bem como serviços como log, que tem informações mutáveis, recebidas via JSON, estavam sendo confeccionados em Node/Mongo (JSON everywhere).
Além disso, você pode usufruir dos novos recursos como BaaS e APIs sobre os bancos. O cenário que eu citei acima de ter um CRUD direto na API do Mongo (Fornecida pelo Azure CosmosDB) é totalmente aceitável. Se você não tem regras de negócio, por que perder tempo codando CRUD? O mesmo vale para o Firebase, que fornece Real-Time Database e é ótimo para esquemas de notificações. O fato é que você tem que pensar fora da caixa, pois agora não estamos falando apenas de ASP.NET, é um mundo maior.
Também citei acima o FaaS (Functions As A Service), que são funções simples e com um único objetivo. Muitas vezes você vai precisar escrever um código, subir uma máquina, para ter apenas um cálculo de ICMS, não seria mais interessante jogar isto em um Azure Functions, que escala automático, tem documentação, suporta padrão REST/HTTP e afins?
Concluindo, se você montar um cenário de Microsserviços e focar apenas em ASP.NET Core/SQL Server, você pode estar perdendo o que melhor ele tem para te oferecer!
Escalonamento
Com os serviços sendo disponibilizados de forma individual, uma vantagem desta arquitetura também é poder escalar um serviço independente. Por exemplo, se você tem um serviço que lista seus produtos, que provavelmente será muito mais utilizado que o serviço de realizar pedido (As pessoas veem muito antes de comprar), você pode escalá-lo independentemente, sem a necessidade de escalar os demais serviços. Na verdade os Microsserviços agem de forma totalmente independente, acho que ficou claro isto.
Quando falamos em escalonamento, eu sempre penso em valores. Dizer que sua aplicação será mais escalável por utilizar Microsserviços eu acho um tanto exagerado. Você consegue ter um bom escalonamento em uma aplicação monolítica, mesmo que precise escalar ela como um todo, assim como você pode fazer caca ao tentar escalar um serviço pequeno. Quer um exemplo? Se você escala a aplicação e esquece do banco dela (Sim, cada Microsserviço tem seu próprio banco), boom!
Outro ponto que coloco é que toda máquina, por menor que seja tem seu custo mínimo. É comum criarmos cenários com mais máquinas menores do que poucas maiores. Isto deve-se ao fato de pagar pelo quanto consumimos, então se você tem uma máquina muito potente, a chance dela ficar sub-utilizada é grande.
No caso dos Microsserviços, enquanto antes você teria uma única aplicação, em uma única máquina, agora você tem 50 micro aplicações, em 50 máquinas menores. Por mais que estas máquinas sejam pequenas, há um custo mínimo sobre elas, que você deve calcular antes.
Este cenário muda um pouco no ambiente FaaS, onde pagamos apenas pela execução dos serviços.
Armazenamento de Informações
Uma das premissas dos Microsserviços é que cada um tenha sua fonte de dados, ponto! Lembra que comentei que eles devem agir de forma independente? Então.
Se você sente dificuldades em manter 2 ou 3 bancos de dados, deve estar preparado para manter 50 (Claro que menores). Em suma, é mais trabalho para o time de infraestrutra e de banco de dados.
Os Microsserviços se comunicam entre sí, mas nunca acessam o banco um do outro. Ou seja, se você precisa da lista de produtos, você faz uma requisição para o serviços de produtos e não uma conexão ao banco dele.
Lembre-se que as regras (Tanto de leitura quanto escrita) da aplicação ficam na aplicação e não no banco, desta forma é imprescindível que você faça a chamada à API e não a fonte de dados dela.
Para piorar um pouco o cenário, lembre-se que não necessariamente teremos apenas relacionais aqui. Podemos ter NoSQL, Azure Table, Redis e uma infinidade de fontes de dados, depende do que o serviço precisa fazer.
API Gateway
Neste ponto você deve estar se perguntando, mas se eu tiver 200 serviços, como ficam minhas requisições? E minha autenticação? E meu cache? Para isto e muito mais, existem as APIs Gateways, em destaque o Kong (Open Source), API Management (Azure) e APIGee (Google).
Um API Gatway funciona como uma fachada para as suas APIs. Ele pode receber um Token, verificar o usuário autenticado, disparar 20 requisições, para 20 APIs diferentes, concentrar o resultado disso tudo e te devolver. Ou seja, uma única requisição, um único retorno, mas com engajamento de 20 Microsserviços.
O API Management por exemplo, se conecta facilmente ao Identity Server ou AD B2C (Fantástico por sinal), e prove uma camada de autenticação para suas APIs. Tudo que você precisa é da API documentada.
Você pode plugar suas APIs REST nele, pode obter resultados de um serviço SOAP por exemplo e converter para JSON, pode limitar a quantidade de requisições de um usuário a sua faxada de API e muitos outros pontos. Tem realmente vários recursos.
Além disso, você pode plugar um serviço de cache (Como Redis) e cachear os resultados, evitando requisições a sua API.
Embora o API Management (Azure) não seja tão barato, ele resolve muito bem estes problemas citados.
Autenticação
Eu citei acima a autenticação, e esta é uma das mudanças que ocorrem neste cenário. Quando você tem apenas uma aplicação, você pode simplesmente implementar uma autenticação baseada em token e pronto, mas e quando você precisa que o mesmo token sirva para várias APIs?
Neste caso, devemos isolar a autenticação da API, ou seja, criar uma API para autenticação apenas, ou melhor ainda, utilizar algo pronto, como o Identity Server. Ele vai permitir que façamos esta autenticação para múltiplas APIs.
Uma menção aqui para o novo Active Directory B2C, que funciona como um autenticador e te dá 50.000 usuários gratuitos! Acho muito válido testá-lo!
Chamadas à serviços
Imagina que você precisa da lista de produtos, mas quando requisitou, o serviço de listagem de produtos não respondeu? Este é um dos pontos que também devemos tratar.
O Retry Pattern cuida bem disso, onde fazemos novas tentativas de requisições caso a anterior falhe, de forma configurável. O Polly é uma biblioteca .NET que faz bem este papel.
De qualquer forma, você tem que garantir que seu serviço funcione, independente de outros serviços. Caso não seja possível obter a lista de produtos de outro serviço, qual decisão será tomada?
Transações de Longa Duração
Este é um problema comum, e chato de resolver. Imagina que você tem uma transação que passará por vários serviços diferentes, como você garante o Commit/Rollback disso tudo?
Basicamente, para cada operação que temos, devemos ter uma ação compensatória caso esta mesma falhe! Eu poderia escrever sobre isto, mas talvez não ficasse tão bem explicito quanto o Elemar colocou neste post dele:
DevOps
Com tudo pronto, tudo publicado, chegou a hora de alterar um serviço.... não, pera.... 2 serviços, não, pera... 12 serviços... É comum que uma alteração em um serviço desencadeie alterações em diversos outros serviços (Lembrem-se sempre de versionar suas APIs), e desta forma, o que antes era 1 deploy, se tornou facilmente 20!
Sem um bom processo de DevOps, uma boa automação, CI/CD e vários outros pontos, eu arrisco dizer que é impossível ter um cenário de Microsserviços. Não tem como fazer 20 deploys na mão, não a tempo de uma nova versão impactar na outra.
Os containers ajudam bastante neste ponto, com máquinas menores, os deploys são mais rápidos, e com estratégias de hot-deploy eles também podem ajudar bastante. Vale mencionar também os novos modelos CaaS (Container As A Service) que temos no mercado!
Mas novamente, tudo isto sem um DevOps bem planejado é sinal de fracasso!
Mudança de Cultura
Se eu pudesse resumir a implantação de Microsserviços, eu diria que é uma mudança de cultura. É algo que não impacta apenas seu time de desenvolvimento, mas também infraestrutura e operações. Se sua empresa é da "nova safra" que adota agilidade dentre vários outros pontos que coloquei aqui, talvez seja algo tranquilo, agora se você nem testes de unidade executa, não tem um processo de DevOps, você tem um belo desafio pela frente.
Novamente, nada é impossível, mas você tem que ter a visão como um todo e parar de achar que Microsserviços é apenas uma mudança no "sistema da firma" e entender que ele faz parte de algo maior, e melhor na minha visão.
Mas cadê o código?
Se eu ganhasse 1 real por cada pedido de exemplo de Microsserviço que já recebi ou pedido de curso de Microsserviço, eu estaria rico hoje! Código é barato, e se você notar, eu falei tanto de Microsserviços aqui e citei pouquíssima coisa de código. Em suma, se você sabe escrever uma API bem, você (A nível de código) está pronto para Microsserviços. Agora o que precisa ser feito, e é a parte mais difícil, são conectar as peças, e isto é feito grande parte a nível de infra (API Gateway).
Ainda há sim mudanças no código como implementação de um barramento de eventos, ou SAGA ou mesmo o Retry Pattern, mas nada que você não encontre em uma boa pesquisada no Google :)
Coordenador de sistemas at Kumon América do Sul
4 aClareza!
Backend Developer at Zup Innovation
6 aEvandro Nascimento
Software Engineer | Back End Developer | .NET Core | C# |
6 aArtigo esclaredor sem entrar em detalhes de implementação.. mostrando os prós e desafios da implementação de microservices ajuda a todos e especialmente os entusiastas a ponderar quando e onde aplicar os microservices.. parabéns!!
Senior Software Engineer | Java, Spring Boot, AWS
6 aExcelente artigo!
Experienced software engineer with over 13 years of expertise in designing, developing, and implementing high-performance software solutions.
6 aParabéns pelo artigo!