Descubra a verdade sobre microsserviços que nunca te contaram

19 de outubro de 2024 Por Ramon Durães

Você realmente entende o que está por trás dos microsserviços que impulsionam a inovação digital das maiores empresas do mundo? Em um mercado em rápida transformação, as empresas estão lutando para não apenas acompanhar, mas liderar a corrida rumo à transformação digital. A chave para esse sucesso? Plataformas digitais que oferecem experiências excepcionais, não apenas para clientes, mas também para colaboradores e parceiros. Neste artigo, vamos desmistificar os microsserviços e revelar verdades que poucos se atrevem a contar.

Nos últimos anos, aprendemos com os desafios enfrentados, consolidando boas práticas de desenvolvimento de software e adotando novas abordagens, como a arquitetura de microsserviços. Embora essa arquitetura esteja presente no mercado há mais de uma década, os conceitos de sistemas distribuídos que ela integra vêm sendo discutidos há mais de 20 anos.

Simultaneamente, surgiram soluções modernas, como a infraestrutura cloud native, baseada em tecnologias como Docker e Kubernetes, viabilizadas por plataformas como Azure, AWS e Google Cloud. Essas plataformas são apoiadas por práticas como DevOps, DevSecOps e SRE, que garantem o suporte eficiente à operação dessas plataformas digitais.

A nova infraestrutura evoluiu, passando da virtualização de máquinas virtuais para a contenização de aplicações em ambientes serverless, escalados por demanda. Isso proporciona uma experiência ponta a ponta de sistemas distribuídos e será um dos pilares importantes para você entender que aplicações monolitas não foram projetadas para esse novo ecosistema.

A reflexão que faço neste momento é: você continuará tentando executar o mesmo workload de software com práticas legadas em uma infraestrutura moderna, esperando resultados diferentes?

O modelo de desenvolvimento de software tradicional, ou arquitetura monolítica, enfrenta dificuldades significativas na manutenção devido à complexidade da base de código e à contaminação comum nas responsabilidades de negócio, quanto na persistência de dados. É normal uma pequena demanda de alteração gerar maioria das vezes grandes mudanças, impactando vários contextos de negócio e dificultando a evolução da aplicação.

Outro problema conhecido na aplicação monolítica é a falta de flexibilidade tecnológica pela dependencia de um único stack, impedindo a adoção de novas tecnologias sem a necessidade de refazer a aplicação inteira. Isso resulta em cenários de aplicações legadas que não conseguem evoluir e ficam presas a tecnologias ineficientes com dificuldade de manutenção, sem inovação no negocio e muitas com risco de segurança.

Além disso, a publicação única de todo o software aumenta a complexidade das atualizações. Mesmo pequenas modificações exigem garantir que o software completo esteja funcionando corretamente. Em produção, isso demanda uma capacidade maior de execução, pois a demanda dos clientes exige a escalabilidade de todo o sistema, e não apenas do serviço específico que está sob maior demanda. Eu costumo apoiar projetos complexos no passado e durante uma consultoria passamos por esse problema em um grande ecommerce para escalar o calculo de frete era necessário criar várias replicas do mesmo software que na epoca duplicava de forma esponecial os servidores e o custo.

A complexidade da base de código em uma arquitetura monolítica também afeta o banco de dados afetando a performance, manutenção e produtividade dos desenvolvedores que precisam conviver com esse problema. Modelos de negócios distintos se misturam, dificultando a separação dos contextos e campos compartilhados na mesma tabela de dados sendo que são visoes de negócio distintas ou tabelas que recebem campos / propriedades sem relação nenhuma com o objetivo principal. Um exemplo clássico é uma atualização no financeiro impactando a vitrine de produtos em um e-commerce.

Essa mistura prejudica a eficiência e a independência das equipes de desenvolvimento e a divisão de atividades se torna mais difícil além do risco de impacto em cada mudança que é alto. Com uma única base de código, aumenta-se a necessidade de comunicação e coordenação entre as equipes. Cada modificação, mesmo as mais simples, exige a compilação de tudo e um teste do projeto inteiro, retardando a inovação, pois naturalmente você vai depender de outras implementações e/ou homologações.

Além disso, processos concorrentes geram alto consumo de recursos no banco de dados, resultando em processamento ineficiente e lentidão. Longos contextos transacionais são executados simultaneamente, pois envolvem várias tabelas.

A arquitetura de microsserviços tornou-se popular nas conferências técnicas desde 2010, promovida por Martin Fowler e outros especialistas em desenvolvimento de software. Ela vem se consolidando no mercado há mais de 10 anos como uma proposta de valor que promove agilidade e inovação contínua.

