Você está na página 1de 23

1

Arquitetura de
Microsserviços
Márcio Galvão
galva.marcio@gmail.com
2021

Neste artigo vamos discutir o uso de microsserviços no desenvolvimento de


aplicações para a nuvem, procurando ressaltar alguns benefícios e desafios.

O que são microsserviços?

Microsserviços é um tipo de arquitetura para desenvolvimento de


aplicações, que muitos consideram uma evolução da arquitetura SOA
(Service-Oriented Architecture) [1]. Uma outra opção de arquitetura é a que
divide a aplicação em camadas (n-tier), por exemplo, interface (web tier),
regra de negócios (middle tier) e banco de dados (data tier) [2]. Enquanto a
arquitetura em camadas produz aplicações tipicamente "monolíticas" onde
todas as funcionalidades rodam em um processo único, a arquitetura de
microsserviços permite construir aplicações compostas por pequenos
serviços integrados, cada um executado em um processo próprio.
2

Como veremos, idealmente cada microsserviço deve "fazer uma coisa só,
bem feita", entregando alguma funcionalidade de negócio em um contexto
bem definido.

O crédito das imagens abaixo é de Martin Fowler ([4] e [5]).

Fontes: [4] e [5]

Nas aplicações monolíticas, temos várias funcionalidades embutidas na


mesma aplicação, executadas como um único processo. Para escalar, é
preciso executar todo o processo em múltiplas máquinas.

Já na arquitetura em microsserviços temos funcionalidades executadas por


processos independentes que se comunicam. Neste caso, cada
microsserviço pode ser escalado de forma independente dos demais.

Benefícios
A abordagem de microsserviços traz vários benefícios.

• Sendo menores e mais focados, os microsserviços são mais fáceis de


desenvolver e manter.
• Os problemas (falhas) são mais bem isolados, reduzindo o blast radius
(raio da catástrofe em problemas que poderiam afetar toda a aplicação).
3

• Cada microsserviço pode ter seu próprio ciclo de vida e deploy


independente, o que torna rápido liberar novas funcionalidades para o
mercado.
• Microsserviços podem ser escalados individualmente, tendo suas
necessidades de processamento, banda de rede, armazenamento em
disco, segurança etc. atendidas de forma independente. Isso reduz
custos de hardware, pois é mais rápido e barato escalar um único
microsserviço que precisa ser acessado por milhares de usuários do que
ter que escalar toda uma aplicação monolítica, cujas diferentes partes
dificilmente têm a mesma volumetria.

Por exemplo, em uma aplicação de e-commerce há em um instante


qualquer provavelmente mais pessoas acessando a função de "catálogo de
produtos" (apenas navegando no site e consultando os produtos
existentes) do que pessoas acessando a funcionalidade "carrinho de
compras", e há menos pessoas ainda acessando a função "pagamento com
cartão" para efetivamente comprar alguma coisa.

Embora todas estas funções possam fazer parte de uma mesma aplicação,
os requisitos de escalabilidade, desempenho, tolerância, segurança e
resiliência de cada uma são diferentes. Se esta aplicação for construída de
forma monolítica não há muito o que fazer - ou se escala toda a aplicação
(incluindo as partes da aplicação que não precisam de muitos recursos), ou
o serviço (funcionalidade) que precisa de maior escalabilidade pode virar
um gargalo.
4

Por outro lado, se esta mesma aplicação de e-commerce for escrita com
arquitetura de microsserviços torna-se possível por exemplo ter um
microsserviço de "Catálogo" - projetado para atender muitos usuários
simultaneamente (escalabilidade), um outro microsserviço de "Carrinho de
compras", e talvez um terceiro microsserviço para "Pagamento com cartão"
(este com alguns requisitos específicos de segurança), e cada um estes
microsserviços pode ser dimensionado e escalado (em certos casos de
forma automática!) em função de suas necessidades específicas de
processamento, armazenamento ou banda de rede para suportar a
demanda.

Deploy independente, delivery contínuo


Do ponto de vista do processo de desenvolvimento, a adoção da
arquitetura de microsserviços representa uma mudança importante de
filosofia e de processos, para além das questões puramente técnicas.
Portanto, requer certa maturidade.

Por exemplo, os times precisam ter maior autonomia. Diferentes times


