O blog da AWS

Estendendo uma arquitetura Serverless e orientada por eventos às cargas de trabalho de containers existentes

Este post foi escrito por Dhiraj Mahapatro, Arquiteto de Soluções Principal Especialista, Sascha Moellering, Arquiteto de Soluções Principal Especialista,  Emily Shea, Líder da WW, Integration Services . 

Muitos serviços Serverless são ideais para arquiteturas orientadas a eventos (EDA), pois os eventos os invocam e só são executados quando há um evento a ser processado. Ao criar na nuvem, muitos serviços emitem eventos por padrão e têm recursos integrados para gerenciar eventos. Essa combinação permite que os clientes criem arquiteturas orientadas por eventos com mais facilidade e rapidez do que nunca.

O aplicativo de exemplo de processamento de sinistros de seguros nesta série de blogs usa princípios de arquitetura orientados por eventos e serviços Serverless, como AWS Lambda, AWS Step Functions, Amazon API Gateway, Amazon EventBridge e Amazon SQS.

Ao criar uma arquitetura orientada por eventos, é provável que você tenha serviços existentes para se integrar à nova arquitetura, de preferência sem precisar fazer alterações significativas de refatoração nesses serviços. À medida que os serviços se comunicam por meio de eventos, estender os aplicativos para microsserviços novos e existentes é um benefício fundamental de criar com o EDA. Você pode escrever esses microsserviços em diferentes linguagens de programação ou executados em diferentes opções de computação.

Este blog mostra um cenário de integração de um serviço existente em containers (um serviço de liquidação) ao software de processamento de sinistros de seguro Serverless e orientado por eventos descrito neste blog.

Visão geral do exemplo de arquitetura orientada a eventos

O aplicativo de exemplo usa um front-end para inscrever um novo usuário e permitir que o usuário faça upload de imagens do carro e da carteira de motorista. Depois de se inscreverem, eles podem registrar uma reclamação e fazer upload de imagens do carro danificado. Anteriormente, ele ainda não se integrava a um serviço de liquidação para concluir o processo de reivindicações e liquidação.

Nesse cenário, o serviço de liquidação é um aplicativo “abandonado” que executa o Spring Boot 3 no Amazon ECS com o AWS Fargate. O AWS Fargate é um mecanismo de computação Serverless e pay-as-you-go (pague pelo que você consome) que permite que você se concentre na criação de aplicativos de containers sem gerenciar servidores.

O aplicativo Spring Boot expõe um endpoint REST, que aceita uma solicitação POST. Ele aplica a lógica comercial de liquidação e cria um registro de liquidação no banco de dados para uma reclamação de seguro automóvel. Seu objetivo é fazer com que a liquidação funcione com o novo aplicativo EDA, projetado para processamento de solicitações sem reestruturação ou reescrita. Cliente, reclamações, fraude, documento e notificação são os outros domínios que são mostrados como caixas azuis no diagrama a seguir:

Reference architecture

Estrutura do projeto

O aplicativo usa o AWS Cloud Development Kit (CDK) para criar a pilha. Com o CDK, você tem a flexibilidade de criar construções modulares e reutilizáveis de forma imperativa usando a linguagem de sua escolha. O aplicativo de exemplo usa TypeScript para CDK.

A estrutura de projeto a seguir permite que você crie diferentes contextos limitados. A arquitetura orientada por eventos depende da coreografia de eventos entre domínios. O conceito de programação orientada a objetos (OOP) do CDK ajuda a provisionar a infraestrutura para separar as preocupações do domínio e, ao mesmo tempo, acoplá-las livremente por meio de eventos.

Você divide as construções de CDK de nível superior nos seguintes domínios correspondentes:
Comparing domains

O código do aplicativo e da infraestrutura está presente em cada domínio. Essa estrutura de projeto cria uma maneira perfeita de adicionar novos domínios, como liquidação com seu código de aplicativo e infraestrutura, sem afetar outras áreas da empresa.

