Construção do Domínio Microserviços Conduzidos

Chandra Ramalingam

Seguinte

Jul 1, 2020 – 18 min ler

Créditos de imagem:

O termo ‘micro’ em Microservices, embora indicativo do tamanho de um serviço, não é o único critério que faz de uma aplicação um Microservice. Quando as equipes mudam para uma arquitetura baseada em microserviços, o objetivo é aumentar sua agilidade – implantar recursos de forma autônoma e frequente. É difícil definir de forma concisa e única este estilo arquitetônico. Gostei desta definição curta de Adrian Cockcroft – “arquitetura orientada a serviços composta por elementos frouxamente acoplados que têm contextos limitados”

Embora isto defina um design heurístico de alto nível, a arquitetura Microservices tem algumas características únicas que a diferenciam da arquitetura orientada a serviços do ano passado. Algumas dessas características, abaixo. Estas e mais algumas estão bem documentadas – artigo de Martin Fowler e Sam Newman’s Building Microservices, para citar algumas.

  1. Os serviços têm limites bem definidos centrados em torno do contexto empresarial, e não em torno de abstrações técnicas arbitrárias
  2. Esconder detalhes de implementação e expor funcionalidades através de interfaces reveladoras de intenções
  3. Os serviços não compartilham suas estruturas internas além de seus limites. Por exemplo, nenhum compartilhamento de bancos de dados.
  4. Os serviços são resistentes a falhas.
  5. As equipes possuem suas funções independentemente e têm a capacidade de liberar mudanças de forma autônoma
  6. As equipes abraçam uma cultura de automação. Por exemplo, testes automatizados, integração contínua e entrega contínua

Em suma, podemos resumir este estilo de arquitetura como abaixo:

Arquitetura orientada a serviços, onde cada serviço é encerrado dentro de um contexto bem definido, permitindo a entrega rápida, freqüente e confiável de aplicações.

Contextualização orientada para o domínio e contextos limitados

O poder dos microserviços vem da definição clara da sua responsabilidade e da demarcação dos limites entre eles. O objetivo aqui é construir uma alta coesão dentro da fronteira e um baixo acoplamento fora dela. Ou seja, as coisas que tendem a mudar juntas devem estar juntas. Como em muitos problemas da vida real, isto é mais fácil de dizer do que fazer – as empresas evoluem, e os pressupostos mudam. Portanto, a capacidade de refactor é outra coisa crítica a considerar ao desenhar sistemas.

Desenho orientado para o domínio (DDD) é uma chave, e na nossa opinião, uma ferramenta necessária ao desenhar microserviços, seja quebrando um monólito ou implementando um projecto de raiz. O design orientado para o domínio, tornado famoso por Eric Evans pelo seu livro , é um conjunto de ideias, princípios e padrões que ajudam a desenhar sistemas de software baseados no modelo subjacente do domínio empresarial. Os desenvolvedores e especialistas em domínio trabalham juntos para criar modelos de negócios em uma linguagem comum e onipresente. Eles então ligam esses modelos a sistemas onde eles fazem sentido, estabelecem protocolos de colaboração entre esses sistemas e as equipes que trabalham nesses serviços. Mais importante ainda, eles projetam os contornos conceituais ou limites entre os sistemas.

O projeto de microsserviços se inspira nesses conceitos, pois todos esses princípios ajudam a construir sistemas modulares que podem mudar e evoluir independentemente uns dos outros.

Antes de prosseguirmos, vamos rapidamente analisar algumas das terminologias básicas do DDD. Uma visão geral completa do Domain-Driven Design está fora do escopo deste blog. Nós recomendamos altamente o livro de Eric Evans para qualquer um que tente construir microserviços

Domínio: Representa o que uma organização faz. No exemplo abaixo, seria Varejo ou eCommerce.

Subdomínio: Uma organização ou unidade de negócios dentro de uma organização. Um domínio é composto de múltiplos subdomínios.

Linguagem ubíqua: Esta é a linguagem usada para expressar os modelos. No exemplo abaixo, Item é um Modelo que pertence à linguagem ubíqua de cada um desses subdomínios. Desenvolvedores, Gerentes de Produto, especialistas no domínio e partes interessadas no negócio concordam na mesma linguagem e a utilizam em seus artefatos – Código, Documentação de Produto, e assim por diante.