podem ficar responsáveis por desenvolver e manter este ou aquele
microsserviço e podem fazer seu deploy de forma independente dos
demais, fazendo com que ele melhore progressivamente e evolua de forma
autônoma, sem o risco (se todos os contratos forem respeitados) de
quebrar o resto da aplicação que está sendo construído por outros times.

Se tudo for planejado (e executado) de forma adequada, pode-se realmente


conseguir o desejado fluxo CI/CD, ou seja, trabalhar com integração
contínua (CI -Continuous Integration) e também com delivery contínuo (CD
- Continuous Delivery) de pequenas funcionalidades, o que é um objetivo
bom para os negócios, dado que novidades se tornam disponíveis para os
usuários de forma mais rápida porém difícil de alcançar na maioria das
empresas que desenvolvem software.
5

Tamanho não é documento


Apesar do nome "microsserviço", o tamanho não é o aspecto mais
importante, como ressaltado em [3].

O ponto mais importante ao se desenvolver um microsserviço é assegurar


que ele seja realmente autônomo, isto é, tenha alta coesão interna
(seguindo o “princípio da responsabilidade única”, ou seja, “fazer bem uma
coisa só”) e ao mesmo tempo tendo baixo acoplamento com outros
serviços (evitar dependências externas), para que possa ter deploy
independente e adotar suas próprias soluções de modelagem de dados e
de armazenamento, como ilustrado na figura seguinte. Cada microsserviço
também controla o seu próprio estado.

Fonte: [3] página 27

Desafios
Se por um lado a arquitetura de microsserviços traz benefícios, ela também
traz desafios e introduz um custo de maior complexidade que precisa ser
justificado - em oposição a apenas seguir o hype tecnológico do momento
(voltaremos a este ponto mais adiante).
6

Microsserviços geram aplicações distribuídas e com deploy mais complexo.

Em certos cenários podem ser a melhor solução, mas para outros cenários
o custo (complexidade) da abordagem pode não compensar os benefícios
que ela vai trazer. Muitas das tecnologias envolvidas são novas. O uso de
microsserviços requer certas competências de desenvolvimento, qualidade
(testes) e processos de DevOps, requerendo familiaridade com certos
patterns ou padrões desenvolvimento (por exemplo, padrões de resiliência)
e tecnologias.

O fato é que nem todos podem estar preparados para criar, fazer o deploy
e em seguida dar conta do suporte e da manutenção de microsserviços
seguindo as boas práticas recomendadas.

Martin Fowler alerta sobre isso com sua habitual clareza em [4] e [5], e
sugere inclusive que existe uma "altura mínima que um time de
desenvolvedores precisa ter para utilizar microsserviços", uma lista de pré-
requisitos que uma equipe precisa satisfazer em termos de maturidade e
capacidade técnica para que possa entrar no jogo dos microsserviços.

Fontes: [4] e [5]

Um dos requisitos ressaltados por Fowler é a "cultura (maturidade) de


DevOps", uma vez que o gerenciamento de deploy em microsserviços pode
se tornar bastante complexo, dado que há muitas partes móveis e
independentes para se gerenciar, que precisam ser monitoradas de forma
independente e apresentar resiliência em caso de falhas. Vejamos alguns
dos principais desafios na adoção dos microsserviços com um pouco mais
de atenção.
7

1. Fronteiras entre serviços (boundaries)

O primeiro desafio do desenvolvedor que deseja entrar no jogo dos


microsserviços é ser capaz de definir suas fronteiras (context boundary). O
que cada microsserviço deve fazer? Quando a responsabilidade funcional
de um microsserviço acaba e começa a de outro, em uma aplicação que
pode ter dezenas ou centenas destas partes móveis?

Não há regra geral para se definir a fronteira (escopo) de um microsserviço.


Em linhas gerais, a linha de corte entre um serviço e outro deve ter relação
com as capacidades de negócio (funcionalidades) que o microsserviço
pretende entregar.

Há algumas boas práticas, entretanto.

Se duas ou três operações são executadas sequencialmente, com alta


coesão e dependência entre elas, provavelmente faz sentido mantê-las em
um mesmo microsserviço, pois se forem separadas em dois haverá baixa
coesão em cada um e alto acoplamento entre ambos, o que é o oposto do
que se deseja (alta coesão interna e baixo acoplamento externo).