Com a estrutura anterior, você pode usar a construção CDK settement-service.ts dentro de claims-processing-stack.ts:

const settlementService = new SettlementService(this, "SettlementService", {
  bus,
});
JavaScript

A única informação que a construção SettlementService precisa para funcionar é o recurso de barramento de eventos personalizado do EventBridge, criado em claims-processing-stack.ts.

Para executar o aplicativo de exemplo, siga as etapas de configuração no arquivo README do aplicativo de exemplo.

Carga de trabalho de contêiner existente

O domínio de liquidação fornece um serviço REST para o resto da organização. Um aplicativo Spring Boot em containers Docker é executado no Amazon ECS com o AWS Fargate. O diagrama de sequência a seguir mostra o fluxo síncrono de solicitação-resposta de um cliente REST externo para o serviço:

 

 

 

Settlement service

  1. O cliente REST externo faz uma chamada POST /settlement por meio de uma API HTTP presente na frente de um Application Load Balancer (ALB) interno.
  2. O SettlementController.java delega ao SettlementService.java.
  3. SettlementService aplica a lógica de negócios e chama SettlementRepository para persistência de dados.
  4. SettlementRepository mantém o item na tabela Settlement DynamoDB.

Uma solicitação para o endpoint da API HTTP se parece com:

curl --location <settlement-api-endpoint-from-cloudformation-output> \
--header 'Content-Type: application/json' \
--data '{ "customerId": "06987bc1-1234-1234-1234-2637edab1e57", "claimId": "60ccfe05-1234-1234-1234-a4c1ee6fcc29", "color": "green", "damage": "bumper_dent" }'
Bash

The response from the API call is:

API response

Você pode aprender mais aqui sobre a otimização de aplicativos Spring Boot no AWS Fargate.

Estendendo a carga de trabalho de containers para eventos

Para integrar o serviço de liquidação, você deve atualizar o serviço para receber e emitir eventos de forma assíncrona. A lógica central do serviço de liquidação permanece a mesma. Quando você registra uma reclamação, carrega imagens de carros danificados e o aplicativo não detecta nenhuma fraude em documentos, o domínio do acordo se inscreve no evento Fraud.Not.Detected e aplica sua lógica comercial. O serviço de liquidação emite um evento de volta ao aplicar a lógica de negócios.

O diagrama de sequência a seguir mostra uma nova interface na liquidação para trabalhar com o EDA. O serviço de liquidação assina eventos que um produtor emite. Aqui, o produtor do evento é o serviço de fraude que coloca um evento em um ônibus de eventos personalizado da EventBridge.

Sequence diagram

  1. O produtor emite o evento Fraud.Not.Detected para o barramento de eventos personalizado do EventBridge.
  2. O EventBridge avalia as regras fornecidas pelo domínio de liquidação e envia a carga útil do evento para a fila SQS de destino.
  3. O SubscriberService.java pesquisa novas mensagens na fila SQS.
  4. Na mensagem, ele transforma o corpo da mensagem em um objeto de entrada que é aceito pelo SettlementService.
  5. Em seguida, delega a chamada ao SettlementService, semelhante à forma como o SettlementController funciona na implementação do REST.
  6. SettlementService aplica a lógica de negócios. O fluxo é como o caso de uso do REST de 7 a 10.
  7. Ao receber a resposta do SettlementService, o SubscriberService transforma a resposta para publicar um evento de volta no barramento de eventos com o tipo de evento Settlement.Finalized.

O resto da arquitetura consome esse evento Settlement.Finalized.

Usando o registro e a descoberta do esquema do EventBridge

O esquema impõe um contrato entre um produtor e um consumidor. O consumidor espera a estrutura exata da carga útil do evento toda vez que um evento chega. O EventBridge fornece registro e descoberta de esquemas para manter esse contrato. O consumidor (o serviço de liquidação) pode baixar as vinculações de código e usá-las no código-fonte.