Fig 1. Subdomínios e Contextos Limitados no domínio do Comércio Electrónico

Contextos Limitados: O desenho orientado pelo domínio define Contextos Limitados como “O cenário em que aparece uma palavra ou uma declaração que determina o seu significado”. Em suma, isto significa o limite dentro do qual um modelo faz sentido. No exemplo acima, “Item” assume um significado diferente em cada um desses contextos. No contexto do Catálogo, um Item significa um produto vendável, enquanto que, no contexto do Carrinho, significa o item que o cliente adicionou ao seu carrinho. No contexto de Cumprimento, significa um Item de Depósito que será enviado ao cliente. Cada um desses modelos é diferente e cada um tem um significado diferente e possivelmente contém atributos diferentes. Ao separar e isolar estes modelos dentro dos seus respectivos limites, podemos expressar os modelos livremente e sem ambiguidade.

Note: É essencial compreender a distinção entre Subdomínios e contextos Limitados. Um subdomínio pertence ao espaço do problema, ou seja, como o seu negócio vê o problema, enquanto os contextos Bounded pertencem ao espaço de solução, ou seja, como vamos implementar a solução para o problema. Teoricamente, cada subdomínio pode ter múltiplos contextos delimitados, embora nos esforcemos por um contexto delimitado por subdomínio.

Como os Microservices estão relacionados aos contextos Limitados

Agora, onde se encaixam os Microservices? É justo dizer que cada mapa de contexto limitado a um microsserviço? Sim e não. Veremos porquê. Pode haver casos em que o limite ou contorno do seu contexto delimitado é bastante grande.

Fig 2. Contextualização e micro-serviços encadernados

Consulte o exemplo acima. O contexto de Preço Limitado tem três modelos distintos – Preço, Itens com Preço e Descontos, cada um responsável pelo preço de um item do catálogo, calculando o preço total de uma lista de itens, e aplicando descontos respectivamente. Poderíamos criar um sistema único que englobasse todos os modelos acima, mas poderia tornar-se uma aplicação exageradamente grande. Cada um dos modelos de dados, como mencionado anteriormente, tem as suas invariantes e regras de negócio. Com o tempo, se não tivermos cuidado, o sistema pode tornar-se uma grande bola de lama com limites obscurecidos, responsabilidades sobrepostas e, provavelmente, de volta ao ponto de partida – um monólito.

Uma outra forma de modelar este sistema é separar, ou agrupar modelos relacionados em microserviços separados. No DDD, estes modelos – Price, Priced Items, and Discounts – são chamados de Aggregates. Um agregado é um modelo auto-contido que compõe os modelos relacionados. Você poderia modificar o estado de um agregado somente através de uma interface publicada, e o agregado garante a consistência e que as invariantes se mantenham boas.

Formalmente, Um Agregado é um cluster de objetos associados tratados como uma unidade para modificações de dados. As referências externas são restritas a um membro do AGGREGADO, designado como a raiz. Um conjunto de regras de consistência aplica-se dentro dos limites do AGGREGATE.

Fig 3. Microserviços no contexto de preços

Again, não é necessário modelar cada agregado como um microserviço distinto. Isto acabou sendo feito para os serviços (agregados) na Fig. 3, mas isso não é necessariamente uma regra. Em alguns casos, pode fazer sentido hospedar múltiplos agregados em um único serviço, particularmente quando não entendemos completamente o domínio do negócio. Uma coisa importante a notar é que a consistência só pode ser garantida dentro de um único agregado, e os agregados só podem ser modificados através da interface publicada. Qualquer violação destes assume o risco de se transformar numa grande bola de lama.

Mapas de contexto – Uma forma de esculpir limites precisos de microserviços