Se, por outro lado, surge a impressão de que o microsserviço está fazendo
muitas coisas diferentes em oposição a "fazer uma coisa só, bem feito" isso
sugere que talvez ele possa ser dividido em dois ou mais microsserviços
autônomos. Há alguns patterns que podem ajudar na identificação destas
fronteiras, como o BC (Bounded Context).

Um ponto importante é a compreensão de que um mesmo objeto (ou


entidade) pode ter representações (atributos, comportamentos) diversas
em diferentes contextos de uma mesma aplicação.

Por exemplo, um objeto denominado PRODUTO pode ter poucos atributos,


todos mantidos em uma tabela única mantida por um microsserviço que
cuide da venda de produtos (talvez apenas o ID do produto que se queira
vender seja suficiente). Ao mesmo tempo, uma imensa quantidade de
atributos pode ser criada para modelar a mesma entidade PRODUTO em
um microsserviço de catálogo de compras, por exemplo, que precisa ter
maior detalhamento sobre cada produto vendido.
8

Os nomes das entidades e de seus atributos também podem variar em cada


domínio de utilização do objeto. Por exemplo, para um serviço que cuida
de compras, o usuário é um COMPRADOR, mas para um outro serviço que
cuida de CRM (Custom Relationship Management) o mesmo usuário é um
CLIENTE. Já para um serviço que controla pagamentos este mesmo usuário
poderia ser chamado de PAGANTE.

Como ressaltado em [3], o desenvolvedor não deve brigar contra isso,


tentando unificar a terminologia ou padronizar os tipos e quantidades de
atributos que todos os domínios devem utilizar. Em vez disso, deve "aceitar
as diferenças" de percepção do mesmo objeto em diferentes domínios de
utilização em uma mesma aplicação. Como fica esta questão de "aceitar as
diferenças" na abordagem dos microsserviços? A figura seguinte mostra
uma ilustração onde uma mesma entidade (na vida real, "pessoas") é
representada de forma diferente em vários contextos de uso, em diferentes
microsserviços, sendo denotada por diferentes termos.

Fonte: Referências [3] - pg 36.

A figura mostra vários contextos diferentes para microsserviços que tratam


uma mesma entidade por diferentes nomes. Em um contexto, a entidade é
chamada USERS, em outro, é BUYERS, em outro, é PAYERS, em outro, é
CUSTOMERS.
9

A entidade PAYERS existe apenas no contexto do microsserviço Payment, e


lá terá os atributos que precisa ter para atender aos requisitos de lógica de
negócio deste serviço. Atributos criados para a entidade PAYERS no
microsserviço Payment não estão presentes no modelo de dados do
microsserviço Customer Service, onde as mesmas pessoas do mundo real
que são PAYERS agora são chamados CUSTOMERS. Lá os atributos são
outros. O mesmo ocorre no microsserviço Orders and Registration, onde a
mesma pessoa que pode ser um CUSTOMER e um PAYER agora é chamada
de BUYER. Naturalmente, há entidades que precisam compartilhar a
mesma IDENTIDADE entre os diferentes domínios de cada microsserviço.
Afinal, a mesma pessoa que é um PAYER será também um BUYER e um
CUSTOMER, e esta é a mesma pessoa identificada como USERS no
microsserviço Conferences Management da figura anterior.

O usuário que acessa o site, registra-se como cliente, compra algum


produto ou serviço e faz um pagamento é o mesmo, mas a forma como ele
será modelado (nome, atributos) pode (e deve) variar em cada domínio /
modelo de dados de cada microsserviço. Assim, há um aspecto de uma
pessoa que é compartilhado entre todos os modelos de dados dos
diferentes microsserviços (a sua IDENTIDADE - quem aquela pessoa é de
fato, para fins de autenticação por exemplo), mas em cada domínio podem
existir diferentes atributos. Isso é bem diferente de compartilhar a mesma
entidade com os mesmos atributos em todos os serviços que precisem dela,
como ocorre em muitos casos em arquiteturas monolíticas. Com
microsserviços, um atributo de uso obrigatório em um contexto pode ser
de uso opcional em outro.