Essa abordagem utiliza práticas arquiteturais para o desacoplamento e o isolamento das responsabilidades por meio de contextos de negócio, incorporando jornadas como Event Storming, Domain-Driven Design (DDD) e Event Modeling, além de práticas de arquitetura para comunicação assíncrona, como Event-Driven Architecture.

É muito comum nas jornadas de consultoria e imersão em microsserviços os clientes perguntarem sobre tamanho do microsserviço. Uma resposta simples para essa questão é demonstrar que o desenvolvedor não deve se preocupar com a quantidade de linhas, mas sim com o contexto de negócio e a responsabilidade de um determinado dominio e a importancia de executar uma jornada estruturada de Domain-Driven Design (DDD) construindo um context-map e mapeando os bouded-context e depois Aggregate Root, Entity, Value Objects relacionados.

Um enorme paradigma na transição para microsserviço é quem esta acostumado a modelar o software começando pela criação do banco de dados, como fazíamos há mais de 25 anos. É importante entender que, com base no modelo de negócio, teremos uma persistência de dados referente a esse domínio, que será exclusivamente da responsabilidade do contexto desse microsserviço envitando qualquer colisão de responsabilidade e respeitando o desacoplamento e com esse principio você entenderá de uma vez o motivo de cada microsserviço ter o seu banco de dados visto que na visão de negócio apenas aquele serviço é responsavel por interagir naquela persistência.

Essa abordagem é fundamental para reduzir a complexidade, simplificar a manutenibilidade, independência, facilitar escalabilidade do microsserviço e de seu banco de dados relacionado. Diferente do modelo tradicional, onde a própria persistência se mistura entre os mais diferentes contextos de negócio, comprometendo a agilidade e a evolução do negócio.

Ao adotar o isolamento de responsabilidade das regras de negócio e do banco de dados, é fundamental entender que processos que aconteciam em um único contexto agora ocorrem de forma assíncrona em vários microsserviços com os contextos de negócios e persitências relacionadas. Isso permite que equipes realizem manutenções com independência e segurança, algo bem diferente do modelo tradicional, onde squads se conflitam no mesmo codebase e em um único banco de dados.

Uma dúvida recorrente refere-se aos cenários que utilizam transações de banco dados e gostaria de compartilhar que e uma abordagem recomendada é o padrão chamado “Saga Pattern” para controlar a transação distribuída entre vários microsserviços onde cada um tem o seu banco de dados mesmo que serviço um tenha uma persistência com tecnologia diferente. Image um banco de dados de um Microserviço “A” com NOSQL no MongoDB e do Microserviço “B” com relacional o Microsoft SQLServer. A persistência poliglota é um tema simplificado no contexto de microsserviços.

O padrão Saga utiliza o conceito de coreografia, onde cada microsserviço emite eventos refletindo uma ação executada, e outros reagem, propagando em seu contexto. O outro modelo chama-se orquestrado, que utiliza um “coordenador” da transação distribuída, intermediando o processo previsibilidade dos estagios e lembra o modelo tradicional. Nós implementamos um distributed Transaction Services para permitir aos nossos clientes acompanhem as transações e relalizar interações e até cancelamentos com o que chamamos de compensação da transação retornando ao estado anterior.

A interação entre os microsserviços deve ser realizada preferencialmente de forma assíncrona usando Event-Driven, por meio de um barramento como RabbitMQ, Kafka e outras soluções. Isso permite que cada contexto propague os fatos de negócio de sua responsabilidade e os outros microsserviços reajam a eventos de seu interesse de forma independente. Para cenários onde fizer sentido, pode-se utilizar comunicação traddicional via HTTP ou gRPC entre os microsserviços que dependem da resposta do outro microsserviço. A padronização e governança são garantidas por meio de contratos de API, versionamento de APIs e contratos de eventos com suporte a versionamento.

Ao utilizar uma abordagem de Event-Driven Architecture (EDA), estamos adicionando um componente valioso à estratégia: o reforço ao desacoplamento entre serviços. Isso permite a independência, uma arquitetura assíncrona (non-blocking), o aumento da disponibilidade e escalabilidade (SLA), além de uma modelagem orientada a eventos. Fatos de negócio podem ser capturados para análises em tempo real, utilizando data analytics e inteligência artificial na tomada de decisões.

A estratégia de Event-Driven Architecture (EDA) é totalmente diferente de recursos como Change Data Capture (CDC), que compartilham changelog de banco de dados e são utilizados atualmente em integrações de aplicações legadas. Esses geram alto tráfego e poluem o barramento de serviços, além de sobrecarregar outros microsserviços.

Em qualquer cenário de aplicação tradicional ou distribuída utilizando microsserviços, é fundamental adotar boas práticas de arquitetura de software para resiliência, com retentativas em caso de indisponibilidade de algum serviço, tratativas com fallbacks e uma abordagem de circuit breaker. Essa abordagem evita que processos fiquem em loop principalmente em cenários de grande volume de acessos e permite que a infraestrutura possa se recuperar e restabelecer os serviços.