Ative a descoberta de esquemas no EventBridge antes de baixar as vinculações de código e usá-las em seu repositório. As associações de código fornecem um organizador que desorganiza o evento de entrada da fila SQS para um objeto Java simples e antigo (POJO) FraudNotDetected.java. Você baixa as vinculações de código usando a opção do seu IDE. O AWS Toolkit for IntelliJ facilita o download e o uso deles.

Download code bindings

A arquitetura final do serviço de liquidação com REST e arquitetura orientada a eventos se parece com:

Final architecture

Transição para se tornar totalmente orientada por eventos

Com a nova capacidade de lidar com eventos, o aplicativo Spring Boot agora suporta o endpoint REST e a arquitetura orientada a eventos, executando a mesma lógica de negócios por meio de interfaces diferentes. Neste cenário de exemplo, à medida que a arquitetura orientada a eventos amadurece e o resto da organização a adota, a necessidade do endpoint POST para salvar um acordo pode diminuir. No futuro, você poderá descontinuar o endpoint e confiar totalmente nas mensagens de sondagem da fila do SQS.

Você começa usando um padrão CDK ECS do serviço ALB e Fargate:

const loadBalancedFargateService = new ecs_patterns.ApplicationLoadBalancedFargateService(
  this,
  "settlement-service",
  {
    cluster: cluster,
    taskImageOptions: {
      image: ecs.ContainerImage.fromDockerImageAsset(asset),
      environment: {
        "DYNAMODB_TABLE_NAME": this.table.tableName
      },
      containerPort: 8080,
      logDriver: new ecs.AwsLogDriver({
        streamPrefix: "settlement-service",
        mode: ecs.AwsLogDriverMode.NON_BLOCKING,
        logRetention: RetentionDays.FIVE_DAYS,
      })
    },
    memoryLimitMiB: 2048,
    cpu: 1024,
    publicLoadBalancer: true,
    desiredCount: 2,
    listenerPort: 8080
  });
JavaScript

Para se adaptar ao EDA, você atualiza os recursos para adaptar a fila do SQS para receber mensagens e o EventBridge para colocar eventos. Adicione novas variáveis de ambiente ao recurso ApplicationLoadBalancerFargateService:

environment: {
  "SQS_ENDPOINT_URL": queue.queueUrl,
  "EVENTBUS_NAME": props.bus.eventBusName,
  "DYNAMODB_TABLE_NAME": this.table.tableName
}
JSON

Conceda à tarefa Fargate permissão para colocar eventos no barramento de eventos personalizado e consumir mensagens da fila SQS:

props.bus.grantPutEventsTo(loadBalancedFargateService.taskDefinition.taskRole);
queue.grantConsumeMessages(loadBalancedFargateService.taskDefinition.taskRole);
JavaScript

Ao fazer a transição do serviço de liquidação para se tornar totalmente orientado por eventos, você não precisa mais do endpoint da API HTTP e do ALB, pois o SQS é a fonte dos eventos.

Uma alternativa melhor é usar o padrão ECS QueueProcessingFargateService para o serviço Fargate. O padrão fornece escalonamento automático com base no número de mensagens visíveis na fila do SQS, além da utilização da CPU. No exemplo a seguir, você também pode adicionar duas estratégias de provedor de capacidade ao configurar o serviço Fargate: FARGATE_SPOT e FARGATE. Isso significa que, para cada tarefa executada usando o FARGATE, há duas tarefas que usam o FARGATE_SPOT. Isso pode ajudar a otimizar os custos.

const queueProcessingFargateService = new ecs_patterns.QueueProcessingFargateService(this, 'Service', {
  cluster,
  memoryLimitMiB: 1024,
  cpu: 512,
  queue: queue,
  image: ecs.ContainerImage.fromDockerImageAsset(asset),
  desiredTaskCount: 2,
  minScalingCapacity: 1,
  maxScalingCapacity: 5,
  maxHealthyPercent: 200,
  minHealthyPercent: 66,
  environment: {
    "SQS_ENDPOINT_URL": queueUrl,
    "EVENTBUS_NAME": props?.bus.eventBusName,
    "DYNAMODB_TABLE_NAME": tableName
  },
  capacityProviderStrategies: [
    {
      capacityProvider: 'FARGATE_SPOT',
      weight: 2,
    },
    {
      capacityProvider: 'FARGATE',
      weight: 1,
    },
  ],
});
JavaScript