Outro conjunto de ferramentas essenciais no seu arsenal é o conceito de Mapas de contexto – novamente, do Domain Driven Design. Um monólito é geralmente composto de modelos díspares, a maioria firmemente acoplados – modelos talvez conheçam os detalhes íntimos um do outro, mudando um poderia causar efeitos colaterais em outro, e assim por diante. Ao decompor o monólito, é vital identificar esses modelos – agregados, neste caso – e suas relações. Os mapas de contexto nos ajudam a fazer exatamente isso. Eles são usados para identificar e definir as relações entre vários contextos e agregados delimitados. Enquanto os contextos delimitados definem os limites de um modelo – Preço, Descontos, etc. no exemplo acima, os mapas de contexto definem as relações entre estes modelos e entre diferentes contextos. Após a identificação destas dependências, podemos determinar o modelo de colaboração correto entre as equipes que irão implementar estes serviços.

Uma exploração completa dos mapas de Contexto está além do escopo deste blog, mas vamos ilustrá-lo com um exemplo. O diagrama abaixo representa as várias aplicações que tratam de pagamentos para um pedido de eCommerce.

  1. O contexto do carrinho cuida das autorizações online de um pedido; Processos de contexto de pedidos pós-pagamento como Liquidações; O Contact center lida com quaisquer exceções como tentativas de pagamento e alteração do método de pagamento usado para o pedido
  2. Por uma questão de simplicidade, vamos assumir que todos estes contextos são implementados como serviços separados
  3. Todos estes contextos encapsulam o mesmo modelo.
  4. Notem que estes modelos são logicamente os mesmos. Ou seja, todos eles seguem a mesma linguagem de domínio ubíqua – métodos de pagamento, autorizações e liquidações. Apenas que eles fazem parte de contextos diferentes.

Outro sinal de que o mesmo modelo está espalhado por contextos diferentes é que todos eles se integram diretamente com um único gateway de pagamento e fazem as mesmas operações uns dos outros

Fig 4. Um mapa de contexto mal definido

Redefinir os limites do serviço – Mapear os agregados para os contextos certos

Existem alguns problemas que são muito evidentes no desenho acima (Fig. 4). O agregado de pagamentos faz parte de múltiplos contextos. É impossível impor invariantes e consistência entre vários serviços, sem mencionar os problemas de concurrência entre estes serviços. Por exemplo, o que acontece se o centro de contato muda o método de pagamento associado à ordem enquanto o serviço de ordens está tentando lançar a liquidação de um método de pagamento previamente submetido. Observe também que qualquer mudança no gateway de pagamento forçaria mudanças em múltiplos serviços e potencialmente numerosas equipes, pois diferentes grupos poderiam possuir estes contextos.

Com alguns ajustes e alinhando os agregados aos contextos certos, obtemos uma representação muito melhor destes subdomínios – Fig. 5. Há muita coisa que mudou. Vamos rever as mudanças:

  1. O agregado de pagamentos tem uma nova casa – Serviço de pagamento. Este serviço também abstrai o gateway de pagamento dos outros serviços que requerem serviços de pagamento. Como um único contexto limitado agora possui um agregado, as invariantes são fáceis de gerenciar; todas as transações acontecem dentro do mesmo limite de serviço ajudando a evitar problemas de concorrência.
  2. O agregado de pagamentos usa uma camada anti-corrupção (ACL) para isolar o modelo de domínio central do modelo de dados do gateway de pagamento, que normalmente é um provedor terceirizado e talvez sujeito a mudanças. Vamos mergulhar mais fundo no design de aplicações como o serviço usando Portos e Adaptadores padrão em um post futuro. A camada ACL geralmente contém os adaptadores que transformam o modelo de dados do gateway de pagamento para o modelo de dados agregados de Pagamentos.
  3. Serviço de Cartões chama o serviço de Pagamentos através de chamadas API diretas, pois o serviço de Cartões pode ter que completar a autorização de pagamento enquanto os clientes estão no site
  4. Faça uma nota sobre a interação entre Pedidos e Serviço de Pagamento. O serviço de Encomendas emite um evento de domínio (mais sobre isso mais adiante neste blog). O serviço de pagamentos ouve este evento e conclui a liquidação da encomenda
  5. O serviço de centro de contacto pode ter muitos agregados, mas só estamos interessados no agregado de Encomendas para este caso de utilização. Este serviço emite um evento quando a forma de pagamento muda, e o serviço de pagamentos reage a ele invertendo o cartão de crédito utilizado anteriormente e processando o novo cartão de crédito.

