10 padrões de design de microsserviços para uma arquitetura melhor

A arquitetura monolítica foi historicamente utilizada por desenvolvedores por muito tempo - e por muito tempo funcionou. Infelizmente, essas arquiteturas utilizam menos partes que são maiores, o que significa que elas têm mais chances de falhar por completo se uma única parte falhar. Muitas vezes, essas aplicações eram executadas como um único processo, o que só agravava o problema.

Os microsserviços resolvem esses problemas específicos ao executar cada microsserviço como um processo separado. Se uma engrenagem falhar, não significa necessariamente que a máquina inteira pare de funcionar. Além disso, diagnosticar e corrigir defeitos em serviços menores e altamente coesos é geralmente mais fácil do que em monólitos maiores.

Padrões de design de microsserviços fornecem blocos de construção fundamentais testados e comprovados que podem ajudar a escrever código para microsserviços. Ao utilizar padrões durante o processo de desenvolvimento, você economiza tempo e garante um nível mais alto de precisão em comparação com escrever código para o seu aplicativo de microsserviços do zero. Neste artigo, abordamos uma visão geral abrangente de 10 padrões de design de microsserviços que você precisa conhecer, bem como quando aplicá-los.

Principais benefícios do uso de padrões de design de microsserviços

Conhecer os principais benefícios dos microsserviços ajudará você a entender os padrões de design. Os benefícios exatos podem variar com base nos microsserviços utilizados e nas aplicações em que estão sendo utilizados. No entanto, desenvolvedores e engenheiros de software geralmente podem esperar as seguintes vantagens ao utilizar padrões de design de microsserviços:

  • Criação de uma arquitetura de aplicativo que é independentemente implantável e descentralizada;
  • Escalabilidade massiva quando necessário;
  • Novas versões de microsserviços que podem ser implementadas incrementalmente, reduzindo assim o tempo de inatividade;
  • Detecção de comportamento indesejado antes que uma versão antiga do aplicativo seja completamente substituída;
  • Uso de várias linguagens de programação;
  • Prevenção de falhas sistêmicas devido a uma causa raiz em um componente isolado;
  • Balanceamento de carga em tempo real.

Na Capital One, aplicamos a arquitetura de microsserviços para ajudar a aumentar nossa velocidade de entrega sem comprometer a qualidade, então temos experiência em usar tipos de padrões de design como esses em primeira mão. Claro, entender as melhores práticas de microsserviços ajudará você a aproveitar ao máximo os benefícios. Antes de incorporar qualquer prática recomendada, o primeiro passo é entender as práticas de design de microsserviços que você pode usar com frequência durante o desenvolvimento.

1. Padrão de banco de dados por serviço

O banco de dados é um dos componentes mais importantes da arquitetura de microsserviços, mas não é incomum os desenvolvedores ignorarem o padrão de banco de dados por serviço ao construir seus serviços. A organização do banco de dados afetará a eficiência e a complexidade da aplicação. As opções mais comuns que um desenvolvedor pode usar ao determinar a arquitetura organizacional de uma aplicação são:

Banco de dados dedicado para cada serviço:

Um banco de dados dedicado a um serviço não pode ser acessado por outros serviços. Essa é uma das razões que torna muito mais fácil dimensionar e entender do ponto de vista de negócios como um todo.

Imagine um cenário em que seus bancos de dados têm necessidades ou requisitos de acesso diferentes. Os dados de propriedade de um serviço podem ser em grande parte relacionais, enquanto um segundo serviço pode ser melhor atendido por uma solução NoSQL e um terceiro serviço pode exigir um banco de dados de vetor. Nesse cenário, usar serviços dedicados para cada banco de dados pode ajudar a gerenciá-los com mais facilidade.

Essa estrutura também reduz o acoplamento, pois um serviço não pode se vincular às tabelas de outro. Os serviços são obrigados a se comunicar por meio de interfaces publicadas. A desvantagem é que bancos de dados dedicados requerem um mecanismo de proteção contra falhas para eventos em que a comunicação falha.

Banco de dados único compartilhado por todos os serviços:

Um único banco de dados compartilhado não é o padrão da arquitetura de microsserviços, mas vale a pena mencionar como uma alternativa mesmo assim. Aqui, o problema é que os microsserviços que usam um único banco de dados compartilhado perdem muitos dos principais benefícios nos quais os desenvolvedores confiam, incluindo escalabilidade, robustez e independência.

No entanto, compartilhar um banco de dados físico pode ser apropriado em algumas situações. Quando um único banco de dados é compartilhado por todos os serviços, é muito importante impor limites lógicos dentro dele. Por exemplo, cada serviço deve possuir seu próprio esquema e o acesso de leitura/escrita deve ser restrito para garantir que os serviços não possam acessar informações que não lhes pertencem.

2. Padrão de saga

Uma saga é uma série de transações locais. Em aplicações de microsserviços, um padrão de saga pode ajudar a manter a consistência dos dados durante transações distribuídas.

O padrão de saga é uma solução alternativa a outros padrões de design que permitem várias transações, oferecendo oportunidades de rollback.

Um cenário comum é um aplicativo de comércio eletrônico que permite que os clientes comprem produtos usando crédito. Os dados podem ser armazenados em dois bancos de dados diferentes: um para pedidos e outro para clientes. O valor da compra não pode exceder o limite de crédito. Para implementar o padrão de saga, os desenvolvedores podem escolher entre duas abordagens comuns.

1. Coreografia:

Usando a abordagem de coreografia, um serviço realizará uma transação e, em seguida, publicará um evento. Em algumas situações, outros serviços responderão a esses eventos publicados e realizarão tarefas de acordo com suas instruções codificadas. Essas tarefas secundárias podem ou não publicar eventos também, de acordo com as configurações predefinidas. No exemplo acima, você poderia usar uma abordagem de coreografia para que cada transação de comércio eletrônico local publique um evento que acione uma transação local no serviço de crédito.

2. Orquestração:

Uma abordagem de orquestração realizará transações e publicará eventos usando um objeto para orquestrar os eventos, acionando outros serviços para responder completando suas tarefas. O orquestrador informa aos participantes quais transações locais devem ser executadas.

Saga é um padrão de design complexo que requer um alto nível de habilidade para ser implementado com sucesso. No entanto, o benefício de uma implementação adequada é a consistência dos dados mantida em vários serviços sem acoplamento rígido.

3. Padrão de gateway de API

Para aplicativos grandes com vários clientes, implementar um padrão de gateway de API é uma opção convincente. Um dos maiores benefícios é que ele isola o cliente da necessidade de saber como os serviços foram particionados. No entanto, diferentes equipes valorizarão o padrão de gateway de API por diferentes motivos. Um desses possíveis motivos é porque ele concede um único ponto de entrada para um grupo de microsserviços, funcionando como um proxy reverso entre os aplicativos do cliente e os serviços. Outro motivo é que os clientes não precisam saber como os serviços estão particionados e os limites dos serviços podem evoluir independentemente, já que o cliente não sabe nada sobre eles.

O cliente também não precisa saber como encontrar ou se comunicar com vários serviços em constante mudança. Você também pode criar um gateway para tipos específicos de clientes (por exemplo, backends para frontends), o que melhora a ergonomia e reduz o número de idas e vindas necessárias para buscar dados. Além disso, um padrão de gateway de API pode cuidar de tarefas cruciais como autenticação, terminação SSL e armazenamento em cache, tornando seu aplicativo mais seguro e amigável ao usuário.

Outra vantagem é que o padrão isola o cliente da necessidade de saber como os serviços foram particionados. Antes de passarmos para o próximo padrão, há mais um benefício a ser abordado: segurança. A principal forma como o padrão melhora a segurança é reduzindo a área de superfície de ataque. Ao fornecer um único ponto de entrada, os endpoints da API não são expostos diretamente aos clientes e a autorização e o SSL podem ser implementados de forma eficiente.

Os desenvolvedores podem usar esse padrão de design para desacoplar os microsserviços internos dos aplicativos do cliente, de modo que uma solicitação parcialmente falhada possa ser utilizada. Isso garante que uma solicitação completa não falhe porque um único microsserviço não está respondendo. Para fazer isso, o gateway de API codificado utiliza o cache para fornecer uma resposta vazia ou retornar um código de erro válido.