Esse padrão abstrai o comportamento de escalonamento automático do serviço Fargate com base na profundidade da fila.

Executando o aplicativo

Para testar o aplicativo, siga Como usar o aplicativo após a configuração inicial. Depois de concluído, você vê que o navegador recebe um evento Settlement.Finalized:

{
  "version": "0",
  "id": "e2a9c866-cb5b-728c-ce18-3b17477fa5ff",
  "detail-type": "Settlement.Finalized",
  "source": "settlement.service",
  "account": "123456789",
  "time": "2023-04-09T23:20:44Z",
  "region": "us-east-2",
  "resources": [],
  "detail": {
    "settlementId": "377d788b-9922-402a-a56c-c8460e34e36d",
    "customerId": "67cac76c-40b1-4d63-a8b5-ad20f6e2e6b9",
    "claimId": "b1192ba0-de7e-450f-ac13-991613c48041",
    "settlementMessage": "Based on our analysis on the damage of your car per claim id b1192ba0-de7e-450f-ac13-991613c48041, your out-of-pocket expense will be $100.00."
  }
}
JSON

Limpando

A pilha cria uma VPC personalizada e outros recursos relacionados. Certifique-se de limpar os recursos após o uso para evitar o custo contínuo da execução desses serviços. Para limpar a infraestrutura, siga as etapas de limpeza mostradas no aplicativo de exemplo.

Conclusão

O blog explica uma forma de integrar a carga de trabalho de containers existente em execução no AWS Fargate com uma nova arquitetura orientada a eventos. Você usa o EventBridge para dissociar serviços diferentes que são criados usando diferentes tecnologias de computação, linguagens e estruturas. Usando o AWS CDK, você obtém a modularidade de criar serviços separados uns dos outros.

Este blog mostra uma arquitetura evolutiva que permite modernizar as cargas de trabalho de containers existentes com mudanças mínimas que ainda oferecem os benefícios adicionais de criar com EDA e Serverless na AWS.

A principal diferença entre a abordagem orientada a eventos e a abordagem REST é que você desbloqueia o produtor quando ele emite um evento. O produtor de eventos do domínio de liquidação que assina esse evento está fracamente acoplado. A funcionalidade de negócio permanece intacta e nenhum esforço significativo de refatoração ou rearquitetura é necessário. Com esses ganhos de agilidade, você pode chegar ao mercado mais rapidamente

O aplicativo de exemplo mostra os detalhes da implementação e as etapas para configurar, executar e limpar o aplicativo. O aplicativo usa o ECS Fargate para um serviço de domínio, mas você não o limita apenas ao Fargate. Você também pode trazer aplicativos baseados em containers executados no Amazon EKS de forma semelhante à arquitetura orientada a eventos.

Saiba mais sobre a arquitetura orientada a eventos no Serverless Land.

 

Este artigo foi traduzido do Blog da AWS em Inglês.

 


Sobre o autor

Dhiraj Mahapatro é Arquiteto de Soluções Principal Especialista

 

 

 

 

Sascha Moellering, Arquiteto de Soluções Principal Especialista

 

 

 

 

Emily Shea, Líder da WW, Integration Services

 

 

 

 

Tradutor

Daniel Abib é Enterprise Solution Architect na AWS, com mais de 25 anos trabalhando com gerenciamento de projetos, arquiteturas de soluções escaláveis, desenvolvimento de sistemas e CI/CD, microsserviços, arquitetura Serverless & Containers e segurança. Ele trabalha apoiando clientes corporativos, ajudando-os em sua jornada para a nuvem.

https://www.linkedin.com/in/danielabib/