Fig 5. Mapa de contexto redefinido

Usualmente, uma aplicação monolítica ou de legado tem muitos agregados, muitas vezes com limites sobrepostos. Criar um mapa de contexto desses agregados e suas dependências nos ajuda a entender os contornos de quaisquer novos microserviços que iremos arrancar desses monólitos. Lembre-se, o sucesso ou fracasso da arquitetura dos microserviços depende do baixo acoplamento entre os agregados e da alta coesão dentro desses agregados.

Também é importante notar que os contextos delimitados são unidades coesivas adequadas. Mesmo que um contexto tenha vários agregados, todo o contexto, juntamente com seus agregados, pode ser composto em um único micro-serviço. Achamos esta heurística particularmente útil para domínios que são um pouco obscuros – pense numa nova linha de negócio em que a organização se está a aventurar. Você pode não ter uma visão suficiente dos limites certos de separação e qualquer decomposição prematura de agregados pode levar a uma refatoração cara. Imagine ter que fundir duas bases de dados em uma só, junto com a migração de dados, porque por acaso descobrimos que dois agregados pertencem juntos. Mas assegure-se de que esses agregados estejam suficientemente isolados através de interfaces para que eles não conheçam os detalhes intrincados um do outro.

Event Storming – Outra técnica para identificar limites de serviço

Event Storming é outra técnica essencial para identificar agregados (e, portanto, microserviços) em um sistema. É uma ferramenta útil tanto para quebrar monólitos quanto para projetar um ecossistema complexo de micro-serviços. Temos usado esta técnica para quebrar uma de nossas aplicações complexas, e pretendemos cobrir nossas experiências com o Event Storming em um blog separado. Para o escopo deste blog, queremos dar uma rápida visão geral de alto nível. Assista ao vídeo de Alberto Brandelloni sobre o tema se você estiver interessado em explorar mais.

Em poucas palavras, Event Storming é um exercício de brainstorming entre as equipes que trabalham em uma aplicação – no nosso caso, um monólito – para identificar os vários eventos e processos de domínio que acontecem dentro de um sistema. As equipes também identificam os agregados ou modelos que esses eventos afetam e quaisquer impactos subseqüentes. medida que as equipes fazem esse exercício, elas identificam diferentes conceitos sobrepostos, linguagem de domínio ambígua e processos de negócios conflitantes. Elas agrupam modelos relacionados, redefinem os agregados e identificam processos duplicados. À medida que avançam com este exercício, tornam-se claros os contextos delimitados a que estes agregados pertencem. Os workshops de Event Storming são úteis se todas as equipes estiverem em uma única sala – física ou virtual – e começam a mapear os eventos, comandos e processos em um quadro branco em estilo scrum. No final deste exercício, abaixo estão os resultados habituais:

  1. Lista redefinida de Agregados. Estes potencialmente se tornam novos microserviços
  2. Agregados que precisam fluir entre estes microserviços
  3. Comandos que são invocações diretas de outras aplicações ou usuários

Mostramos um quadro de exemplo ao final de um workshop de Event Storming abaixo. É um grande exercício de colaboração para as equipes concordarem sobre os agregados certos e contextos delimitados. Além de ser um ótimo exercício de construção de equipe, as equipes saem desta sessão com uma compreensão compartilhada do domínio, linguagem ubíqua e limites precisos de serviço.

Fig 6. Evento Storming board

Comunicação entre microserviços

Para recapitular rapidamente, um monólito hospeda múltiplos agregados dentro de um único limite de processo. Portanto, é possível gerenciar a consistência dos agregados dentro desse limite. Por exemplo, se um Cliente faz um Pedido, podemos diminuir o Inventário dos itens, enviar um e-mail para o Cliente – tudo dentro de uma única transação. Todas as operações seriam bem sucedidas, ou todas fracassariam. Mas, à medida que quebramos o monólito e espalhamos os agregados em diferentes contextos, teremos dezenas ou mesmo centenas de microserviços. Os processos que até então existiam dentro do limite único de um monólito estão agora espalhados por múltiplos sistemas distribuídos. Alcançar integridade e consistência transacional em todos esses sistemas distribuídos é muito difícil, e isso tem um custo – a disponibilidade dos sistemas.

