Modernização de aplicações com Kafka, Debezium e Kubernetes

Modernização de aplicações com Kafka, Debezium e Kubernetes

We build our computers the way we build our cities—over time, without a plan, on top of ruins.

Ellen Ullman escreveu isto em 1998, mas isso se aplica tanto hoje quanto à maneira como construímos aplicativos modernos; ou seja, ao longo do tempo, com planos de curto prazo, em cima do software legado. Neste artigo, apresentarei alguns padrões e ferramentas que acredito funcionar bem para modernizar cuidadosamente aplicativos legados e construir sistemas modernos orientados a eventos.

Contexto de Modernização de Aplicações

A modernização de aplicativos refere-se ao processo de pegar um aplicativo legado existente e modernizar sua infraestrutura – a arquitetura interna – para melhorar a velocidade de entrega de novos recursos, melhorar o desempenho e a escalabilidade, expor a funcionalidade para novos casos de uso e assim por diante. Felizmente, já existe uma boa classificação dos tipos de modernização e migração, conforme mostra a Figura 1.

No alt text provided for this image

Dependendo de suas necessidades e apetite por mudanças, existem alguns níveis de modernização:

  • Retention: A coisa mais fácil que você pode fazer é reter o que você tem e ignorar as necessidades de modernização do aplicativo. Isso faz sentido se as necessidades ainda não forem urgentes.
  • Retirement: Outra coisa que você pode fazer é se aposentar e se livrar do aplicativo legado. Isso é possível se você descobrir que o aplicativo não está mais sendo usado.
  • Rehosting: A próxima coisa que você pode fazer é rehospedar o aplicativo, o que normalmente significa pegar um aplicativo como está e hospedá-lo em uma nova infraestrutura, como infraestrutura em nuvem, ou mesmo no Kubernetes por meio de algo como KubeVirt. Essa não é uma má opção se seu aplicativo não puder ser conteinerizado, mas você ainda quiser reutilizar suas habilidades, práticas recomendadas e infraestrutura do Kubernetes para gerenciar uma máquina virtual como um contêiner.
  • Replatforming: Quando mudar a infraestrutura não é suficiente e você está fazendo uma pequena alteração nas bordas do aplicativo sem mudar sua arquitetura, a replataforma é uma opção. Talvez você esteja alterando a forma como o aplicativo é configurado para que possa ser conteinerizado ou migrando de um runtime Java EE legado para um runtime de software livre. Aqui, você pode usar uma ferramenta como o windup para analisar sua aplicação e retornar um relatório com o que precisa ser feito.
  • Refactoring: grande parte da modernização de aplicativos hoje se concentra na migração de aplicativos monolíticos locais para uma arquitetura de microsserviços nativa da nuvem que oferece suporte a ciclos de lançamento mais rápidos. Isso envolve refatorar e rearquitetar seu aplicativo, que é o foco deste artigo.

Para este artigo, vamos supor que estamos trabalhando com um aplicativo monolítico no local, que é um ponto de partida comum para a modernização. A abordagem discutida aqui também pode se aplicar a outros cenários, inclusive em modernização de mainframes, em uma iniciativa de migração para a nuvem.

Desafios da migração de aplicativos legados monolíticos

A frequência de implantação é um desafio comum para migrar aplicativos legados monolíticos. Outro desafio é dimensionar o desenvolvimento para que mais desenvolvedores e equipes possam trabalhar em uma base de código comum sem pisar nos calos uns dos outros. Dimensionar o aplicativo para lidar com uma carga crescente de maneira confiável é outra preocupação. Por outro lado, os benefícios esperados de uma modernização incluem redução do tempo de lançamento no mercado, maior autonomia da equipe na base de código e dimensionamento dinâmico para lidar com a carga de serviço com mais eficiência. Cada um desses benefícios compensa o trabalho envolvido na modernização. A Figura 2 mostra um exemplo de infraestrutura para dimensionar um aplicativo legado para aumentar a carga.

No alt text provided for this image

Visualizando o estado-alvo e medindo o sucesso

Para nosso caso de uso, o estado de destino é um estilo de arquitetura que segue princípios de microsserviços usando tecnologias de código aberto, como Kubernetes, Apache Kafka e Debezium. Queremos terminar com serviços implementáveis de forma independente modelados em torno de um domínio de negócios. Cada serviço deve possuir seus próprios dados, emitir seus próprios eventos e assim por diante.