4. Padrão de design de agregador

Um padrão de design de agregador é usado para coletar dados de vários microsserviços e retornar um agregado para processamento. Embora seja semelhante ao padrão de design de backend-for-frontend (BFF), um agregador é mais genérico e não é usado explicitamente para a interface do usuário.

Para concluir tarefas, o padrão de agregador recebe uma solicitação e envia solicitações a vários serviços, com base nas tarefas atribuídas a ele. Assim que cada serviço responder às solicitações, esse padrão de design combina os resultados e inicia uma resposta para a solicitação original.

5. Padrão de design de Circuit breaker

Esse padrão é geralmente aplicado entre serviços que se comunicam de forma síncrona. Um desenvolvedor pode decidir utilizar o disjuntor quando um serviço está apresentando alta latência ou está completamente inoperante. A utilidade aqui é que a falha em vários sistemas é evitada quando um único microsserviço não está respondendo. Portanto, as chamadas não se acumularão e usarão os recursos do sistema, o que poderia causar atrasos significativos no aplicativo ou até mesmo uma sequência de falhas de serviço.

Implementar esse padrão como uma função em um padrão de design de disjuntor requer a chamada de um objeto para monitorar as condições de falha. Quando uma condição de falha é detectada, o disjuntor será acionado. Uma vez que isso aconteça, todas as chamadas ao disjuntor resultarão em um erro e serão direcionadas a um serviço diferente. Alternativamente, as chamadas podem resultar na recuperação de uma mensagem de erro padrão.

Existem três estados das funções do padrão de design de disjuntor que os desenvolvedores devem conhecer. São eles:

  • Aberto: Um padrão de design de disjuntor está aberto quando o número de falhas excede o limite. Quando nesse estado, o microsserviço retorna erros para as chamadas sem executar a função desejada.
  • Fechado: Quando um disjuntor está fechado, ele está no estado padrão e todas as chamadas são respondidas normalmente. Esse é o estado ideal em que os desenvolvedores desejam que um microsserviço de disjuntor permaneça - em um mundo perfeito, é claro.
  • Meio aberto: Quando um disjuntor está verificando problemas subjacentes, ele permanece em um estado meio aberto. Algumas chamadas podem ser respondidas normalmente, mas outras não. Isso depende do motivo pelo qual o disjuntor mudou para esse estado inicialmente.

6. Separação de consulta e comando (CQRS)

Um desenvolvedor pode usar um padrão de design de separação de consulta e comando (CQRS) se desejar uma solução para problemas tradicionais de banco de dados, como risco de conflito de dados. O CQRS também pode ser usado em situações em que o desempenho e a segurança do aplicativo são complexos e os objetos são expostos tanto para leitura quanto para gravação de transações.

A forma como isso funciona é que o CQRS é responsável por alterar o estado da entidade ou retornar o resultado em uma transação. Várias visualizações podem ser fornecidas para fins de consulta, e o lado de leitura do sistema pode ser otimizado separadamente do lado de gravação. Essa mudança permite reduzir a complexidade de todos os aplicativos, consultando modelos e comandos separadamente, para que:

  • O lado de gravação do modelo manipula eventos de persistência e atua como uma fonte de dados para o lado de leitura;
  • O lado de leitura do modelo gera projeções dos dados, que são visualizações altamente desnormalizadas.

7. Mensagens assíncronas

Se um serviço não precisa esperar por uma resposta e pode continuar executando seu código após uma falha, as mensagens assíncronas podem ser usadas. Usando esse padrão de design, os microsserviços podem se comunicar de maneira rápida e responsiva. Às vezes, esse padrão é chamado de comunicação orientada a eventos.

Para obter o aplicativo mais rápido e responsivo possível, os desenvolvedores podem usar uma fila de mensagens para maximizar a eficiência e minimizar os atrasos na resposta. Esse padrão pode ajudar a conectar vários microsserviços sem criar dependências ou acoplamento rígido. Embora haja compensações a serem feitas com a comunicação assíncrona (como consistência eventual), ainda é uma abordagem flexível e escalável para projetar uma arquitetura de microsserviços.

8. Event sourcing

