O documento discute princípios arquiteturais e boas práticas para o desenvolvimento de microsserviços, incluindo Domain-Driven Design, 12 Factor Apps, testes, resiliência e automação.
3. O conteúdo foi dividido em fundamentos sobre Microsserviços, e o conjunto de lições
aprendidas no aspecto arquitetural, desenvolvimento, testes e sustentação.
4. A arquitetura de “Microsserviços” não tem um fundador oficial. Convencionou-se como
uma especialização de arquitetura distribuída, em que serviços independentes se
comunicam via interfaces agnósticas à tecnologia. Por “independente” entenda: ciclo de
vida individual e resiliente, em que um serviço pode ser deployado ou desligado sem
impacto na solução.
O termo “micro” é controverso. Não existe uma definição acadêmica do quão pequeno é
um microsserviço, quantas classes ou linhas de código. Faz mais sentido pensar que é
um serviço que atende a um único propósito.
A proposta é prover gerenciamento descentralizado ao contexto de negócio.
Determinado contexto pode demandar necessidades específicas, tanto no
desenvolvimento (linguagem, paradigma) quanto na infra (bare metal vs virtualização,
número de instâncias). Quando o modelo está maduro, permite entregas velozes,
ambiente resiliente e elasticidade.
5. A falha na implantação dos princípios básicos pode transformar o que seria uma solução
de microsserviços em um monolito de composto em serviços altamente acoplados - na
prática, o que antes era um ponto de falha centralizado e um serviço para versionar,
deployar e sustentar, se transforma em dezenas ou centenas.
6. O cenário mencionado aqui não é tão incomum. Normalmente a arquitetura de
microsserviços é aplicada a soluções complexas, com muitos times desenvolvendo
simultaneamente, envolvendo integração com sistemas legados. Muitas vezes não fica
claro o esforço operacional que irá surgir num cenário de migração – um time de 5
pessoas é capaz de sustentar um monólito complexo, por se tratar de uma única
aplicação. Esse mesmo time poderá sustentar centenas de serviços com ciclo de vida
próprio?
7. Dentro deste contexto que não é tão incomum, elenquei algumas das lições aprendidas
mais importantes no aspecto DevOps de uma solução de Microsserviços. Não existe
exatamente o certo e errado, porém foram conclusões que o nosso time entendeu que
eram mais apropriadas.
8. Domain Driven Design pode ser considerado um mindset de desenvolvimento de
software em que o entendimento do domínio do negócio permite a estruturação do
software de maneira a refletir o domínio, mantendo um único padrão de comunicação.
Mais informações: https://www.lambda3.com.br/2017/10/desmistificando-o-ddd/
9. A figura acima mostra um mapa de contexto de um cenário em que um domínio de
negógios foi dividido em diversos contextos. Os contextos principais contém as
funcionalidades relacionadas ao core do negócio, e os genéricos, as funções que
poderiam ser adquiridas no mercado por exemplo.
O entendimento do domínio e a delimitação dos contextos podem servir como base de
organização de microsserviços.
Mais informações em:
https://www.lambda3.com.br/2017/11/ddd-aplicado-case-fluxo-de-producao-de-
equipamentos-por-demanda/
10. Segundo a Conway’s Law -“Any organization that designs a system (defined broadly)
will produce a design whose structure is a copy of the organization's communication
structure”, isto é, a implementação da solução reflete a estrutura empresarial, o que
muitas vezes não é o mais adequado para tirar proveito da flexibilidade que a arquitetura
de microsserviços oferece. Se isso acontecer, pode-se imaginar que a organização dos
microsserviços irá refletir a organização dos times, por exemplo, front-end, regras de
negócios, infraestrutura, etc.
11. Ao invés disso, os princípios do DDD podem ser considerados para aproximar a
implementação da natureza do negócio. Um contexto pode demandar uma tecnologia
que o atenda melhor, como uma linguagem de programação, modelo de banco de dados
(no-sql / relacional) ou de infraestrutura (baremetal / virtualização). Com limites bem
delimitados, a melhor forma de conectá-los e gerenciar sua comunicação pode ser
identificada e implementada.
12. Outro ponto também vinculado a princípios do DDD é que o entendimento do domínio é
crescente. Combinando isso ao fato de que é mais difícil “juntar” módulos do que separá-
los, é preferível que o serviço cresça até o ponto que esteja maduro o suficiente para ser
dividido, se necessário, ainda que cause uma contradição ao considerar o termo
“microsserviço”.
13. Independente da tecnologia, da clareza do código ou do tamanho do serviço, é
importante que alguns princípios sejam respeitados para que ele seja portável, elástico,
resiliente e sustentável.
12 Factor Apps é uma metodologia de construção de serviços, criada pelo time da
plataforma Heroku, com base em lições aprendidas na sustentação de sua plataforma.
Aqui os fatores foram agrupados em quatro contextos: os que afetam diretamente o
desenvolvimento, entrega e validação, os que afetam a portabilidade, a elasticidade e a
resiliência do serviço.
Mais informações no https://12factor.net/
14. Ainda relacionado a 12 Factor Apps, principalmente nos fatores “Build, Release, Run”,
“Dependencies” e “Backing Services”: Considere um conjunto de funções pertencentes a
determinado contexto do negócio que é replicado em diversos serviços.
Pensando em código limpo e facilidade de manutenção, essas funções poderiam ser
agrupadas e disponibilizadas via biblioteca (Nuget, por exemplo). Porém caso alguma
melhoria ou correção seja implementada na biblioteca, todos os serviços que a
consomem teriam que ser atualizados e redeployados para refletir as alterações. Isso
pode não ser urgente em alguns cenários, mas em outros, como correção de
vulnerabilidades, os serviços precisam ter acesso à correção o quanto antes.
Tratar dependências como serviços é diferente. As atualizações que não impliquem
quebra de contrato, como bugfixes e securityfixes são disponibilizadas em tempo real.
É difícil pensar em um serviço que possa ser desenvolvido sem a utilização de alguma
biblioteca corporativa, mas para evitar overhead de gerenciamento, é importante
questionar:
● Existe alguma biblioteca validada de mercado que desempenha essa função?
● Se não, faz sentido criar um serviço que exponha essas funcionalidades?
● Se a resposta for não, o time irá decidir entre repetir o código entre os serviços
consumidores (o que em um cenário de microsserviços muitas vezes é aceitável)
ou criar a biblioteca, estando ciente do impacto de gerenciamento.
15. Quando a solução envolve centenas de serviços, é praticamente impossível realizar
testes end-to-end em um ambiente local. Porém é possível ter confiança nas entregas se
os contratos de comunicação estabelecidos estiverem bem validados.
A utilização de mocks viabiliza a automação dos testes de integração.
Os testes de aceitação podem consumir bases de dados descartáveis para que possam
ser executados a qualquer momento sem prejudicar a massa de dados de testes
funcionais em andamento.
O desenvolvimento de serviços deve visar a compatibilidade - se houver quebra de
contrato, a versão nova deve coexistir com a antiga até que todos os serviços que a
consomem estejam utilizando a versão nova. Com isso estratégias de deploy Blue /
Green podem ser implementadas - a versão nova entra em produção, é validada, só
então o tráfego é redirecionado gradualmente da versão anterior para a nova, até que a
versão anterior possa ser desativada.
16. Depois de desenvolver centenas de serviços, como funciona a manutenção?
O maior aliado de um time de operações sustentando centenas de serviços em produção
é o processo de automação de atividades que costumam ser efetuadas manualmente.
Os procedimentos viram código, que podem ser centralizados, versionados, publicados e
executados via plataforma.
Isso diminui a possibilidade de falha humana durante uma execução, e mantém um
repositório de utilitários centralizado que pode ser acessível a todo o time.
Quando as tarefas são automatizadas, é viável um time pequeno sustentar uma solução
de microsserviços.
Para isso é importante criar a cultura de, ao invés de realizar uma tarefa manualmente,
criar um script que a faça, generalizar / parametrizá-lo para que possa ser reutilizado, e
disponibilizá-lo em um repositório comum, com execução agendada ou não.
17. Durante o desenvolvimento de aplicações monolíticas, a preocupação com a latência de
operações é baixa. É esperado que a performance da aplicação de uma forma geral seja
ainda melhor em produção, por ser um ambiente mais robusto por natureza.
Porém com microsserviços, o processamento de uma única transação pode passar por
dezenas de serviços até que seja concluída. Quando os times não estão levam isso em
consideração durante o desenvolvimento, falhas de performance e disponibilidade só
serão identificadas em produção.
Algumas das asserções mais ingênuas que programadores que não estão acostumados
com microsserviços pensam foram compiladas no "Fallacies of distributed computing"
https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing
18. A Netflix tem uma abordagem interessante para zelar pela resiliência da solução sem
interferir na liberdade de desenvolvimento dos times - “The Freedom and Responsibility
culture at Netflix doesn’t have a mechanism to force engineers to architect their code in
any specific way.”
Para que todos os times desenvolvessem serviços visando a resiliência, foi instituído
primeiramente o Chaos Monkey, procedimento que desativava clusters aleatoriamente,
depois o Chaos Kong, que desativava datacenters, para validar a geo-redundância da
solução.
Outras iniciativas implantadas foram o FIT (Failure Injection Testing) e o ChAP (Chaos
Automation Platform) para injetar falhas diretamente em serviços.
Todas essas ações resumem uma espécie de “Chaos-driven development” - falhas
acontecem, e a solução e o time precisam estar preparados para isso.
19. Uma abordagem ganhando mais e mais espaço no mercado é a containerização, em
particular com Docker.
No ponto de vista de desenvolvimento, ele promove o cumprimento dos 12 fatores.
No ponto de vista de operações, ele permite que qualquer serviço, independente da
tecnologia em que foi desenvolvido, seja deployado e gerenciado de maneira
padronizada.
Considere uma plataforma Kubernetes com centenas de serviços desenvolvidos com a
tecnologia mais adequada ao contexto. C#, Node, Java… ao entrarem na plataforma,
são containers.
Sustentar centenas de serviços é mais fácil se o time que mantém a plataforma pode se
especializar em uma única tecnologia e tirar o proveito dela ao máximo com
automações.
O Kubernetes já provê recursos visando elasticidade e resiliência por default, tornado
mais simples o gerenciamento.
Replicar um cluster completo é um procedimento de um conjunto pequeno de
comandos.