Quando planejamos a modernização, também é importante considerar como mediremos os resultados ou resultados de nossos esforços. Para isso, podemos usar métricas como lead time para alterações, frequência de implantação, tempo de recuperação, usuários simultâneos e assim por diante.

As próximas seções apresentarão três padrões de design e três tecnologias de código aberto – Kubernetes, Apache Kafka e Debezium – que você pode usar para migrar de sistemas brownfield para serviços greenfield, modernos e orientados a eventos. Vamos começar com o padrão Strangler.

O padrão Strangler

O padrão Strangler é a técnica mais popular usada para migrações de aplicativos. Martin Fowler introduziu e popularizou esse padrão sob o nome de Strangler Fig Application, que foi inspirado em um tipo de figo que se planta nos galhos superiores de uma árvore e evolui gradualmente em torno da árvore original, eventualmente substituindo-a. O paralelo com a migração de aplicativos é que nosso novo serviço é configurado inicialmente para envolver o sistema existente. Dessa forma, o antigo e o novo sistema podem coexistir, dando tempo ao novo sistema para crescer e potencialmente substituir o antigo. A Figura 3 mostra os principais componentes do padrão Strangler para uma migração de aplicativo legado.

No alt text provided for this image

O principal benefício do padrão Strangler é que ele permite uma migração incremental de baixo risco de um sistema legado para um novo. Vejamos cada uma das principais etapas envolvidas nesse padrão.

Etapa 1: identificar os limites funcionais

A primeira pergunta é por onde começar a migração. Aqui, podemos usar o design orientado a domínio para nos ajudar a identificar agregados e os contextos limitados em que cada um representa uma unidade potencial de decomposição e um limite potencial para microsserviços. Ou podemos usar a técnica de tempestade de eventos criada por Antonio Brandolini para obter uma compreensão compartilhada do modelo de domínio. Outras considerações importantes aqui seriam como esses modelos interagem com o banco de dados e qual trabalho é necessário para a decomposição do banco de dados. Uma vez que tenhamos uma lista desses fatores, o próximo passo é identificar as relações e dependências entre os contextos limitados para ter uma ideia da dificuldade relativa da extração.

De posse dessas informações, podemos prosseguir com a próxima pergunta: Queremos começar com o serviço que tem a menor quantidade de dependências, para uma vitória fácil, ou devemos começar com a parte mais difícil do sistema? Um bom compromisso é escolher um serviço que represente muitos outros e possa nos ajudar a construir uma boa base tecnológica. Essa base pode servir como base para estimar e migrar outros módulos.

Etapa 2: migrar a funcionalidade

Para que o padrão strangler funcione, devemos ser capazes de mapear claramente as chamadas de entrada para a funcionalidade que queremos mover. Também devemos ser capazes de redirecionar essas chamadas para o novo serviço e voltar, se necessário. Dependendo do estado do aplicativo legado, aplicativos cliente e outras restrições, ponderar nossas opções para essa interceptação pode ser simples ou difícil:

• A opção mais fácil seria alterar o aplicativo cliente e redirecionar as chamadas de entrada para o novo serviço. Tarefa concluída.

• Se o aplicativo legado usa HTTP, então começamos bem. O HTTP é muito receptivo ao redirecionamento e temos uma variedade de opções de proxy transparentes para escolher.

• Na prática, é provável que nosso aplicativo não use apenas APIs REST, mas também tenha SOAP, FTP, RPC ou algum tipo de terminal de mensagens tradicional. Nesse caso, podemos precisar construir uma camada de tradução de protocolo personalizada com algo como Apache Camel.

A interceptação é uma ladeira escorregadia potencialmente perigosa: se começarmos a construir uma camada de tradução de protocolo personalizada que é compartilhada por vários serviços, corremos o risco de adicionar muita inteligência ao proxy compartilhado do qual os serviços dependem. Isso nos afastaria do mantra "microsserviços inteligentes, tubos burros". Uma opção melhor é usar o padrão Sidecar, ilustrado na Figura 4.

No alt text provided for this image

Em vez de colocar a lógica de proxy personalizada em uma camada compartilhada, torne-a parte do novo serviço. Mas, em vez de incorporar o proxy personalizado no serviço em tempo de compilação, usamos o padrão sidecar do Kubernetes e tornamos o proxy uma atividade de vinculação em tempo de execução. Com esse padrão, os clientes legados usam o proxy de tradução de protocolo e os novos clientes recebem a nova API de serviço. Dentro do proxy, as chamadas são traduzidas e direcionadas para o novo serviço. Isso nos permite reutilizar o proxy, se necessário. Mais importante, podemos desativar facilmente o proxy quando ele não for mais necessário para clientes legados, com impacto mínimo nos serviços mais recentes.