2. Persistência de dados e consistência eventual

Resolvida a questão da fronteira, um segundo desafio para o desenvolvedor


é ser capaz de desenhar seus microsserviços de forma que cada um
contenha (de forma autônoma) sua própria lógica e modelo de dados. Se
cada microsserviço não encapsular seus próprios dados (e tiver
dependência de acessar outros microsserviços por conta disso, gerando
acoplamento) não será possível que cada microsserviço tenha deploy e ciclo
de vida independente, e assim ficaria comprometido um dos pilares básicos
deste tipo de arquitetura - o princípio da autonomia de cada microsserviço.
10

Na prática, isso significa que em muitos cenários será preciso renunciar à


consistência forte dos dados oferecida pelas transações ACID (Atomicidade,
Consistência, Isolamento e Durabilidade) em bancos de dados relacionais.
Em uma aplicação composta por vários microsserviços com estruturas de
dados distribuída, não será mais possível fazer transações ACID envolvendo
dados que estejam em microsserviços diferentes. Como boa prática,
nenhum microsserviço jamais deve fazer acessos diretos em tabelas de
outros microsserviços em suas próprias transações.

Em vez disso, o desenvolvedor terá que recorrer a patterns que assegurem


a chamada consistência eventual dos dados (eventual consistency) [6] - o
que é, diga-se de passagem, perfeitamente aceitável em inúmeros cenários
- mesmo em alguns que tradicionalmente utilizam consistência forte ou
imediata assegurada pelos bancos relacionais e transações comitadas em
bloco.

Um exemplo tornará esta importante restrição mais clara.

Suponha que em uma aplicação de e-commerce existam dois


microsserviços, um denonimado Catalog (catálogo de produtos) e outro
denominado Basket (carrinho de compras). Cada serviço tem seu próprio
modelo de dados e seu próprio banco, como deve ser.

Fonte: Referências [3]

Suponha ainda que um usuário da aplicação selecionou um produto do


catálogo, após consultar seu preço (ProdPrice), e incluiu na sua cesta de
compras, e que depois disso, o preço do produto foi alterado.

Naturalmente, o preço terá que ser alterado também na tabela do


microserviço Basket, ou o cliente pagará o preço desatualizado. Porém, o
11

modelo de arquitetura de microsserviços não permite (ou melhor, não


recomenda) que seja feita uma query pelo microsserviço Catalog para
atualizar diretamente o preço do mesmo item na tabela do microsserviço
Basket, pois isso quebraria a autonomia entre eles.

Fonte: Referências [3] página 32

Em vez disso, o que se recomenda (linha verde na figura acima) é a adoção


da consistência eventual. Na prática, isso significa que o microsserviço
Catalog deve publicar uma mensagem em um barramento (uma "fila")
informando que "o preço de tal produto mudou", e o microserviço Basket,
que tem interesse neste tipo de alteração, deve monitorar o barramento, e
ao ser informado deste evento, poderá notificar o usuário de que "o preço
do produto que ele já incluiu no carrinho foi alterado" e sugerir um refresh
(atualização).

Isso seria um exemplo de consistência eventual, pois durante um pequeno


intervalo de tempo o preço do produto poderá estar inconsistente nos dois
microsserviços. Como veremos na seção seguinte, a solução para assegurar
a consistência eventual tem forte relação com a forma como os
microsserviços vão se comunicar.
12

O uso de microsserviços traz outras implicações relacionadas com


persistência e consistência de dados. Por exemplo, uma aplicação cliente
pode precisar extrair ao mesmo tempo dados que estão sendo mantidos
em dois ou três microsserviços diferentes, o que pode ser bem mais
complexo do que acessar um único banco de dados relacional no SQL Server
no caso das aplicações monolíticas.

Para lidar com isso, o desenvolvedor deverá estar familiarizado com certas
técnicas de agregação de dados, como o conceito de CQRS (Command and
Query Responsibility Segregation) [7], que envolve simplificadamente
duplicar os dados em diferentes serviços, bem como conhecer técnicas
como o uso de tabelas não normalizadas apenas de leitura (que já contêm
dados de vários microsserviços apenas para servir consultas), ou a
exportação de dados de vários microsserviços para serem agregados em
algum banco unificado (cold data) permitindo a geração de relatórios e
consultas) que não requeiram dados atualizados em tempo real e outros.