Microservices são sistemas distribuídos também. Portanto, o teorema do PAC também se aplica a eles – “um sistema distribuído pode fornecer apenas duas das três características desejadas: consistência, disponibilidade e tolerância de partição (o ‘C,’ ‘A’ e ‘P’ no PAC)”. Em sistemas do mundo real, a tolerância de partição não é negociável – a rede não é confiável, máquinas virtuais podem cair, a latência entre regiões pode se tornar pior, e assim por diante.

Então isso nos deixa com uma escolha entre Disponibilidade ou Consistência. Agora, sabemos que em qualquer aplicação moderna, sacrificar a disponibilidade também não é uma boa idéia.

Fig 7. Teorema do CAP

Aplicações de desenho em torno de uma eventual consistência

Se você tentar construir transações através de vários sistemas distribuídos, você vai acabar no terreno monolítico novamente. Só que desta vez será o pior tipo, um monólito distribuído. Se algum dos sistemas ficar indisponível, todo o processo se torna indisponível, levando frequentemente a experiências frustrantes para o cliente, promessas falhadas, e assim por diante. Além disso, mudanças em um serviço podem geralmente implicar em mudanças em outro serviço, levando a implantações complexas e dispendiosas. Por isso, é melhor concebermos aplicações à medida dos nossos casos de utilização para tolerar um pouco de inconsistência em favor da disponibilidade. Para o exemplo acima, podemos tornar todos os processos assíncronos e, portanto, eventualmente, consistentes. Podemos enviar e-mails de forma assíncrona, independentemente dos outros processos; se um item prometido não estiver disponível no depósito mais tarde, o item pode ser encomendado em atraso, ou podemos parar de receber pedidos para o item além de um determinado limite.
Ocasionalmente, você pode encontrar um cenário que pode exigir transações fortes no estilo ACID através de dois agregados em diferentes limites de processo. Isso é um excelente sinal para revisitar esses agregados e talvez combiná-los em um só. O Event Storming e os Mapas de Contexto ajudarão a identificar essas dependências antes de começarmos a quebrar esses agregados em diferentes limites de processo. A fusão de dois microserviços em um só é cara, e isso é algo que devemos nos esforçar para evitar.

Favor arquitetura orientada a eventos

Microserviços podem emitir alterações essenciais que acontecem com seus agregados. Estes são chamados eventos de domínio, e quaisquer serviços que estejam interessados nestas mudanças podem ouvir estes eventos e tomar as respectivas medidas dentro dos seus domínios. Este método evita qualquer acoplamento comportamental – um domínio não prescreve o que os outros domínios devem fazer, e o acoplamento temporal – a conclusão bem sucedida de um processo não depende de todos os sistemas para estar disponível ao mesmo tempo. Isto, claro, significa que os sistemas serão eventualmente consistentes.

Fig 8. Arquitetura orientada a eventos

No exemplo acima, o serviço de pedidos publica um evento – Pedido Cancelado. Os outros serviços que subscreveram o evento processam as suas respectivas funções de domínio: O serviço de pagamento reembolsa o dinheiro, o serviço de Inventário ajusta o inventário dos itens, e assim por diante. Poucas coisas a notar para assegurar a fiabilidade e resiliência desta integração:

  1. Os produtores devem assegurar-se de que produzem um evento pelo menos uma vez. Se houver falhas ao fazê-lo, eles devem assegurar-se de que existe um mecanismo de retorno de queda presente para re-iniciar os eventos
  2. Os consumidores devem assegurar-se de que eles consomem os eventos de uma forma idempotente. Se o mesmo evento ocorrer novamente, não deve haver nenhum efeito colateral no final do consumidor. Os eventos também podem chegar fora de sequência. Os consumidores podem usar os campos Timestamp ou números de versão para garantir a singularidade dos eventos.