Etapa 3: migrar o banco de dados

Depois de identificarmos o limite funcional e o método de interceptação, precisamos decidir como abordaremos o estrangulamento do banco de dados, ou seja, separando nosso banco de dados legado dos serviços de aplicativos. Temos alguns caminhos para escolher.

Data First

Em uma abordagem de banco de dados em primeiro lugar, separamos o esquema primeiro, o que poderia impactar o aplicativo legado. Por exemplo, um SELECT pode exigir a extração de dados de dois bancos de dados e um UPDATE pode levar à necessidade de transações distribuídas. Essa opção requer alterações no aplicativo de origem e não nos ajuda a demonstrar o progresso no curto prazo. Não é isso que estamos procurando.

Code First

Uma abordagem code-first nos permite chegar rapidamente a serviços implantados de forma independente e reutilizar o banco de dados legado, mas isso pode nos dar uma falsa sensação de progresso. Separar o banco de dados pode ser um desafio e ocultar futuros gargalos de desempenho. Mas é um movimento na direção certa e pode nos ajudar a descobrir a propriedade dos dados e o que precisa ser dividido na camada de banco de dados posteriormente.

Código e banco de dados juntos

Trabalhar no código e no banco de dados juntos pode ser difícil desde o início, mas, em última análise, é o estado final que queremos chegar. Independentemente de como fazemos isso, queremos terminar com um serviço e um banco de dados separados; começar com isso em mente nos ajudará a evitar refatoração mais tarde.

Ter um banco de dados separado requer sincronização de dados. Mais uma vez, podemos escolher entre algumas abordagens tecnológicas comuns.

Triggers

A maioria dos bancos de dados nos permite executar um comportamento personalizado quando os dados são alterados. Em alguns casos, isso pode até chamar um serviço da web e integrar com outro sistema. Mas como os triggers são implementados e o que podemos fazer com eles varia entre os bancos de dados. Outra desvantagem significativa aqui é que o uso de triggers requer a alteração do banco de dados legado, o que podemos estar relutantes em fazer.

Queries

Podemos usar consultas para verificar regularmente se há alterações no banco de dados de origem. As alterações são normalmente detectadas com estratégias de implementação, como carimbos de data/hora, números de versão ou alterações de coluna de status no banco de dados de origem. Independentemente da estratégia de implementação, a sondagem sempre leva ao dilema entre sondar com frequência e criar sobrecarga no banco de dados de origem ou perder atualizações frequentes. Embora as consultas sejam simples de instalar e usar, essa abordagem tem limitações significativas. Não é adequado para aplicativos de missão crítica com interações frequentes de banco de dados.

Leitores de Logs

Os leitores de log identificam as alterações verificando os arquivos de log de transações do banco de dados. Os arquivos de log existem para fins de backup e recuperação de banco de dados e fornecem uma maneira confiável de capturar todas as alterações, incluindo DELETEs. O uso de leitores de log é a opção menos disruptiva porque eles não exigem modificação no banco de dados de origem e não têm uma carga de consulta. A principal desvantagem dessa abordagem é que não há um padrão comum para os arquivos de log de transações e precisaremos de ferramentas especializadas para processá-los. É aqui que o Debezium se encaixa.

Antes de passar para a próxima etapa, vamos ver como funciona o uso do Debezium com a abordagem do leitor de log.

Change Data Capture (CDC) com o Debezium

Quando um aplicativo grava no banco de dados, as alterações são registradas nos arquivos de log e, em seguida, as tabelas do banco de dados são atualizadas. Para MySQL, o arquivo de log é binlog; para o PostgreSQL, é o log de gravação antecipada; e para o MongoDB é o log operacional. A boa notícia é que o Debezium tem conectores para diferentes bancos de dados, então ele faz o trabalho duro de entender o formato de todos esses arquivos de log. O Debezium pode ler os arquivos de log e produzir um evento abstrato genérico em um sistema de mensagens como o Apache Kafka, que contém as alterações de dados. A Figura 5 mostra os conectores Debezium como a interface para uma variedade de bancos de dados.

No alt text provided for this image

O Debezium é o projeto de captura de dados de alteração (CDC) de código aberto mais amplamente usado com vários conectores e recursos que o tornam uma ótima opção para o padrão Strangler.

