O blog da AWS

Implementando padrões arquiteturais com Amazon EventBridge Pipes

Esta publicação foi escrita por Dominik Richter (Solutions Architect).

Padrões arquiteturais ajudam você a resolver desafios recorrentes no design de software. Eles são blueprints que foram usados e testados muitas vezes. Quando você projeta aplicações distribuídas, padrões de integração empresarial (EIP) ajudam você a integrar componentes distribuídos. Por exemplo, eles descrevem como integrar serviços de terceiros em suas aplicações existentes. Mas os padrões são agnósticos em relação à tecnologia. Eles não fornecem nenhuma orientação sobre como implementá-los.

Esta publicação mostra como usar Amazon EventBridge Pipes para implementar quatro padrões comuns de integração empresarial (EIP) na AWS. Isso ajuda você a simplificar suas arquiteturas. Pipes é um recurso do Amazon EventBridge para conectar seus recursos AWS. Usar Pipes pode reduzir a complexidade de suas integrações. Também pode reduzir a quantidade de código que você precisa escrever e manter.

Padrão de filtro de conteúdo e filtro de mensagem

Para remover mensagens e conteúdo indesejados, combine filtro de conteúdo e filtro de mensagem. O padrão de filtro de mensagem descarta mensagens inteiras. O padrão de filtro de conteúdo remove conteúdo indesejado de uma mensagem individual antes de encaminhá-la para um sistema downstream. Casos de uso para esses padrões incluem reduzir custos de armazenamento removendo dados desnecessários ou remover informações de identificação pessoal (PII) para fins de conformidade.

No exemplo a seguir, o objetivo é reter apenas dados não-PII de eventos “ORDER”. Para conseguir isso, você deve remover todos os eventos que não são eventos “ORDER”. Além disso, você deve remover qualquer campo nos eventos “ORDER” que contenha PII.

Embora você possa usar esses padrões com várias origens e destinos, a arquitetura a seguir mostra esse padrão com Amazon Kinesis. A filtragem do EventBridge Pipes descarta eventos indesejados. Transformadores de entrada do EventBridge Pipes removem dados PII de eventos que são encaminhados para o segundo stream com retenção mais longa.

Em vez de usar Pipes, você poderia conectar os streams usando uma função AWS Lambda. Isso requer que você escreva e mantenha código para ler e escrever no Kinesis. No entanto, Pipes pode ser mais econômico do que usar uma função Lambda.

Algumas situações requerem uma função de enriquecimento. Por exemplo, se seu objetivo é mascarar um atributo sem removê-lo completamente. Por exemplo, você poderia substituir o atributo “birthday” por um atributo “age_group”.

Neste caso, se você usar Pipes para integração, a função Lambda contém apenas sua lógica de negócios. Por outro lado, se você usar Lambda tanto para integração quanto para lógica de negócios, você não paga pelo Pipes. Ao mesmo tempo, você adiciona complexidade à sua função Lambda, que agora contém código de integração. Isso pode aumentar seu tempo de execução e custo. Portanto, suas prioridades determinam a melhor opção e você deve comparar ambas as abordagens para tomar uma decisão.

Para implementar Pipes usando o AWS Cloud Development Kit (AWS CDK), use o seguinte código-fonte. O código-fonte completo para todos os padrões descritos nesta publicação do blog pode ser encontrado no repositório GitHub de exemplos da AWS.

const filterPipe = new pipes.CfnPipe(this, 'FilterPipe', {
  roleArn: pipeRole.roleArn,
  source: sourceStream.streamArn,
  target: targetStream.streamArn,
  sourceParameters: { filterCriteria: { filters: [{ pattern: '{"data" : {"event_type" : ["ORDER"] }}' }] }, kinesisStreamParameters: { startingPosition: 'LATEST' } },
  targetParameters: { inputTemplate: '{"event_type": <$.data.event_type>, "currency": <$.data.currency>, "sum": <$.data.sum>}', kinesisStreamParameters: { partitionKey: 'event_type' } },
});

Para permitir acesso à origem e ao destino, você deve atribuir as permissões corretas:

const pipeRole = new iam.Role(this, 'FilterPipeRole', { assumedBy: new iam.ServicePrincipal('pipes.amazonaws.com') });

sourceStream.grantRead(pipeRole);
targetStream.grantWrite(pipeRole);

Padrão de tradutor de mensagem

Em uma arquitetura orientada a eventos, produtores e consumidores de eventos são independentes uns dos outros. Portanto, eles podem trocar eventos de diferentes formatos. Para habilitar a comunicação, os eventos devem ser traduzidos. Isso é conhecido como o padrão tradutor de mensagem. Por exemplo, um evento pode conter um endereço, mas o consumidor espera coordenadas.

Se um cálculo for necessário para traduzir mensagens, use a etapa de enriquecimento. O diagrama de arquitetura a seguir mostra como realizar esse enriquecimento via destinos de API. No exemplo, você pode chamar um serviço de geocodificação existente para resolver endereços em coordenadas.

Pode haver casos em que a tradução é puramente sintática. Por exemplo, um campo pode ter um nome ou estrutura diferente.

Você pode realizar essas traduções sem enriquecimento usando transformadores de entrada.

Aqui está o código-fonte para o pipe, incluindo a role com as permissões corretas:

const pipeRole = new iam.Role(this, 'MessageTranslatorRole', { assumedBy: new iam.ServicePrincipal('pipes.amazonaws.com'), inlinePolicies: { invokeApiDestinationPolicy } });

sourceQueue.grantConsumeMessages(pipeRole);
targetStepFunctionsWorkflow.grantStartExecution(pipeRole);