Pode nem sempre ser possível usar a integração baseada em eventos, devido à natureza de alguns casos de uso. Por favor, dê uma olhada na integração entre o serviço Carrinho e o serviço Pagamento. É uma integração síncrona e, portanto, tem algumas coisas que devemos ter em atenção. É um exemplo de acoplamento comportamental – O serviço Cart talvez chame um REST API do serviço Payment e o instrua a autorizar o pagamento de uma encomenda, e o acoplamento temporal – O serviço Payment precisa estar disponível para que o serviço Cart aceite uma encomenda. Este tipo de acoplamento reduz a autonomia destes contextos e talvez uma dependência indesejável. Existem algumas formas de evitar este acoplamento, mas com todas estas opções, perderemos a capacidade de fornecer feedback imediato aos clientes.

  1. Converter o REST API para uma integração baseada em eventos. Mas essa opção pode não estar disponível se o Serviço de Pagamento expuser apenas uma API REST
  2. O serviço de pagamento aceita um pedido instantaneamente, e há um trabalho em lote que pega os pedidos e chama a API do serviço de pagamento
  3. O serviço de pagamento produz um evento local que então chama a API do serviço de pagamento

Uma combinação do acima com tentativas de novo em caso de falhas e indisponibilidade da dependência upstream – Serviço de pagamento – pode resultar em um design muito mais resistente. Por exemplo, a integração síncrona entre o Carrinho e os serviços de Pagamento pode ser respaldada por um evento ou por novas tentativas baseadas em lotes em caso de falhas. Esta abordagem tem um impacto adicional na experiência do cliente – os clientes podem ter introduzido detalhes de pagamento incorrectos, e não os teremos online quando processarmos os pagamentos offline. Ou pode haver um custo adicional para o negócio para recuperar pagamentos falhados. Mas, com toda a probabilidade, os benefícios do serviço de Carrinho de Compras serem resilientes à indisponibilidade ou falhas do serviço de Pagamento compensam as falhas. Por exemplo, podemos notificar os clientes se não formos capazes de cobrar pagamentos offline. Em resumo, existem trade-offs entre experiência do usuário, resiliência e custos operacionais, e é sábio projetar sistemas, mantendo esses compromissos em mente.

Evite orquestração entre serviços para necessidades de dados específicos do consumidor

Um dos anti-padrões em qualquer arquitetura orientada a serviços é que os serviços atendam aos padrões de acesso específicos dos consumidores. Normalmente, isto acontece quando as equipes de consumidores trabalham em conjunto com as equipes de serviços. Se a equipe estivesse trabalhando em uma aplicação monolítica, eles freqüentemente criariam uma única API que cruza diferentes limites de agregados, portanto, acoplando firmemente esses agregados. Vamos considerar um exemplo. Digamos a página Detalhes do Pedido na Web, e os aplicativos móveis precisam mostrar os detalhes de um Pedido e os detalhes dos reembolsos processados para o pedido em uma única página. Em uma aplicação monolítica, uma Order GET API – assumindo que é REST API – consulta Pedidos e Reembolsos juntos, consolida os dois agregados e envia uma resposta composta para os chamadores. É possível fazer isso sem muita sobrecarga, pois os agregados pertencem ao mesmo limite do processo. Assim, os consumidores podem obter todos os dados necessários em uma única chamada.

Se Pedidos e Reembolsos fazem parte de contextos diferentes, os dados não estão mais presentes dentro de um único micro-serviço ou limite do agregado. Uma opção para manter a mesma funcionalidade para os consumidores é tornar o serviço de pedidos responsável por chamar o serviço de Reembolsos e criar uma resposta composta. Esta abordagem causa várias preocupações:

1. O serviço de pedidos agora se integra a outro serviço puramente para apoiar os consumidores que precisam dos dados de Reembolso junto com os dados do pedido. O serviço de pedidos agora é menos autônomo, pois qualquer alteração no agregado de Reembolsos levará a uma alteração no agregado de Pedidos.

2. O serviço de pedidos tem outra integração e, portanto, outro ponto de falha a levar em conta – se o serviço de Reembolsos estiver em baixo, o serviço de pedidos ainda pode enviar dados parciais e os consumidores podem falhar graciosamente?