Em aplicações tradicionais ou microsserviços é fundamental a adoção de uma estratégia para controlar a exposição pública das APIs, que no caso de microsserviços teremos vários endpoinds e podem ser unificados no API Gateway além de estratégias de segurança, cache, transformação de rotas e throttling que são cruciais para controlar os recursos expostos publicamente na internet plataformas de API Gateway (como Kong e NGINX, outras) de mercado resolvem muito bem esse papel.

A segurança é uma preocupação importante em qualquer projeto de software, e não é diferente na estratégia de microsserviços. Você pode utilizar OAuth2, JWT para as API internas e publicas e/ou combinar com o API Gateway para proteger as APIs, além de empregar criptografia nas comunicações e utilizar um Web Application Firewall nos serviços públicos, implementar scanners Cloud, Cluster, Imagens, Container, componentes, código e adotar estratégias nativas de segurança nos serviços de cloud. No entanto, considerando que os itens anteriores são esperados no dia a dia, é crucial, na modelagem do domínio de negócio, introduzir conversas sobre segurança da informação e aplicar restrições nas regras de negócio visto que muitas das falhas atuais são usuários autenciados que violaram permissões de negócio não estabelecidas.

Adotar uma estratégia de DevOps é um item fundamental em qualquer projeto de software e em minha experiencia obrigatório na abordagem de microsserviços, principalmente com a separação dos repositórios dedicados a cada microsserviço e pipelines de publicação independentes. Dessa forma, as equipes terão um fluxo de CI/CD para cada serviço, podendo executar o pipeline conforme a demanda e mantendo um fluxo seguro para a promoção de versões. Isso inclui estratégias de feature flags para funcionalidades parciais e service mesh para executar mais de uma versão ao mesmo tempo, direcionando o tráfego para testes parciais de uma determinada versão.

Outro grande obstáculo em uma abordagem moderna de microsserviços é a implementação de uma estratégia robusta de observabilidade, em apoio à disciplina de Site Reliability Engineering (SRE). É importante garantir que a infraestrutura esteja preparada para registrar logs adequados ao contexto de sistemas distribuidos, criando um mecanismo de monitoramento contínuo, além de implementar métricas de monitoramento em dashboards dedicados. Assim, é possível garantir que as aplicações e serviços estejam operando de acordo com as expectativas.

Para publicar os microsserviços em produção a minha recomendação pessoal é que utilizar o ambiente de cluster Kubernetes gerenciado pelos cloud providers como Azure Kubernetes Service (AKS), Amazon Elastic Kubernetes Service (EKS), Google Kubernetes Engine (GKE) que já funcionam como plataforma base elimiando grande parte de gestão e esclabilidade do Kubernetes e também podem experimentar os serviços serverless como o: Azure Container Apps, AWS App Runner, Google Cloud Run que já entregam uma instancia de um “POD” kubernetes totalmente serverless.

Até este momento, discutimos os problemas comuns das aplicações tradicionais monolíticas e os recursos modernos dos microsserviços. No entanto, a pergunta de um milhão de dólares que mais recebo nos últimos anos durante nossas consultorias em projetos complexos de software em empresas planejando implementar um projeto digital, SaaS e até um novo ERP é como podemos modernizar o legado?

O principal ponto de decisão sempre gira em torno da prioridade do negócio. Com essa informação em mãos, podemos realizar jornadas de Event-Storm e Domain-Driven Design (DDD) para identificar fluxos de negócio e depois utilizando estratégias e arquitetura de software desenvolver e aplicar abordagens de estrangulamento do legado (Strangler Pattern). Além disso, é possível implementar camadas de convivência e integração, como o padrão Anti-Corruption Layer (ACL), para permitir uma modernização incremental, levando em consideração o cenário de projetos complexos de software.

Ao decompor sistemas monolíticos altamente complexos em componentes independentes, pessoas e empresas podem alcançar maior agilidade no desenvolvimento, flexibilidade para padronizar processos, simplificação da manutenção, redução de custos operacionais e melhoria da eficiência, independentemente do tamanho da organização.

Os microsserviços estão alinhados aos principais padrões das arquiteturas modernas de cloud. A adoção de práticas como Domain-Driven Design, service mesh para controle de tráfego e pipelines automatizados de CI/CD, além de uma cultura DevOps sólida, são fatores críticos para o sucesso na implementação de aplicações modernas e microsserviços.

Faz sentido? Compartilhe e colabore com comentários. Precisa de ajuda especializada em estratégia de software para apoiar a modernização do seu software? Entre em contato.

Até a próxima !!!

Ramon Durães

VP Engineering