Por que o Debezium é uma boa opção para o padrão Strangler?

Uma das razões mais importantes para considerar o padrão Strangler para migrar aplicativos legados monolíticos é o risco reduzido e a capacidade de retornar ao aplicativo legado. Da mesma forma, o Debezium é completamente transparente para o aplicativo legado e não requer nenhuma alteração no modelo de dados legado. A Figura 6 mostra o Debezium em um exemplo de arquitetura de microsserviços.

No alt text provided for this image

Com uma configuração mínima no banco de dados legado, podemos capturar todos os dados necessários. Então, a qualquer momento, podemos remover o Debezium e retornar ao aplicativo legado, se necessário.

Recursos do Debezium que suportam migrações legadas

Aqui estão alguns dos recursos específicos do Debezium que suportam a migração de um aplicativo legado monolítico com o padrão Strangler:

Snapshots: o Debezium pode tirar um snapshot do estado atual do banco de dados de origem, que podemos usar para importações de dados em massa. Assim que um snapshot for concluído, o Debezium começará a transmitir as alterações para manter o sistema de destino sincronizado.

Filtros: O Debezium nos permite escolher de quais bancos de dados, tabelas e colunas transmitir as alterações. Com o padrão Strangler, não estamos movendo todo o aplicativo.

Transformação de mensagem única (SMT): esse recurso pode atuar como uma camada anticorrupção e proteger nosso novo modelo de dados de nomenclatura herdada, formatos de dados e até mesmo filtrar dados obsoletos

Usando o Debezium com um registro de esquema: Podemos usar um registro de esquema como o Apicurio com Debezium para validação de esquema e também usá-lo para impor verificações de compatibilidade de versão quando o modelo de banco de dados de origem for alterado. Isso pode impedir que as alterações do banco de dados de origem afetem e interrompam os novos consumidores de mensagens downstream.

Usando o Debezium com o Apache Kafka: Há muitas razões pelas quais o Debezium e o Apache Kafka funcionam bem juntos para migração e modernização de aplicativos. Pedido garantido de alterações no banco de dados, compactação de mensagens, capacidade de reler as alterações quantas vezes forem necessárias e rastreamento de deslocamentos de log de transações são bons exemplos de por que podemos optar por usar essas ferramentas juntas.

Etapa 4: liberarando os serviços

Com essa rápida visão geral do Debezium, vamos ver onde estamos com o padrão Strangler. Suponha que, até agora, fizemos o seguinte:

• Identificou um limite funcional.

• Migração da funcionalidade.

• Migrado o banco de dados.

• Implantou o serviço em um ambiente Kubernetes.

• Migrei os dados com o Debezium e mantive o Debezium em execução para sincronizar as mudanças em andamento.

Neste ponto, ainda não há tráfego roteado para os novos serviços, mas estamos prontos para liberar os novos serviços. Dependendo dos recursos de nossa camada de roteamento, podemos usar técnicas como dark launch, execuções paralelas e canary release para reduzir ou remover o risco de lançar o novo serviço, conforme mostrado na Figura 7.

No alt text provided for this image

O que também podemos fazer aqui é direcionar apenas solicitações de leitura para nosso novo serviço inicialmente, enquanto continuamos a enviar as gravações para o sistema legado. Isso é necessário, pois estamos replicando as alterações apenas em uma única direção.

Quando vemos que as operações de leitura estão ocorrendo sem problemas, podemos direcionar o tráfego de gravação para o novo serviço. Neste ponto, se ainda precisarmos que o aplicativo legado funcione por qualquer motivo, precisaremos transmitir as alterações dos novos serviços para o banco de dados do aplicativo legado. Em seguida, desejaremos interromper qualquer atividade de gravação ou mutação no módulo legado e interromper a replicação de dados a partir dele. A Figura 8 ilustra essa parte da implementação do padrão.

No alt text provided for this image

Como ainda temos operações de leitura legadas, continuamos a replicação do novo serviço para o aplicativo legado. Eventualmente, interromperemos todas as operações no módulo legado e interromperemos a replicação de dados. Neste ponto, poderemos descomissionar o módulo migrado.

Analisamos amplamente o uso do padrão Strangler para migrar um aplicativo legado monolítico, mas ainda não terminamos de modernizar nossa nova arquitetura baseada em microsserviços. Em seguida, vamos considerar alguns dos desafios que surgem posteriormente no processo de modernização e como Debezium, Apache Kafka e Kubernetes podem ajudar.

Após a migração: desafios da modernização