Mais do que ter que adotar padrões de consistência eventual, o


desenvolvedor deve ter a percepção de que cada microsserviço, sendo
responsável pela persistência de seus próprios dados, poderá utilizar
diferentes tipos de bancos de dados, sejam relacionais (SQL) ou não
relacionais (NoSQL), como por exemplo o MongoDB ou o CosmosDB. Isso
vai depender do modelo de dados e das necessidades de cada um.

Esta possibilidade de mistura de tipos de storage para armazenar diferentes


tipos de dados em uma mesma aplicação tem o nome de "persistência
poliglota" (polyglot persistence) [8]. Obviamente isso só se justifica se for
bom para o negócio, ou seja, aqui existe não a obrigação, mas a
oportunidade de utilizar diferentes bancos de dados para atender melhor
as funções que os microserviços precisam entregar. Não existe mais a
obrigação de que todos utilizem a mesma tecnologia de armazenamento.

A figura seguinte mostra como referência meia dúzia de microsserviços que


poderiam fazer parte de uma mesma aplicação que utilizam diversos tipos
de bancos de dados diferentes, e em função disso, seguem também
diferentes patterns e utilizam diferentes linguagens de programação.
13

Fonte: Referências [3] página 32

3. Comunicação entre microsserviços

Uma técnica para implementar a consistência eventual de dados é o uso de


comunicação assíncrona baseada em eventos (asynchronous event-driven
communication) entre diferentes microsserviços.

Como se pode observar na figura, um componente importante desta


abordagem é o barramento de mensagens (Event Bus). Ele permite a
comunicação do tipo PUB-SUB (publish/subscribe) entre microsserviços
sem requerer que cada serviço sequer tenha conhecimento da existência
dos demais (assegurando assim o baixo acoplamento).

Fonte: Referências [3] - pg 137.

No exemplo acima, o microsserviço A faz (por exemplo) algum tipo de


atualização em seus dados e publica um evento em uma fila de mensagens,
informando sobre a mudança.
14

Os microsserviços B e C podem estar interessados naquele tipo de evento


(e não em outros), e assim fazem uma "assinatura" dos eventos de seu
interesse. Ao receberem informação sobre o evento publicado por A na fila
(Event Bus), B e C poderão fazer suas próprias atualizações ou realizar
outras operações (por exemplo, enviar um alerta etc.).

O resultado da abordagem de consistência eventual implementada através


da troca de mensagens assíncronas entre serviços poderia ser por exemplo
uma notificação para o usuário na interface da aplicação de que "o preço
do item que ele já incluiu no carrinho de compras foi alterado".

Um botão de UPDATE poderia atualizar os preços antes do CHECKOUT, ou


mesmo desabilitar o CHECKOUT até que a página fosse atualizada com os
preços corretos.

Fonte: Referências [3] - pg 151.

A figura seguinte resume o que já foi discutido até o momento, incluindo as


teses de que:
15

1) Diversos serviços devem cuidar de seus próprios dados, podendo utilizar


tecnologias de storage diferentes (SQL Server, MongoDB, Redis cache etc.),
estando assim desacoplados em relação aos dados,

2) Microsserviços devem se comunicar de forma assíncrona (quando


possível) através de um barramento de mensagens, ajudando a manter um
nível baixo de acoplamento entre eles.

Fonte: Referências [3] - pg 97

Note que a aplicação pode ter vários clientes diferentes (web app, mobile)
como front end e utiliza diferentes microsserviços como back end. Nesta
abordagem, cada microsserviço expõe um endpoint público (uma URL a
partir do qual pode ser acessado).

API Gateway

Uma melhoria que se poderia fazer neste modelo para evitar o acesso
direto das aplicações nos microserviços é a utilização de um padrão
denominado API Gateway (figura). O Gateway atua como um agregador, e
oferece um ponto único de acesso das aplicações clientes para todos os
microsserviços existentes.
16

Este pattern é exemplificado na figura seguinte.

Fonte: Referências [3] - pg 40

Ainda que o desenvolvedor defina de forma adequada o escopo (fronteira)