const messageTranslatorPipe = new pipes.CfnPipe(this, 'MessageTranslatorPipe', {
  roleArn: pipeRole.roleArn,
  source: sourceQueue.queueArn,
  target: targetStepFunctionsWorkflow.stateMachineArn,
  enrichment: enrichmentDestination.apiDestinationArn,
  sourceParameters: { sqsQueueParameters: { batchSize: 1 } },
});

Padrão normalizador

O padrão normalizador é similar ao tradutor de mensagem, mas existem diferentes componentes de origem com diferentes formatos para eventos. O padrão normalizador roteia cada tipo de evento através de seu tradutor de mensagem específico para que os sistemas downstream processem mensagens com uma estrutura consistente.

O exemplo mostra um sistema onde diferentes sistemas de origem armazenam a propriedade name de forma diferente. Para processar as mensagens de forma diferente com base em sua origem, use um workflow AWS Step Functions. Você pode separar por tipo de evento e então ter caminhos individuais executando o processo de unificação. Este diagrama visualiza que você pode chamar uma função Lambda se necessário. No entanto, em casos básicos como o exemplo “name” anterior, você pode modificar os eventos usando Amazon States Language (ASL).

No exemplo, você unifica os eventos usando Step Functions antes de colocá-los em seu barramento de eventos. Como frequentemente acontece com escolhas arquiteturais, existem alternativas. Outra abordagem é introduzir filas separadas para cada sistema de origem, conectadas por seu próprio pipe contendo apenas suas ações de unificação.

Este é o código-fonte para o padrão normalizador usando um workflow Step Functions como enriquecimento:

const pipeRole = new iam.Role(this, 'NormalizerRole', { assumedBy: new iam.ServicePrincipal('pipes.amazonaws.com') });

sourceQueue.grantConsumeMessages(pipeRole);
enrichmentWorkflow.grantStartSyncExecution(pipeRole);
normalizerTargetBus.grantPutEventsTo(pipeRole);

const normalizerPipe = new pipes.CfnPipe(this, 'NormalizerPipe', {
  roleArn: pipeRole.roleArn,
  source: sourceQueue.queueArn,
  target: normalizerTargetBus.eventBusArn,
  enrichment: enrichmentWorkflow.stateMachineArn,
  sourceParameters: { sqsQueueParameters: { batchSize: 1 } },
});

Padrão claim check

Para reduzir o tamanho dos eventos em sua aplicação orientada a eventos, você pode remover temporariamente atributos. Esta abordagem é conhecida como o padrão claim check. Você divide uma mensagem em uma referência (“claim check”) e o payload associado. Em seguida, você armazena o payload em armazenamento externo e adiciona apenas o claim check aos eventos. Quando você processa eventos, você recupera partes relevantes do payload usando o claim check. Por exemplo, você pode recuperar o nome e aniversário de um usuário com base em seu userID.

O padrão claim check tem duas partes. Primeiro, quando um evento é recebido, você o divide e armazena o payload em outro lugar. Segundo, quando o evento é processado, você recupera a informação relevante. Você pode implementar ambos os aspectos com um pipe.

No primeiro pipe, você usa o enriquecimento para dividir o evento, no segundo para recuperar o payload. Abaixo estão várias opções de enriquecimento, como usar uma API externa via API Destinations, ou usar Amazon DynamoDB via Lambda. Outras opções de enriquecimento são Amazon API Gateway e Step Functions.

Usar um pipe para dividir e recuperar mensagens tem três vantagens. Primeiro, você mantém os eventos concisos à medida que eles se movem pelo sistema. Segundo, você garante que o evento contenha todas as informações relevantes quando for processado. Terceiro, você encapsula a complexidade de dividir e recuperar dentro do pipe.

O código a seguir implementa um pipe para o padrão claim check usando o CDK:

const pipeRole = new iam.Role(this, 'ClaimCheckRole', { assumedBy: new iam.ServicePrincipal('pipes.amazonaws.com') });

claimCheckLambda.grantInvoke(pipeRole);
sourceQueue.grantConsumeMessages(pipeRole);
targetWorkflow.grantStartExecution(pipeRole);

const claimCheckPipe = new pipes.CfnPipe(this, 'ClaimCheckPipe', {
  roleArn: pipeRole.roleArn,
  source: sourceQueue.queueArn,
  target: targetWorkflow.stateMachineArn,
  enrichment: claimCheckLambda.functionArn,
  sourceParameters: { sqsQueueParameters: { batchSize: 1 } },
  targetParameters: { stepFunctionStateMachineParameters: { invocationType: 'FIRE_AND_FORGET' } },
});

Conclusão

Esta publicação do blog mostra como você pode implementar quatro padrões de integração empresarial com Amazon EventBridge Pipes. Em muitos casos, isso reduz a quantidade de código que você precisa escrever e manter. Também pode simplificar suas arquiteturas e, em alguns cenários, reduzir custos.

Você pode encontrar o código-fonte para todos os padrões no repositório GitHub de exemplos da AWS.

Para mais recursos de aprendizado sobre Serverless, visite Serverless Land. Para encontrar mais padrões, vá diretamente para a Coleção de Padrões Serverless.


Este conteúdo foi traduzido do post original do blog, que pode ser encontrado aqui.

Tradutores

Nicolas Tarzia é Senior Technical Account Manager na AWS, com mais de 13 anos de experiência, com ampla experiência em arquitetura cloud, engenharia e design de software. Sua área de interesse são tecnologias serverless.
https://www.linkedin.com/in/nicolastarzia
Daniel Abib é Arquiteto de Soluções Sênior e Especialista em Amazon Bedrock 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 especialização em Machine Learning. Ele trabalha apoiando Startups, ajudando-os em sua jornada para a nuvem.
https://www.linkedin.com/in/danielabib/