3. Se os consumidores precisarem de uma alteração para buscar mais dados do agregado de Reembolsos, duas equipes estão envolvidas agora para fazer essa alteração

4. Este padrão, se seguido através da plataforma, pode levar a uma teia intrincada de dependências entre os vários serviços de domínio, tudo porque estes serviços atendem aos padrões de acesso específicos dos chamadores.

Backend for Frontends (BFFs)

Uma abordagem para mitigar este risco é deixar as equipas de consumidores gerirem a orquestração entre os vários serviços de domínio. Afinal, os chamadores conhecem melhor os padrões de acesso e podem estar em completo controle de quaisquer alterações a estes padrões. Esta abordagem dissocia os serviços de domínio do nível de apresentação, permitindo que se concentrem nos processos centrais do negócio. Mas se os aplicativos web e móveis começarem a chamar serviços diferentes diretamente em vez de uma API composta do monólito, isso pode causar sobrecarga de desempenho para esses aplicativos – múltiplas chamadas em redes de menor largura de banda, processamento e fusão de dados de diferentes APIs, e assim por diante.

Em vez disso, pode-se usar outro padrão chamado Backend para Front-ends. Neste padrão de design, um serviço de backend criado e gerenciado pelos consumidores – neste caso, a web e as equipes móveis – cuida da integração através de múltiplos serviços de domínio, puramente para proporcionar a experiência do front-end aos clientes. As equipas web e móveis podem agora conceber os contratos de dados com base nos casos de utilização a que se destinam. Eles podem até mesmo usar GraphQL em vez de APIs REST para consultar com flexibilidade e obter de volta exatamente o que precisam. É importante notar que este serviço pertence e é mantido pelas equipes de consumidores e não pelas equipes que possuem os serviços de domínio. As equipes front-end agora podem otimizar com base em suas necessidades – um aplicativo móvel pode solicitar uma carga útil menor, reduzir o número de chamadas do aplicativo móvel, e assim por diante. Veja a visão revisada da orquestração abaixo. O serviço BFF agora chama tanto os serviços de Ordens e Reembolsos para o seu caso de uso.

Fig 9. Backend para Frontends

Também é útil construir o serviço BFF antecipadamente, antes de quebrar uma infinidade de serviços do monólito. Caso contrário, ou os serviços de domínio terão que suportar a orquestração entre domínios, ou as aplicações web e móveis terão que chamar múltiplos serviços diretamente do front-end. Ambas as opções levarão à sobrecarga de performance, trabalho de lançamento e falta de autonomia entre as equipes.

Conclusion

Neste blog, abordamos vários conceitos, estratégias e heurísticas de design a considerar quando nos aventuramos no mundo dos microserviços, mais especificamente quando tentamos quebrar um monólito em múltiplos microserviços baseados em domínios. Muitos destes são vastos tópicos por si só, e não creio que tenhamos feito justiça suficiente para explicá-los em detalhes, mas queríamos introduzir alguns dos tópicos críticos e nossa experiência na adoção dos mesmos. A seção Leitura Adicional (link) tem algumas referências e alguns conteúdos úteis para quem deseja seguir este caminho.

Atualização: Os próximos dois blogs da série estão fora. Estes dois blogs discutem a implementação do microserviço Cart, com exemplos de código, usando princípios de Design Dirigido por Domínio, e padrões de design de Portos e Adaptadores. O foco principal desses blogs é demonstrar como esses dois princípios/padrões nos ajudam a construir aplicações modulares que são ágeis, testáveis e refatoráveis – em resumo, ser capazes de responder ao ambiente de ritmo rápido em que todos nós estamos operando.

Implementing Cart Microservice using Domain Driven Design and Ports and Adapters Pattern – Part 1

Implementing Cart Microservice using Domain Driven Design and Ports and Adapters Pattern – Part 2

Outras leituras

1. Eric Evans’s Domain Driven Design

2. Vaughn Vernon’s Implementing Domain Driven Design

3. artigo de Martin Fowler sobre Microservices

4. Sam Newman’s Building Microservices

>

5. Evento de tormenta

7. Backend para Frontends

8. Falácias de computação distribuída

Deixe uma resposta

O seu endereço de email não será publicado.