de seus microsserviços, crie bons modelos de dados, escolha de forma
sensata a tecnologia de storage para os dados que seus microsserviços
precisam manter e estabeleça o padrão de consistência eventual utilizando
comunicação assíncrona, há ainda outros desafios para superar. Um deles
é o da resiliência - como tornar os microsserviços mais tolerantes para
falhas?

Este é o tema da próxima seção.

4. Resiliência

Uma outra dificuldade que pode afastar os desenvolvedores menos


experientes é a necessidade de criar microsserviços resilientes a falhas,
tanto de software quanto de indisponibilidade de hardware. Os
microsserviços devem ser capazes de se recuperar em alguma outra
máquina virtual, por exemplo, em caso de falhas. Resiliência aqui não
significa produzir uma aplicação que não falhe, e sim que seja capaz de se
recuperar de falhas, e tenha um comportamento minimamente gracioso ou
funcional quando surgirem os inevitáveis problemas de latência.
17

Ao se reinicializar, o microsserviço deve preservar a consistência de seu


estado anterior. Durante as atualizações de versão (upgrades) também
podem ocorrer falhas, e os microsserviços (e seus orquestradores) devem
ser capazes de fazer roll back automático para uma versão anterior para
preservar consistência se for necessário.

Uma boa prática para microsserviços (que pode ser perigosa se


transportada para outros contextos) é FAIL FAST. Se a falha for inevitável, é
melhor que ocorra logo, pois a demora em tirar do ar um serviço
irresponsivo poderá levar a um número maior de tentativas de acesso pelos
usuários que não serão atendidas, piorando o problema.

Existem padrões que o desenvolvedor pode adotar estratégias de Fallback


(ter um “plano B” para entregar alguma coisa em vez de nada em caso de
falha), ou implementar padrões de resiliência como Bulkhead [9] e Circuit
Breaker [10]. Neste contexto, é importante que cada microsserviço seja
capaz de reportar sobre sua própria saúde (health monitoring) para algum
monitor, periodicamente ou quando solicitado.

Além disso, se a geração de logs (formatos, padrões) e diagnósticos de


falhas criados por diferentes desenvolvedores não for padronizada desde o
início, a correlação destas informações por parte de quem terá que dar
suporte pode ser bastante difícil.
18

5. Contratos

Além do que já foi comentado, há vários outros desafios na arquitetura de


microsserviços. O desenvolvedor deverá utilizar o conceito de API-first
development, e a API de um microsserviço precisa ser entendida como um
contrato entre aquele serviço e seus clientes, que especifica que funções o
microserviço executa e como tais funções devem ser acessadas. Assim, é
preciso seguir regras ao enviar uma requisição para a API de um
microserviço, respeitar o contrato, ou ele não terá obrigação de atender e
devolverá um erro. Naturalmente, todo serviço precisa evoluir ao longo do
tempo, de modo que se ele já disponibilizou uma API que já está sendo
consumida por aplicações clientes, é preciso evoluir o serviço e sua API de
forma que cada novo release seja compatível com versões anteriores, sem
"quebrar o contrato". Porém, pode ocorrer a necessidade de grandes
refatorações que de fato tornem o novo serviço (e sua nova API)
incompatíveis com a versão anterior, e neste caso, ou bem se convence os
clientes a migrarem suas aplicações (o que pode ser difícil), ou bem se adota
alguma técnica de versionamento para que o serviço continue suportando
a API na versão antiga - por algum tempo - e passe a suportar também a
versão mais atual. Enfim, há soluções para isso, mas considerando que cada
microsserviço precisa ter sua própria API isso a questão do versionamento
é importante.

Há também a questão do endereçamento e do discovery de quais serviços


existem, dado que novos microsserviços podem ser incorporados por
outros times. Os serviços precisam ser criados de modo que sejam
facilmente endereçáveis, o que traz a necessidade de um padrão para
resolução de URLs de microsserviços (que podem estar sendo executados
em alguma máquina virtual que ninguém deve precisar saber qual é). O
padrão de nomes (naming service), o registro de serviços (service registry)
e o próprio discovery de serviços existentes são requisitos importantes para
desenvolvimento em microsserviços.