A razão mais importante para considerar o uso do padrão Strangler para migração é o risco reduzido. Esse padrão dá valor de forma constante e nos permite demonstrar o progresso por meio de lançamentos frequentes. Mas a migração por si só, sem aprimoramentos ou novo “valor de negócios” pode ser difícil de vender para algumas partes interessadas. No processo de modernização de longo prazo, também queremos aprimorar nossos serviços existentes e adicionar novos. Com iniciativas de modernização, muitas vezes, também temos a tarefa de estabelecer a base e as melhores práticas para a construção de aplicativos modernos que se seguirão. Ao migrar cada vez mais serviços, adicionar novos e, em geral, fazer a transição para a arquitetura de microsserviços, novos desafios surgirão, incluindo o seguinte:

  • Automatização da implantação e operação de um grande número de serviços.
  • Executar gravações duplas e orquestrar processos de negócios de longa duração de maneira confiável e escalável.
  • Atender às necessidades analíticas e de relatórios.

Existem todos os desafios que podem não ter existido no mundo legado. Vamos explorar como podemos abordar alguns deles usando uma combinação de padrões de design e tecnologias.

Desafio 1: Operar serviços orientados a eventos em escala

Ao remover cada vez mais serviços do aplicativo monolítico legado e também criar novos serviços para satisfazer os requisitos de negócios emergentes, a necessidade de implantações automatizadas, reversões, posicionamentos, gerenciamento de configuração, atualizações e autorrecuperação torna-se aparente. Esses são os recursos exatos que tornam o Kubernetes uma ótima opção para operar microsserviços em grande escala. A Figura 9 ilustra.

No alt text provided for this image

Quando estivermos trabalhando com serviços orientados a eventos, descobriremos rapidamente que precisamos automatizar e integrar com uma infraestrutura orientada a eventos - que é onde o Apache Kafka e outros projetos em seu ecossistema podem entrar. Além disso, podemos usar os operadores Kubernetes para ajudar a automatizar o gerenciamento do Kafka e dos seguintes serviços de suporte:

  • O Apcurio Registry fornece um Operador para gerenciar o Apicurio Schema Registry no Kubernetes
  • Strimzi oferece operadores para gerenciar clusters Kafka e Kafka Connect de forma declarativa no Kubernetes. ( já falei dele aqui)
  • O KEDA (Kubernetes Event-Driven Autoscaling) oferece autoescaladores de carga de trabalho para aumentar e diminuir os serviços que consomem do Kafka. Portanto, se o atraso do consumidor ultrapassar um limite, o Operador iniciará mais consumidores até o número de partições para acompanhar a produção de mensagens.
  • O Knative Eventing oferece abstrações orientadas a eventos com suporte do Apache Kafka.

Observação: o Kubernetes não apenas fornece uma plataforma de destino para modernização de aplicativos, mas também permite que você expanda seus aplicativos sobre a mesma base em uma arquitetura orientada a eventos de grande escala. Ele faz isso por meio da automação de cargas de trabalho de usuários, cargas de trabalho Kafka e outras ferramentas do ecossistema Kafka. Dito isso, nem tudo precisa ser executado no seu Kubernetes. Por exemplo, você pode usar um Apache Kafka totalmente gerenciado ou um serviço de registro de esquema e vinculá-lo automaticamente ao seu aplicativo usando Kubernetes Operators. A criação de um cluster Kafka de várias zonas de disponibilidade (multi-AZ) em alguns provedores como AWS, Azure e GCP, para Apache Kafka leva pouquicimo tempo.

Agora, vamos ver como podemos enfrentar os dois desafios de modernização restantes usando padrões de design.

Desafio 2: evitar gravações duplas

Depois de criar alguns microsserviços, você percebe rapidamente que a parte mais difícil deles são os dados. Como parte de sua lógica de negócios, os microsserviços geralmente precisam atualizar seu armazenamento de dados local. Ao mesmo tempo, eles também precisam notificar outros serviços sobre as mudanças ocorridas. Esse desafio não é tão óbvio no mundo de aplicativos monolíticos e transações distribuídas herdadas. Como podemos evitar ou resolver essa situação da maneira nativa da nuvem? A resposta é modificar apenas um dos dois recursos — o banco de dados — e então conduzir a atualização do segundo, como o Apache Kafka, de maneira eventualmente consistente. A Figura 10 ilustra essa abordagem.

No alt text provided for this image