O padrão de design de event sourcing é usado em microsserviços quando um desenvolvedor deseja capturar todas as alterações no estado de uma entidade. O uso de armazenamentos de eventos como Kafka ou alternativas ajudará a acompanhar as alterações de eventos e até mesmo funcionar como um corretor de mensagens. Um corretor de mensagens ajuda na comunicação entre diferentes microsserviços, monitorando mensagens e garantindo que a comunicação seja confiável e estável. Para facilitar essa função, o padrão de design de event sourcing armazena uma série de eventos de mudança de estado e pode reconstruir o estado atual reproduzindo as ocorrências de uma entidade.

O uso de event sourcing é uma opção viável em microsserviços quando as transações são críticas para o aplicativo. Isso também funciona bem quando as alterações no código da camada de dados existente precisam ser evitadas.

9. Estrangulador

Os desenvolvedores geralmente usam o padrão de design de estrangulador para transformar incrementalmente um aplicativo monolítico em microsserviços. Isso é realizado substituindo a funcionalidade antiga por um novo serviço - e, consequentemente, é assim que o padrão recebe seu nome. Assim que o novo serviço estiver pronto para ser executado, o serviço antigo é "estrangulado" para que o novo possa assumir.

Para realizar essa transferência bem-sucedida de monólito para microsserviços, os desenvolvedores usam uma interface de fachada que permite expor serviços e funções individuais. As funções-alvo são libertadas do monólito para que possam ser "estranguladas" e substituídas.

Para entender completamente esse padrão específico, é útil entender como os aplicativos monolíticos diferem dos microsserviços.

10. Padrões de decomposição

Os padrões de design de decomposição são usados para dividir um aplicativo monolítico em microsserviços menores e mais gerenciáveis. Um desenvolvedor pode alcançar isso de três maneiras:

1. Decomposição por capacidade de negócio:

Muitas empresas têm mais de uma capacidade de negócio. Por exemplo, uma loja de comércio eletrônico provavelmente terá capacidades que incluem gerenciamento de catálogos de produtos, inventário, pedidos e entrega. No passado, um único aplicativo monolítico pode ter sido usado para cada serviço, mas digamos, por exemplo, que a empresa decida criar um aplicativo de microsserviços para gerenciar esses serviços no futuro. Nesse cenário comum, a empresa pode optar por usar a decomposição por capacidade de negócio.

Isso pode ser usado quando um aplicativo tem um grande número de funções ou processos inter-relacionados. Os desenvolvedores também podem usá-lo quando as funções ou processos são propensos a mudanças frequentes. O benefício é que ter serviços mais focados e menores permite iterações e experimentações mais rápidas.

2. Decomposição por subdomínio:

Isso é adequado para aplicativos excepcionalmente grandes e complexos que utilizam muita lógica de negócio. Por exemplo, você pode usar isso se um aplicativo usa vários fluxos de trabalho, modelos de dados e modelos independentes. Dividir o aplicativo em subdomínios ajuda a facilitar o gerenciamento da base de código, ao mesmo tempo em que facilita o desenvolvimento e a implantação mais rápidos. Um exemplo fácil de entender é um blog hospedado em um subdomínio separado (por exemplo, blog.nomedaempresa.com). Essa abordagem pode separar o blog da lógica de negócio do domínio raiz.

3. Decomposição por transação:

Esse é um padrão apropriado para muitas operações transacionais em vários componentes ou serviços. Os desenvolvedores podem escolher essa opção quando há requisitos estritos de consistência. Por exemplo, considere casos em que uma solicitação de seguro é enviada. A solicitação de sinistro pode interagir tanto com um aplicativo de Clientes quanto com microsserviços de Sinistros ao mesmo tempo.

Utilizar padrões de design para tornar a organização mais gerenciável

Configurar a arquitetura adequada e as ferramentas de processo ajudará você a criar um fluxo de trabalho de microsserviços bem-sucedido. Use os padrões de design descritos acima e saiba mais sobre microsserviços em nosso blog para criar um aplicativo robusto e funcional.


Este é um artigo traduzido. O artigo original pode ser lido no link abaixo.

10 microservices design patterns for better architecture
Consider using these popular design patterns in your next microservices app and make organization more manageable.