Como já deve ter ficado claro neste ponto, a necessidade de uso de padrões
de consistência eventual (que são mais complexos de se implementar do
que os tradicionais JOINS da linguagem SQL) e da comunicação assíncrona
entre microsserviços (mais sofisticada que a comunicação utilizada em
aplicações monolíticas através de function calls, por exemplo) introduz de
fato um razoável "custo de complexidade" na escolha da arquitetura de
microsserviços.
19

A princípio, os benefícios que a arquitetura distribuída em microsserviços


trazem são mais perceptíveis em aplicações de missão crítica e/ou que
demandam grande escalabilidade (como o NETFLIX, Uber ou Amazon, por
exemplo). Porém, qualquer empresa que pretenda desenvolver aplicações
para a nuvem deve ao menos considerar esta arquitetura, avaliando os
benefícios e custos para seu cenário particular.

Cuidado com o hype!

Antes de optar pela arquitetura de microsserviços, é importante avaliar


bem as vantagens e desvantagens desta opção. A questão básica é: qual
problema você deseja resolver adotando microsserviços?

Microsserviço é um meio, e não um fim em si próprio.

É preciso focar no resultado de negócio desejado, e não na tecnologia.


Dizendo de outro modo, é preciso ter um bom motivo para adotar
microsserviços, e dada a complexidade que isso traz (tecnicamente, em
operações e mudanças culturais) juntamente com os inegáveis benefícios,
esta arquitetura não deve ser a opção default para fazer qualquer software
para qualquer coisa. Sua adoção precisa ser justificada. Como saber se o
uso se justifica ou não? Bem, é preciso avaliar os benefícios potenciais dos
microsserviços e ver se eles são importantes para o seu negócio.

Como um teste, convém se fazer algumas perguntas, e se as respostas forem


'não' provavelmente adotar microsserviços não será uma boa ideia.
20

Pergunta 1. Preciso realmente ter deploy independente e mais rápido de