O uso do padrão Outbox com o Debezium permite que os serviços executem essas duas tarefas de maneira segura e consistente. Em vez de enviar uma mensagem diretamente ao Kafka ao atualizar o banco de dados, o serviço usa uma única transação para realizar a atualização normal e inserir a mensagem em uma tabela de caixa de saída específica em seu banco de dados. Uma vez que a transação tenha sido gravada no log de transações do banco de dados, o Debezium pode pegar a mensagem da caixa de saída e enviá-la para o Apache Kafka. Essa abordagem nos dá propriedades muito boas. Ao gravar de forma síncrona no banco de dados em uma única transação, o serviço se beneficia da semântica "ler suas próprias gravações", em que uma consulta subsequente ao serviço retornará o registro recém-persistente. Ao mesmo tempo, obtemos propagação confiável e assíncrona para outros serviços via Apache Kafka. O padrão Outbox é uma abordagem comprovada para evitar gravações duplas para microsserviços orientados a eventos escaláveis. Ele resolve o desafio de comunicação entre serviços de forma muito elegante, sem exigir que todos os participantes estejam disponíveis ao mesmo tempo, incluindo Kafka. Acredito que o Outbox se tornará um dos padrões fundamentais para projetar microsserviços escaláveis orientados a eventos.

Desafio 3: transações de longa duração

Embora o padrão Caixa de saída resolva o problema de comunicação entre serviços mais simples, ele não é suficiente sozinho para resolver o caso de uso de transações comerciais distribuídas e de longa duração mais complexas. O último requer a execução de várias operações em vários microsserviços e a aplicação de semântica consistente de tudo ou nada. Um exemplo comum para demonstrar esse requisito é o caso de uso de reserva de uma viagem que consiste em várias partes em que o voo e a acomodação devem ser reservados juntos. No mundo legado, ou com uma arquitetura monolítica, você pode não estar ciente desse problema, pois a coordenação entre os módulos é feita em um único processo e em um único contexto transacional. O mundo distribuído requer uma abordagem diferente, conforme ilustrado na Figura 11.

No alt text provided for this image

O padrão Saga oferece uma solução para esse problema dividindo uma transação comercial abrangente em uma série de várias transações de banco de dados local, que são executadas pelos serviços participantes. Geralmente, existem duas maneiras de implementar sagas distribuídas:

• Coreografia: Nesta abordagem, um serviço participante envia uma mensagem para o próximo após ter executado sua transação local.

• Orquestração: Nesta abordagem, um serviço de coordenação central coordena e invoca os serviços participantes.

A comunicação entre os serviços participantes pode ser síncrona, via HTTP ou gRPC, ou assíncrona, via mensagens como Apache Kafka.

O legal aqui é que você pode implementar sagas usando Debezium, Apache Kafka e o padrão Outbox. Com essas ferramentas, é possível aproveitar a abordagem de orquestração e ter um local para gerenciar o fluxo de uma saga e verificar o status da transação abrangente da saga. Também podemos combinar orquestração com comunicação assíncrona para dissociar o serviço de coordenação da disponibilidade dos serviços participantes e até mesmo da disponibilidade do Kafka. Isso nos dá o melhor dos dois mundos: orquestração e comunicação assíncrona, sem bloqueio, paralela com os serviços participantes, sem acoplamento temporal.

Combinar o padrão Outbox com o padrão Sagas é uma excelente opção de implementação orientada a eventos para o caso de uso de transações de negócios de longa duração no mundo dos serviços distribuídos. Consulte Saga Orchestration for Microservices Using the Outbox Pattern (InfoQ) para obter uma descrição detalhada. Veja também um exemplo de implementação desse padrão no GitHub.

Conclusão

O padrão Strangler, o padrão Outbox e o padrão Saga podem ajudá-lo a migrar de sistemas brownfield, mas, ao mesmo tempo, podem ajudá-lo a criar serviços greenfield, modernos e orientados a eventos que são à prova de futuro.

Kubernetes, Apache Kafka e Debezium são projetos de código aberto que se tornaram padrões de fato em seus respectivos campos. Você pode usá-los para criar soluções padronizadas com um rico ecossistema de ferramentas de suporte e práticas recomendadas.



André Del Cid

CEO na Anpla e Presidente da ABRATENE - Impactando o mercado de Nobreaks

8 m

Marcos, obrigado por compartilhar..! 🙂

Entre para ver ou adicionar um comentário

Outros artigos de Marcos Almeida, MSc

Outras pessoas também visualizaram

Conferir tópicos