pequenas funcionalidades, em oposição a liberar tudo como um monolito
mais lentamente? Existe necessidade de fazer CI/CD para cada microserviço
(cada um ter seu próprio pipeline?
Pergunta 2. Preciso realmente de zero downtime ao fazer atualizações dos
meus serviços para os clientes? Meu negócio é crítico a este ponto?
Pergunta 3. Preciso realmente de ter escalabilidade horizontal, e
elasticidade sob demanda, rodando minha aplicação em pequenos pedaços
espalhados em máquinas virtuais? Pois se não há variação importante de
workload, você talvez não precise de "escalabilidade horizontal", e talvez a
complexidade trazida pela computação distribuída não se justifique.
Pergunta 4. Posso viver com dados eventualmente consistentes? A natureza
de minha operação permite isso?
Pergunta 5. Tenho cultura de DevOps e desenvolvedores tecnicamente
competentes, com a "altura necessária para desenvolver microsserviços"?
Pergunta 6. O domínio está maduro? Ao adotar microserviços que entregam
funções através de APIs, a complexidade em parte vai migrar para o
contratos destas APIs. Mudou o negócio, muda a função, isso requer que
mude o contrato, e se este processo não for bem implementado a mudança
de contrato poderá "quebrar" aplicações que já estão em produção!
Portanto, é preciso evitar desenvolver microsserviços em domínio de
negócio ainda não maduros. Em outras palavras, microsserviços precisam
que o problema que se quer resolver esteja bem definido para que ele possa
ser decomposto da maneira correta. Se o domínio do negócio ainda está em
definição, os microsserviços não são adequados, é melhor criar monolitos.

Também é preciso levar em conta o investimento necessário. Certamente


microsserviços têm o potencial (e às vezes são o único meio!) de fazer a
organização ganhar muito dinheiro (que o digam a Netflix e a Amazon), mas
a adoção desta arquitetura não é barata e dificilmente vai reduzir custos.
Assim, não se deve adotar microsserviços achando que isso trará
economias.

Finalmente, ainda que tecnicamente todas as condições estejam presentes,


para que a adoção de microserviços dê certo a organização talvez tenha que
mudar sua cultura, sua forma de organizar pessoas, a autonomia dos times
para tomar decisões. É a Lei de Conway - organizações monolíticas vão
produzir sistemas monolíticos.
21

Diante disso, cabe perguntar aos donos - vocês querem mudar?

Isso também se aplica aos próprios desenvolvedores. A turma antiga, a


velha guarda, do tempo da arquitetura em camadas que produz monólitos,
vai se engajar nesta nova forma de programar? Se os próprios
desenvolvedores não se entenderem será difícil adotar microsserviços com
sucesso.

O ambiente molda o comportamento. Em um ambiente com nove janelas


quebradas em dez, alguém provavelmente vai quebrar a última. Por outro
lado, se não tiver nenhuma janela quebrada é menor o risco de alguém
quebrar a primeira. A cultura organizacional afeta em vários aspectos o
produto gerado. Se a empresa não tem preocupação com qualidade, será
difícil produzir código com qualidade lá. Se a empresa não costuma dar
autonomia para ninguém, como esperar que o time de microserviços vá ter
autonomia para cuidar de "parte" de um produto? etc.

Conclusão

Sam Newman nos alerta em [11] que "quanto menos você compreender o
domínio, mais difícil será definir as fronteiras" para seus microsserviços, e
um erro aqui poderá ter custos elevados no futuro. Assim, uma boa
estratégia é começar com aplicações monolíticas, estabilizar seu
entendimento, e depois verificar quais partes da aplicação podem e devem
ser isoladas como microsserviços independentes. A mudança para os
microsserviços pode ser gradual, não precisa ser uma ruptura.
22

Microservices are a journey, not a destination. Go incrementally.


Break your legacy monolithic system piece by piece, learning as you
go. And get used to it: in many ways, the discipline to continually
change and evolve our systems is a far more important lesson to learn
than any other. Change is inevitable. Embrace it.

A nuvem é o futuro, e desenvolvimento de aplicações para a nuvem requer


que a arquitetura de microsserviços seja ao menos considerada. Em alguns
casos esta arquitetura poderá ser descartada em favor de outras opções.
Em outros seu uso será recomendável talvez em uma parte específica da
aplicação, e em alguns outros casos poderá ser necessária, como única
opção para atender requisitos de escalabilidade e elasticidade de demanda
por exemplo.

O importante é ter bastante senso crítico na adoção da arquitetura, para


evitar caindo na armadilha de produzir "monolitos distribuídos", o que é o
pior dos dois mundos - a inflexibilidade das aplicações monolíticas aliada à
complexidade das muitas partes móveis.

Por este motivo, é importante que os desenvolvedores e arquitetos de


software se preparem para atingir "a altura mínima para desenvolver
aplicações com microsserviços" sugerida por Fowler [4], para que os
benefícios desta abordagem possam ser colhidos e os desafios que
certamente surgirão sejam superados.

Da mesma forma, esperamos que as informações aqui reunidas permitam


aos consultores interessados alcançar "a altura mínima para conversar
sobre microsserviços" com seus clientes ou suas equipes quando surgir esta
necessidade.
23

Referências
1 https://pt.wikipedia.org/wiki/Service-oriented_architecture

2 https://docs.microsoft.com/en-
us/azure/architecture/guide/architecture-styles/n-tier

3 _NET-Microservices-Architecture-for-Containerized-NET-Applications-
(Microsoft-eBook). Cesar de la Torre, Bill Wagner, Mike Rousos - Microsoft
Corporation. DOWNLOAD available at:
https://aka.ms/microservicesebook2 _COPI, I. M. 1981: Introdução à
Lógica. Editora Mestre Jou.

4 _Martin Fowler - Microservices.

http://www.martinfowler.com/articles/microservices.html

5 _Martin Fowler - Microservices Prerequisites

http://martinfowler.com/bliki/MicroservicePrerequisites.html

6 _Eventual consistency -
https://en.wikipedia.org/wiki/Eventual_consistency

7 _Martin Fowler - CQRS (Command and Query Responsibility


Segregation)

http://martinfowler.com/bliki/CQRS.html

8 _Martin Fowler - Polyglot Persistence

http://martinfowler.com/bliki/PolyglotPersistence.html

9 _https://docs.microsoft.com/en-
us/azure/architecture/patterns/bulkhead

10 _https://docs.microsoft.com/en-
us/azure/architecture/patterns/circuit-breaker

11 _Building Microservices: Designing Fine-Grained Systems. Sam


Newman - O'Reilly.

Você também pode gostar