<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>O Sertão será Cloud</title><description>Blog sobre cloud computing em português — O Sertão será Cloud.</description><link>https://sertaoseracloud.com/</link><language>pt-BR</language><item><title>Hello, Sertão!</title><link>https://sertaoseracloud.com/posts/hello-sertao/</link><guid isPermaLink="true">https://sertaoseracloud.com/posts/hello-sertao/</guid><description>Bem-vindo ao O Sertão será Cloud — um blog sobre cloud computing em português.</description><pubDate>Fri, 24 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Do Sertão para a nuvem. Este é o primeiro post do blog — um marcador de que a plataforma está funcionando.&lt;/p&gt;
&lt;h2&gt;O que vem por aí&lt;/h2&gt;
&lt;p&gt;Tutoriais, análises e opinião sobre cloud computing, tudo em PT-BR.
Pipeline de sync dev.to → tradução automática → PR editorial → publicação chega na Fase 2.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Feito com ☁ — sertaoseracloud&lt;/em&gt;&lt;/p&gt;
</content:encoded></item><item><title>Event-Driven Architecture on Azure vs AWS: Service Bus vs SNS/SQS</title><link>https://sertaoseracloud.com/posts/event-driven-architecture-on-azure-vs-aws-service-bus-vs-snssqs-3cml/</link><guid isPermaLink="true">https://sertaoseracloud.com/posts/event-driven-architecture-on-azure-vs-aws-service-bus-vs-snssqs-3cml/</guid><description>Your OrderService does six things when a customer clicks Place Order: writes to orders table, reserves inventory, charges card, queues shipping, emails receipt, logs analytics.</description><pubDate>Tue, 21 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Seu &lt;code&gt;OrderService&lt;/code&gt; faz seis coisas quando o cliente clica em &lt;em&gt;Place Order&lt;/em&gt;. Ele escreve na tabela de pedidos, reserva estoque, cobra o cartão, enfileira a etiqueta de envio, envia o recibo por e‑mail e registra o evento de análise. Tudo isso ocorre dentro de um único handler HTTP, em uma transação, em um único servidor. Quando o gateway de pagamento hesita, o pedido falha. Quando o provedor de e‑mail limita a taxa, o pedido falha. Quando o estoque está lento, o pedido falha. O monólito amarrou seis domínios de falha independentes a um destino compartilhado.&lt;/p&gt;
&lt;p&gt;Este artigo reconstrói esse pipeline como um sistema orientado a eventos tanto no Azure quanto na AWS, e detalha onde eles diferem genuinamente — não onde se parecem superficialmente. O cenário de referência é o processamento de pedidos de e‑commerce com fan‑out para Inventário, Pagamento e Notificação. Público‑alvo: engenheiros intermediários a sêniores que já leram as páginas de vendas dos fornecedores e querem as partes que essas páginas omitem.&lt;/p&gt;
&lt;h2&gt;O padrão antes dos produtos&lt;/h2&gt;
&lt;p&gt;Antes de nomear qualquer serviço de nuvem, defina o formato. O que você quer é &lt;strong&gt;publicar/assinar com fan‑out durável, com filas por consumidor e filas de dead‑letter por consumidor&lt;/strong&gt;. O produtor emite um evento lógico — &lt;code&gt;OrderPlaced&lt;/code&gt; — para um tópico. O tópico entrega uma cópia para uma fila durável por consumidor. Cada consumidor esvazia sua própria fila no seu ritmo, tenta novamente em sua própria agenda e, quando desiste, a mensagem cai em &lt;em&gt;sua própria&lt;/em&gt; DLQ — não em uma compartilhada.&lt;/p&gt;
&lt;p&gt;Essa última parte importa. Uma DLQ compartilhada significa que uma mensagem envenenada de inventário bloqueia a equipe de pagamento de ver seu próprio veneno. Uma fila por consumidor, uma DLQ por consumidor, um orçamento de retry por consumidor. O raio de impacto de qualquer mensagem ruim é exatamente um contexto limitado.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4u58flh3h0l5nf1oxzz6.png&quot; alt=&quot;Fluxograma&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Com o formato definido, podemos mapeá‑lo para duas nuvens — uma preocupação de cada vez.&lt;/p&gt;
&lt;h2&gt;Lado a lado, uma preocupação de cada vez&lt;/h2&gt;
&lt;h3&gt;Modelo de tópico e assinatura&lt;/h3&gt;
&lt;p&gt;Na &lt;strong&gt;AWS&lt;/strong&gt; o tópico e as filas são primitivas separadas. SNS publica; SQS armazena. Você os conecta com uma assinatura de tópico e uma política de fila. A fila mantém suas mensagens; o tópico apenas as espalha. Dois tipos de recurso por consumidor.&lt;/p&gt;
&lt;p&gt;No &lt;strong&gt;Azure&lt;/strong&gt; o Service Bus colapsa isso em um único grafo de recursos. Um &lt;strong&gt;namespace&lt;/strong&gt; contém um &lt;strong&gt;tópico&lt;/strong&gt;, e cada consumidor é uma &lt;strong&gt;assinatura&lt;/strong&gt; nesse tópico. A assinatura tem uma fila virtual atrás dela; você não gerencia um recurso de fila separado. Menos partes móveis na camada de IaC, mas menor separação de preocupações — o tópico e seus assinantes compartilham um ciclo de vida e uma unidade de faturamento.&lt;/p&gt;
&lt;h3&gt;Semântica de fila e DLQ&lt;/h3&gt;
&lt;p&gt;Ambos os brokers são &lt;strong&gt;at‑least‑once&lt;/strong&gt;. Consumidores verão duplicatas. Nenhum slide de marketing muda isso.&lt;/p&gt;
&lt;p&gt;A AWS emparelha cada fila SQS com uma DLQ explícita via política de redrive. &lt;code&gt;maxReceiveCount&lt;/code&gt; é o limiar; a fila principal mantém a mensagem em voo até que o consumidor a exclua explicitamente, controlada por &lt;code&gt;visibility_timeout_seconds&lt;/code&gt;. Esse timeout de visibilidade deve exceder a latência P99 do handler, com margem. Defina‑o muito baixo e o broker redistribui enquanto o primeiro handler ainda está trabalhando — você obtém dois handlers concorrentes correndo, e a idempotência torna‑se essencial de uma forma que você provavelmente não testou.&lt;/p&gt;
&lt;p&gt;O Azure Service Bus incorpora a DLQ em cada assinatura como &lt;code&gt;$DeadLetterQueue&lt;/code&gt;. &lt;code&gt;maxDeliveryCount&lt;/code&gt; desempenha o mesmo papel que &lt;code&gt;maxReceiveCount&lt;/code&gt;. O Service Bus também envia para dead‑letter em duas classes de falha que o SQS não conhece: &lt;strong&gt;expiração de TTL da mensagem&lt;/strong&gt; e &lt;strong&gt;exceções de avaliação de filtro&lt;/strong&gt;. Esses dois gatilhos extra de DLQ são vitórias operacionais reais — mensagens expiradas ou malformadas não desaparecem nas métricas.&lt;/p&gt;
&lt;p&gt;Uma afirmação comum que vale a pena reconsiderar: &lt;em&gt;Service Bus oferece entrega exactly‑once&lt;/em&gt;. Não oferece. O que ele oferece é &lt;strong&gt;detecção de duplicatas dentro de uma janela limitada&lt;/strong&gt; (até sete dias) com base em &lt;code&gt;MessageId&lt;/code&gt; por mensagem. Isso é uma ajuda do lado do broker, não uma garantia semântica. Consumidores ainda devem ser idempotentes. Mesma história no SQS FIFO e sua janela de dedup baseada em conteúdo de cinco minutos.&lt;/p&gt;
&lt;h3&gt;Identidade e auth do plano de dados&lt;/h3&gt;
&lt;p&gt;Na AWS, consumidores assumem uma função IAM do Lambda, ECS ou EKS. Sem chaves de acesso, sem credenciais de usuário na configuração. A política de fila restringe remetentes a um ARN de tópico específico via uma condição &lt;code&gt;aws:SourceArn&lt;/code&gt; — sem isso, qualquer tópico SNS na sua conta pode escrever na sua fila. Essa é a clássica armadilha de confused‑deputy, e deixar a condição desligada é um dos gatilhos de rejeição mais comuns em uma revisão real.&lt;/p&gt;
&lt;p&gt;No Azure, o equivalente a &quot;sem chaves de longa duração&quot; é &lt;code&gt;disableLocalAuth: true&lt;/code&gt; no namespace, o que elimina totalmente a autenticação SAS. Toda a autenticação passa pelo AAD e Identidade Gerenciada. A função correta é &lt;strong&gt;Service Bus Data Receiver&lt;/strong&gt; com escopo &lt;strong&gt;por assinatura&lt;/strong&gt;, não em todo o namespace. Escritores obtêm &lt;strong&gt;Service Bus Data Sender&lt;/strong&gt; no tópico. Escopar no nível de assinatura significa que um consumidor de notificação comprometido não pode ler eventos de pagamento — o movimento lateral é limitado pelo escopo RBAC.&lt;/p&gt;
&lt;h3&gt;Ordenação&lt;/h3&gt;
&lt;p&gt;Ambas as plataformas podem fazer ordenação. Nenhuma deve fazer ordenação por padrão.&lt;/p&gt;
&lt;p&gt;Na AWS, ordenação significa filas FIFO chaveadas por &lt;code&gt;MessageGroupId&lt;/code&gt;. O limite é 300 mensagens/seg (3.000 com batching) por fila. Esse é um teto rígido, não um limitador suave.&lt;/p&gt;
&lt;p&gt;No Azure, ordenação significa &lt;code&gt;SessionId&lt;/code&gt; nas mensagens e &lt;code&gt;requiresSession: true&lt;/code&gt; na assinatura. A ordem é preservada por sessão. O throughput é bom no Standard; o particionamento no Premium empurra‑o mais alto. O custo é que o pinamento de sessão serializa uma assinatura — um consumidor lento em uma sessão paralisa mensagens nessa sessão até que o bloqueio seja liberado.&lt;/p&gt;
&lt;p&gt;Se o domínio não exige ordem estrita, não a habilite. FIFO é uma decisão de negócios, não um padrão arquitetural.&lt;/p&gt;
&lt;h2&gt;A matriz de trade‑offs&lt;/h2&gt;
&lt;p&gt;Este é o centro do artigo, não o fechamento. Se você lembrar de uma coisa, lembre‑se desta tabela.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimensão&lt;/th&gt;
&lt;th&gt;AWS SNS + SQS&lt;/th&gt;
&lt;th&gt;Azure Service Bus (Standard / Premium)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Modelo primitivo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tópico (SNS) espalha para filas SQS separadas — dois tipos de recurso por consumidor&lt;/td&gt;
&lt;td&gt;Único namespace → tópico → assinatura — um único grafo de recursos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Modelo de custo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pagamento por requisição tanto em publicações SNS quanto em requisições SQS; sem custo ocioso&lt;/td&gt;
&lt;td&gt;Standard: pagamento por milhão de operações + hora de namespace. Premium: unidades de mensagens fixas (piso previsível ≈ $670/MU/mês no momento da escrita; verifique o preço atual de SKU, ele deriva)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ordenação de mensagens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Standard: nenhuma. FIFO: ordenação estrita por &lt;code&gt;MessageGroupId&lt;/code&gt;, limitada a 300 msgs/s (3.000 com batching)&lt;/td&gt;
&lt;td&gt;Standard: ordenação dentro de uma sessão (&lt;code&gt;SessionId&lt;/code&gt;). Premium: mesmo, maior throughput, suporte a particionamento&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Semântica de entrega&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;At‑least‑once. FIFO adiciona dedup baseada em conteúdo em uma janela de 5 minutos&lt;/td&gt;
&lt;td&gt;At‑least‑once. PeekLock + detecção de duplicatas até 7 dias. Ajuda do lado do broker; ainda exige consumidores idempotentes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tamanho máximo de mensagem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;256 KB tanto no SNS quanto no SQS. Solução alternativa: claim‑check via S3 + SQS Extended Client&lt;/td&gt;
&lt;td&gt;Standard: 256 KB. Premium: 100 MB nativo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tratamento de DLQ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fila SQS explícita + política de redrive. Limiar &lt;code&gt;maxReceiveCount&lt;/code&gt;. DLQ apenas para falhas de entrega&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$DeadLetterQueue&lt;/code&gt; implícita por assinatura. Limiar &lt;code&gt;maxDeliveryCount&lt;/code&gt;. Também DLQs em expiração de TTL e erros de avaliação de filtro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Filtragem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Políticas de filtro SNS: correspondência de atributos JSON no momento da assinatura&lt;/td&gt;
&lt;td&gt;Filtro SQL e filtro de correlação por assinatura — modelo de expressão mais rico&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Superfície de ops&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CloudWatch: &lt;code&gt;ApproximateNumberOfMessages&lt;/code&gt;, &lt;code&gt;ApproximateAgeOfOldestMessage&lt;/code&gt;. Alarme em profundidade de DLQ &amp;gt; 0&lt;/td&gt;
&lt;td&gt;Azure Monitor: &lt;code&gt;ActiveMessages&lt;/code&gt;, &lt;code&gt;DeadletteredMessages&lt;/code&gt;. Alarme em &lt;code&gt;DeadletteredMessages &amp;gt; 0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Identidade&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Funções IAM assumidas por Lambda/ECS/EKS; SSE‑KMS&lt;/td&gt;
&lt;td&gt;AAD + Identidade Gerenciada; &lt;code&gt;disableLocalAuth=true&lt;/code&gt; elimina SAS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Isolamento de rede&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;VPC Endpoints (Interface para SNS, Interface/Gateway para SQS)&lt;/td&gt;
&lt;td&gt;Private Endpoint (SKU Premium para integração VNet completa)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Throughput&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SNS: milhões de msgs/s. SQS standard: efetivamente ilimitado. SQS FIFO: 300–3.000 msgs/s por fila&lt;/td&gt;
&lt;td&gt;Standard: ~2.000 msgs/s por namespace como diretriz de trabalho. Premium: escala com unidades de mensagens (~1.000 msgs/s por MU)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Heurística de uma linha:&lt;/strong&gt; Se a carga de trabalho exige mensagens &amp;gt; 256 KB, ordenação forte com alto throughput, ou isolamento VNet com ordenação, o Service Bus Premium justifica seu custo. Caso contrário — fan‑out massivo, consumidores idempotentes, faturamento por requisição — SNS + SQS vence. &lt;strong&gt;O Service Bus Standard é a linha de base; o Premium é um upgrade que você justifica, não um padrão.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;O IaC — AWS&lt;/h2&gt;
&lt;p&gt;Terraform de nível de produção. Tags de FinOps, SSE, políticas de redrive e uma condição &lt;code&gt;aws:SourceArn&lt;/code&gt; na política de fila. Mantenha tudo isso — cada linha carrega uma propriedade de segurança ou confiabilidade da qual o cluster depende.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;terraform {
  required_version = &quot;&amp;gt;= 1.6.0&quot;
  required_providers {
    aws = {
      source  = &quot;hashicorp/aws&quot;
      version = &quot;~&amp;gt; 5.40&quot;
    }
  }
}

variable &quot;project&quot;        { type = string }
variable &quot;environment&quot;    { type = string }               # dev | stg | prd
variable &quot;aws_region&quot;     { type = string  default = &quot;us-east-1&quot; }
variable &quot;consumers&quot; {
  description = &quot;Logical consumers that subscribe to the OrderPlaced topic.&quot;
  type        = set(string)
  default     = [&quot;inventory&quot;, &quot;payment&quot;, &quot;notification&quot;]
}
variable &quot;max_receive_count&quot; { type = number default = 5 } # redrive threshold

locals {
  name_prefix = &quot;${var.project}-${var.environment}&quot;
  tags = {
    Project     = var.project
    Environment = var.environment
    Workload    = &quot;eda-orders&quot;
    CostCenter  = &quot;platform-events&quot;
    ManagedBy   = &quot;terraform&quot;
  }
}

# --- Topic ----------------------------------------------------------------
resource &quot;aws_sns_topic&quot; &quot;orders_placed&quot; {
  name              = &quot;${local.name_prefix}-orders-placed&quot;
  kms_master_key_id = &quot;alias/aws/sns&quot;          # SSE at rest with AWS-managed CMK; swap to customer CMK for stricter tenants
  tags              = local.tags
}

# --- Queues + DLQs per consumer ------------------------------------------
resource &quot;aws_sqs_queue&quot; &quot;dlq&quot; {
  for_each                  = var.consumers
  name                      = &quot;${local.name_prefix}-${each.key}-dlq&quot;
  message_retention_seconds = 1209600            # 14 days - max allowed, buys ops time
  kms_master_key_id         = &quot;alias/aws/sqs&quot;
  tags = merge(local.tags, { Role = &quot;dlq&quot;, Consumer = each.key })
}

resource &quot;aws_sqs_queue&quot; &quot;main&quot; {
  for_each                  = var.consumers
  name                      = &quot;${local.name_prefix}-${each.key}&quot;
  visibility_timeout_seconds = 60                 # must exceed consumer max processing time
  message_retention_seconds = 345600             # 4 days
  receive_wait_time_seconds = 20                 # long polling - reduces empty-receive cost
  kms_master_key_id         = &quot;alias/aws/sqs&quot;
  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.dlq[each.key].arn
    maxReceiveCount     = var.max_receive_count
  })
  tags = merge(local.tags, { Role = &quot;main&quot;, Consumer = each.key })
}

# --- Allow SNS to write to SQS -------------------------------------------
data &quot;aws_iam_policy_document&quot; &quot;sns_to_sqs&quot; {
  for_each = var.consumers
  statement {
    sid     = &quot;AllowSNSDeliver&quot;
    effect  = &quot;Allow&quot;
    actions = [&quot;sqs:SendMessage&quot;]
    principals {
      type        = &quot;Service&quot;
      identifiers = [&quot;sns.amazonaws.com&quot;]
    }
    resources = [aws_sqs_queue.main[each.key].arn]
    condition {
      test     = &quot;ArnEquals&quot;
      variable = &quot;aws:SourceArn&quot;
      values   = [aws_sns_topic.orders_placed.arn]
    }
  }
}

resource &quot;aws_sqs_queue_policy&quot; &quot;main&quot; {
  for_each  = var.consumers
  queue_url = aws_sqs_queue.main[each.key].id
  policy    = data.aws_iam_policy_document.sns_to_sqs[each.key].json
}

resource &quot;aws_sns_topic_subscription&quot; &quot;consumer&quot; {
  for_each             = var.consumers
  topic_arn            = aws_sns_topic.orders_placed.arn
  protocol             = &quot;sqs&quot;
  endpoint             = aws_sqs_queue.main[each.key].arn
  raw_message_delivery = true                     # consumers parse the raw event, not the SNS envelope
}

# --- Consumer IAM role template (least-privilege) ------------------------
data &quot;aws_iam_policy_document&quot; &quot;consumer_assume&quot; {
  statement {
    actions = [&quot;sts:AssumeRole&quot;]
    principals {
      type        = &quot;Service&quot;
      identifiers = [&quot;lambda.amazonaws.com&quot;, &quot;ecs-tasks.amazonaws.com&quot;]
    }
  }
}

resource &quot;aws_iam_role&quot; &quot;consumer&quot; {
  for_each           = var.consumers
  name               = &quot;${local.name_prefix}-${each.key}-consumer&quot;
  assume_role_policy = data.aws_iam_policy_document.consumer_assume.json
  tags               = merge(local.tags, { Consumer = each.key })
}

data &quot;aws_iam_policy_document&quot; &quot;consumer_sqs&quot; {
  for_each = var.consumers
  statement {
    actions = [
      &quot;sqs:ReceiveMessage&quot;,
      &quot;sqs:DeleteMessage&quot;,
      &quot;sqs:GetQueueAttributes&quot;,
      &quot;sqs:ChangeMessageVisibility&quot;,
    ]
    resources = [aws_sqs_queue.main[each.key].arn]
  }
}

resource &quot;aws_iam_role_policy&quot; &quot;consumer_sqs&quot; {
  for_each = var.consumers
  role     = aws_iam_role.consumer[each.key].id
  policy   = data.aws_iam_policy_document.consumer_sqs[each.key].json
}

output &quot;topic_arn&quot; { value = aws_sns_topic.orders_placed.arn }
output &quot;queues&quot;    { value = { for k, q in aws_sqs_queue.main : k =&amp;gt; q.arn } }
output &quot;dlqs&quot;      { value = { for k, q in aws_sqs_queue.dlq  : k =&amp;gt; q.arn } }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;O IaC — Azure&lt;/h2&gt;
&lt;p&gt;Mesmo cenário, mesmas tags, mesmo &lt;code&gt;maxDeliveryCount = 5&lt;/code&gt;. Note &lt;code&gt;disableLocalAuth: true&lt;/code&gt; no namespace e RBAC por assinatura no final.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Deployment scope: resourceGroup
targetScope = &apos;resourceGroup&apos;

@description(&apos;Project short code, e.g. osecloud&apos;)
param project string

@allowed([ &apos;dev&apos;, &apos;stg&apos;, &apos;prd&apos; ])
param environment string

@description(&apos;Azure region.&apos;)
param location string = resourceGroup().location

@description(&apos;Logical consumers that subscribe to the OrderPlaced topic.&apos;)
param consumers array = [ &apos;inventory&apos;, &apos;payment&apos;, &apos;notification&apos; ]

@description(&apos;Consumer identity object IDs (managed identities that will receive Data Receiver role). Key must match a name in `consumers`.&apos;)
param consumerPrincipalIds object = {}

@description(&apos;Service Bus SKU. Use Premium when you need ordering at scale, VNet integration, or &amp;gt;1MB messages.&apos;)
@allowed([ &apos;Standard&apos;, &apos;Premium&apos; ])
param skuName string = &apos;Standard&apos;

var namePrefix = toLower(&apos;${project}-${environment}&apos;)
var tags = {
  project:     project
  environment: environment
  workload:    &apos;eda-orders&apos;
  costCenter:  &apos;platform-events&apos;
  managedBy:   &apos;bicep&apos;
}

// --- Namespace ------------------------------------------------------------
resource sbNamespace &apos;Microsoft.ServiceBus/namespaces@2022-10-01-preview&apos; = {
  name:     &apos;${namePrefix}-sb&apos;
  location: location
  sku: {
    name: skuName
    tier: skuName
  }
  properties: {
    minimumTlsVersion: &apos;1.2&apos;
    publicNetworkAccess: &apos;Enabled&apos; // set to &apos;Disabled&apos; + private endpoint in prd
    disableLocalAuth: true         // force AAD/Managed Identity - no SAS keys
  }
  tags: tags
}

// --- Topic ----------------------------------------------------------------
resource topic &apos;Microsoft.ServiceBus/namespaces/topics@2022-10-01-preview&apos; = {
  parent: sbNamespace
  name: &apos;orders-placed&apos;
  properties: {
    defaultMessageTimeToLive: &apos;P14D&apos;
    enableBatchedOperations: true
    supportOrdering: true          // preserves order within a session (partition) - only honoured with session-enabled subscriptions
  }
}

// --- Subscriptions + DLQ (DLQ is implicit per subscription) --------------
resource subs &apos;Microsoft.ServiceBus/namespaces/topics/subscriptions@2022-10-01-preview&apos; = [for name in consumers: {
  parent: topic
  name:   &apos;${name}-sub&apos;
  properties: {
    deadLetteringOnMessageExpiration:    true
    deadLetteringOnFilterEvaluationExceptions: true
    maxDeliveryCount: 5             // redrive threshold → moves to $DeadLetterQueue
    lockDuration:     &apos;PT1M&apos;        // matches visibility-timeout in AWS terms
    defaultMessageTimeToLive: &apos;P4D&apos;
    requiresSession: false          // set true for FIFO-per-session guarantees
  }
}]

// --- RBAC: Azure Service Bus Data Receiver on each subscription ----------
var dataReceiverRoleId = &apos;4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0&apos; // Service Bus Data Receiver

resource rbacReceiver &apos;Microsoft.Authorization/roleAssignments@2022-04-01&apos; = [for (name, i) in consumers: if (contains(consumerPrincipalIds, name)) {
  name:  guid(subs[i].id, consumerPrincipalIds[name], dataReceiverRoleId)
  scope: subs[i]
  properties: {
    roleDefinitionId: subscriptionResourceId(&apos;Microsoft.Authorization/roleDefinitions&apos;, dataReceiverRoleId)
    principalId:      consumerPrincipalIds[name]
    principalType:    &apos;ServicePrincipal&apos;
  }
}]

output namespaceId       string = sbNamespace.id
output topicId           string = topic.id
output subscriptionNames array  = [for (name, i) in consumers: subs[i].name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Um aviso sobre a versão da API: &lt;code&gt;2022-10-01-preview&lt;/code&gt; ainda é rotulada como preview no momento da escrita. Se sua equipe de plataforma proibe versões de API em preview em produção, fixe na última versão GA estável e re‑teste &lt;code&gt;disableLocalAuth&lt;/code&gt; — seu comportamento mudou entre versões da API.&lt;/p&gt;
&lt;h2&gt;Sete restrições arquiteturais&lt;/h2&gt;
&lt;p&gt;Trate estas como critérios de aceitação para qualquer pipeline EDA que você entregar em qualquer nuvem. Elas são uma lista de verificação, não uma lista de desejos.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;At‑least‑once é o padrão em ambos os lados.&lt;/strong&gt; Exactly‑once é uma propriedade do consumidor — handlers idempotentes mais um armazenamento de dedup — não uma garantia do broker. Janelas do lado do broker (SQS FIFO 5 min, detecção de duplicatas do Service Bus até 7 dias) estreitam o problema; elas não o eliminam.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timeout de visibilidade e duração de bloqueio devem exceder a latência P99 do handler.&lt;/strong&gt; Se o broker redistribuir enquanto o primeiro handler ainda está trabalhando, você processa em duplicidade. Meça P99 sob carga, adicione margem e alerta quando a duração do handler se aproximar do timeout.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ordenação é uma aposta que você paga.&lt;/strong&gt; FIFO limita o throughput da AWS a 300–3.000 msgs/s; sessões serializam assinaturas do Azure. Habilite‑a apenas quando o domínio exigir ordenação — nunca como um cobertor de segurança.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DLQs não são um cemitério.&lt;/strong&gt; Elas precisam de alertas (&lt;code&gt;profundidade de DLQ &amp;gt; 0&lt;/code&gt; pagina o plantonista) e um procedimento de replay documentado — redrive do SQS ou recebimento e reenvio do &lt;code&gt;$DeadLetterQueue&lt;/code&gt; do Service Bus. Uma DLQ sem um runbook de replay é um vazamento silencioso.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mensagens grandes são um antipadrão.&lt;/strong&gt; &amp;gt; 256 KB na AWS implica claim‑check via S3. O Service Bus Premium suporta 100 MB, mas custo de transporte e pressão de memória do consumidor ainda argumentam a favor de claim‑check nesse tamanho.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Marque cada recurso&lt;/strong&gt; com &lt;code&gt;project&lt;/code&gt;, &lt;code&gt;environment&lt;/code&gt;, &lt;code&gt;workload&lt;/code&gt;, &lt;code&gt;costCenter&lt;/code&gt;, &lt;code&gt;managedBy&lt;/code&gt;. Sem essas tags, FinOps não pode atribuir gastos e a equipe de plataforma não pode impor políticas de ciclo de vida. Os snippets acima carregam o conjunto completo; não os remova.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sem chaves SAS, sem chaves de usuário IAM.&lt;/strong&gt; Identidade Gerenciada no Azure, funções IAM na AWS. &lt;code&gt;disableLocalAuth: true&lt;/code&gt; no namespace do Service Bus, condição &lt;code&gt;aws:SourceArn&lt;/code&gt; em cada política de fila SQS. Qualquer outra coisa é uma credencial de longa duração esperando para vazar.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A escolha entre SNS/SQS e Service Bus raramente é binária. Comece no Service Bus Standard ou SNS + SQS. Mova para Premium ou FIFO apenas quando uma restrição acima — tamanho, ordenação, isolamento — forçar você para lá.&lt;/p&gt;
</content:encoded></item><item><title>Practical Guide: Building a Cell-Based Architecture on Azure with Terraform</title><link>https://sertaoseracloud.com/posts/practical-guide-building-a-cell-based-architecture-on-azure-with-terraform-and-python-1h04/</link><guid isPermaLink="true">https://sertaoseracloud.com/posts/practical-guide-building-a-cell-based-architecture-on-azure-with-terraform-and-python-1h04/</guid><description>As cloud applications scale globally, a single centralized stack introduces critical vulnerabilities. Cell-Based Architecture mitigates this by partitioning into isolated, self-contained units.</description><pubDate>Tue, 21 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;1. Introdução&lt;/h2&gt;
&lt;p&gt;À medida que as aplicações em nuvem escalam para atender públicos globais, depender de uma única pilha de infraestrutura centralizada introduz vulnerabilidades críticas. Uma falha localizada ou um vizinho ruidoso pode desencadear uma interrupção sistêmica. A Arquitetura Baseada em Células mitiga isso ao particionar o sistema em unidades isoladas, idênticas e autocontidas chamadas &quot;células.&quot; Ao rotear inquilinos ou usuários específicos para células dedicadas, você restringe o raio de explosão de qualquer degradação estritamente àquela célula, preservando a disponibilidade do restante do sistema.&lt;/p&gt;
&lt;p&gt;Ao final deste tutorial, você será capaz de projetar e provisionar uma arquitetura celular no Microsoft Azure. Utilizaremos o Azure Front Door como ponto de entrada global, o Azure Functions (Python) para roteamento dinâmico de tráfego e processamento, e o Azure Cosmos DB para gerenciamento de estado isolado. Dominar esse padrão no Azure não apenas reforça suas cargas de trabalho atuais, mas também consolida princípios fundamentais de multi-nuvem, pois os conceitos de roteamento desacoplado e isolamento de estado são diretamente transferíveis entre diferentes provedores de nuvem em um cenário corporativo unificado.&lt;/p&gt;
&lt;h2&gt;2. Pré-requisitos&lt;/h2&gt;
&lt;p&gt;Para executar as configurações e o código deste tutorial, verifique se você possui as seguintes ferramentas e níveis de acesso:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uma assinatura ativa do Microsoft Azure com permissões de Proprietário ou Colaborador para criar Grupos de Recursos, contas do Cosmos DB, Azure Functions e Azure Front Door.&lt;/li&gt;
&lt;li&gt;Terraform CLI (versão 1.0 ou superior) instalado localmente para provisionamento de Infraestrutura como Código (IaC).&lt;/li&gt;
&lt;li&gt;Python (versão 3.9 ou superior) instalado localmente, junto com o Azure Functions Core Tools para desenvolvimento e empacotamento local.&lt;/li&gt;
&lt;li&gt;Azure CLI instalado e autenticado (&lt;code&gt;az login&lt;/code&gt;) em seu ambiente local.&lt;/li&gt;
&lt;li&gt;Compreensão fundamental de conceitos de particionamento e sintaxe HCL do Terraform.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. Passo a Passo&lt;/h2&gt;
&lt;p&gt;Antes de mergulhar no código de infraestrutura, vamos visualizar o fluxo de execução. O diagrama de sequência abaixo detalha como uma solicitação global é interceptada, avaliada e encaminhada com segurança para uma pilha celular estritamente isolada no Azure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b41axbpax2r7t18yxjvt.png&quot; alt=&quot;Diagrama de Sequência&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.1 Definindo o Blueprint da Célula (Módulo Terraform)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Crie um módulo Terraform reutilizável que represente uma única &quot;Célula&quot; isolada. Isso inclui uma Conta de Armazenamento dedicada, um Plano de Serviço de Aplicativo, uma Azure Function (Worker) e um banco de dados Cosmos DB.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por que fazer isso:&lt;/strong&gt; A regra principal da arquitetura celular é a consistência absoluta entre ambientes. Ao encapsular a infraestrutura em um módulo Terraform, você garante que cada célula gerada seja uma réplica exata, evitando desvio de configuração e simplificando a escala horizontal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;
Crie um diretório chamado &lt;code&gt;modules/cell&lt;/code&gt; e adicione um arquivo &lt;code&gt;main.tf&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# modules/cell/main.tf
variable &quot;location&quot; { type = string }
variable &quot;resource_group_name&quot; { type = string }
variable &quot;cell_id&quot; { type = string }

resource &quot;azurerm_cosmosdb_account&quot; &quot;cell_db&quot; {
  name                = &quot;cosmos-cell-${var.cell_id}&quot;
  location            = var.location
  resource_group_name = var.resource_group_name
  offer_type          = &quot;Standard&quot;
  kind                = &quot;GlobalDocumentDB&quot;

  consistency_policy {
    consistency_level = &quot;Session&quot;
  }

  geo_location {
    location          = var.location
    failover_priority = 0
  }
}

resource &quot;azurerm_cosmosdb_sql_database&quot; &quot;cell_sqldb&quot; {
  name                = &quot;app-state&quot;
  resource_group_name = var.resource_group_name
  account_name        = azurerm_cosmosdb_account.cell_db.name
}

resource &quot;azurerm_storage_account&quot; &quot;cell_storage&quot; {
  name                     = &quot;stcell${var.cell_id}&quot;
  resource_group_name      = var.resource_group_name
  location                 = var.location
  account_tier             = &quot;Standard&quot;
  account_replication_type = &quot;LRS&quot;
}

resource &quot;azurerm_service_plan&quot; &quot;cell_plan&quot; {
  name                = &quot;plan-cell-${var.cell_id}&quot;
  location            = var.location
  resource_group_name = var.resource_group_name
  os_type             = &quot;Linux&quot;
  sku_name            = &quot;Y1&quot; # Consumption plan
}

resource &quot;azurerm_linux_function_app&quot; &quot;cell_worker&quot; {
  name                       = &quot;func-worker-${var.cell_id}&quot;
  location                   = var.location
  resource_group_name        = var.resource_group_name
  service_plan_id            = azurerm_service_plan.cell_plan.id
  storage_account_name       = azurerm_storage_account.cell_storage.name
  storage_account_access_key = azurerm_storage_account.cell_storage.primary_access_key

  site_config {
    application_stack {
      python_version = &quot;3.11&quot;
    }
  }

  app_settings = {
    &quot;CELL_ID&quot;            = var.cell_id
    &quot;COSMOS_DB_ENDPOINT&quot; = azurerm_cosmosdb_account.cell_db.endpoint
  }
}

output &quot;function_default_hostname&quot; {
  value = azurerm_linux_function_app.cell_worker.default_hostname
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 Criando Múltiplas Células&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Na configuração raiz do Terraform, defina o grupo de recursos base e itere sobre uma coleção de identificadores para implantar múltiplas células simultaneamente.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por que fazer isso:&lt;/strong&gt; Isso traduz o módulo teórico em ambientes físicos isolados. O uso de um loop &lt;code&gt;for_each&lt;/code&gt; permite escalar sem esforço de duas para cinquenta células apenas atualizando uma variável local, abstraindo a complexidade de gerenciar infraestruturas paralelas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;
No seu diretório raiz, crie o arquivo &lt;code&gt;main.tf&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# main.tf
terraform {
  required_providers {
    azurerm = {
      source  = &quot;hashicorp/azurerm&quot;
      version = &quot;~&amp;gt; 3.0&quot;
    }
  }
}

provider &quot;azurerm&quot; {
  features {}
}

resource &quot;azurerm_resource_group&quot; &quot;rg&quot; {
  name     = &quot;rg-cellular-architecture&quot;
  location = &quot;East US&quot;
}

locals {
  cells = [&quot;alpha&quot;, &quot;beta&quot;]
}

module &quot;isolated_cells&quot; {
  source   = &quot;./modules/cell&quot;
  for_each = toset(local.cells)

  cell_id             = each.key
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 Desenvolvendo o Roteador de Célula em Python&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Escreva o código da Função do Azure em Python que atuará como roteador de entrada global. Ele deve inspecionar solicitações HTTP recebidas, determinar o mapeamento apropriado de locatários e encaminhar a solicitação para a célula correta.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por que fazer isso:&lt;/strong&gt; O roteador abstrai a topologia interna do cliente. Aplicativos interagem com um único endpoint de API, sem saber que sua solicitação está sendo roteada para &lt;code&gt;cell-alpha&lt;/code&gt; ou &lt;code&gt;cell-beta&lt;/code&gt;. Esse mapeamento dinâmico permite realizar migrações ao vivo de locatários entre células para balancear carga sem tempo de inatividade.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;
Crie o código Python para sua função de roteamento (&lt;code&gt;__init__.py&lt;/code&gt; dentro da pasta da Função do Azure).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import logging
import json
import os
import urllib.request
import azure.functions as func

# In production, fetch this from Cosmos DB (Tenant Registry)
CELL_ENDPOINTS = {
    &quot;alpha&quot;: os.environ.get(&quot;CELL_ALPHA_URL&quot;, &quot;https://func-worker-alpha.azurewebsites.net&quot;),
    &quot;beta&quot;: os.environ.get(&quot;CELL_BETA_URL&quot;, &quot;https://func-worker-beta.azurewebsites.net&quot;)
}

def get_target_cell(tenant_id: str) -&amp;gt; str:
    # Mocking a Cosmos DB registry lookup
    # A real implementation would query the global metadata database
    if tenant_id.startswith(&quot;A&quot;):
        return &quot;alpha&quot;
    return &quot;beta&quot;

def main(req: func.HttpRequest) -&amp;gt; func.HttpResponse:
    logging.info(&apos;Global Router processing a request.&apos;)

    try:
        req_body = req.get_json()
        tenant_id = req_body.get(&apos;tenant_id&apos;)
        
        if not tenant_id:
            return func.HttpResponse(
                &quot;Missing partition key: tenant_id&quot;, 
                status_code=400
            )

        target_cell = get_target_cell(tenant_id)
        target_url = f&quot;{CELL_ENDPOINTS[target_cell]}/api/process&quot;
        
        # Proxy the request to the isolated cell
        data = json.dumps(req_body).encode(&apos;utf-8&apos;)
        proxy_req = urllib.request.Request(
            target_url, 
            data=data,
            headers={&apos;Content-Type&apos;: &apos;application/json&apos;}
        )
        
        with urllib.request.urlopen(proxy_req) as response:
            cell_response = response.read().decode(&apos;utf-8&apos;)

        return func.HttpResponse(
            json.dumps({
                &quot;status&quot;: &quot;success&quot;,
                &quot;x_routed_to&quot;: target_cell,
                &quot;data&quot;: json.loads(cell_response)
            }),
            mimetype=&quot;application/json&quot;,
            status_code=200
        )

    except Exception as e:
        logging.error(f&quot;Routing error: {str(e)}&quot;)
        return func.HttpResponse(&quot;Internal Server Error&quot;, status_code=500)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 Provisionando a Camada de Roteamento Global&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Implante a Função do Azure de roteamento central e configure um perfil do Azure Front Door para ficar na frente dela.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por que fazer isso:&lt;/strong&gt; O Azure Front Door atua como um ponto de entrada global seguro e distribuído. Ele absorve ataques DDoS na borda, fornece recursos de WAF e garante que a Função de Roteador Global seja protegida contra exposição direta e não autenticada à internet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;
Adicione a infraestrutura de roteamento ao seu &lt;code&gt;main.tf&lt;/code&gt; raiz.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Shared storage for the Global Router
resource &quot;azurerm_storage_account&quot; &quot;router_storage&quot; {
  name                     = &quot;stglobalrouter&quot;
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = azurerm_resource_group.rg.location
  account_tier             = &quot;Standard&quot;
  account_replication_type = &quot;LRS&quot;
}

resource &quot;azurerm_service_plan&quot; &quot;router_plan&quot; {
  name                = &quot;plan-global-router&quot;
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  os_type             = &quot;Linux&quot;
  sku_name            = &quot;Y1&quot;
}

resource &quot;azurerm_linux_function_app&quot; &quot;global_router&quot; {
  name                       = &quot;func-global-router-gateway&quot;
  location                   = azurerm_resource_group.rg.location
  resource_group_name        = azurerm_resource_group.rg.name
  service_plan_id            = azurerm_service_plan.router_plan.id
  storage_account_name       = azurerm_storage_account.router_storage.name
  storage_account_access_key = azurerm_storage_account.router_storage.primary_access_key

  site_config {
    application_stack {
      python_version = &quot;3.11&quot;
    }
  }

  app_settings = {
    &quot;CELL_ALPHA_URL&quot; = &quot;https://${module.isolated_cells[\&quot;alpha\&quot;].function_default_hostname}&quot;
    &quot;CELL_BETA_URL&quot;  = &quot;https://${module.isolated_cells[\&quot;beta\&quot;].function_default_hostname}&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute &lt;code&gt;terraform init&lt;/code&gt;, &lt;code&gt;terraform plan&lt;/code&gt; e &lt;code&gt;terraform apply&lt;/code&gt; para implantar toda a arquitetura multi-célula.&lt;/p&gt;
&lt;h2&gt;4. Solução de Problemas Comuns&lt;/h2&gt;
&lt;p&gt;A transição para uma arquitetura celular requer uma mudança na forma como você gerencia estado e tráfego. Aqui estão problemas comuns que você pode encontrar:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Limitação de Cosmos DB (HTTP 429) em uma Célula Específica:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Problema:&lt;/strong&gt; Uma célula começa a rejeitar solicitações, enquanto outras operam normalmente. Isso geralmente indica um &quot;vizinho ruidoso&quot; – um locatário cuja carga de trabalho subiu repentinamente, esgotando as Unidades de Solicitação (RUs) provisionadas para o banco de dados dessa célula específica.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Solução:&lt;/strong&gt; Verifique as métricas no Azure Monitor. Se um locatário tiver ultrapassado o tamanho da célula compartilhada, você deve executar uma migração ao vivo. Atualize o Registro Global de Locatários (Banco de Dados de Metadados) para apontar aquele &lt;code&gt;tenant_id&lt;/code&gt; específico para uma célula dedicada recém-provisionada, redirecionando seu tráfego de forma transparente.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sobrecarga de Latência na Camada de Roteamento:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Problema:&lt;/strong&gt; As solicitações demoram significativamente mais porque precisam passar pelo Front Door, pela Função de Roteador e, finalmente, pela Função de Célula.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Solução:&lt;/strong&gt; A lógica do roteador deve ser otimizada agressivamente. Implemente cache no nível do Roteador Global usando o Cache do Azure para Redis para que a função não precise consultar o Cosmos DB para o mapeamento de locatários em cada solicitação.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Inicializações a Frio em Funções do Azure:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Problema:&lt;/strong&gt; A primeira solicitação para uma célula específica leva vários segundos para ser executada.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Solução:&lt;/strong&gt; Como usamos o Plano de Consumo (&lt;code&gt;Y1&lt;/code&gt;) para eficiência de custo neste tutorial, as inicializações a frio são esperadas. Para cargas de trabalho de produção, mude o Plano de Serviço de Aplicativo para Premium (&lt;code&gt;EP1&lt;/code&gt; ou superior) para manter as instâncias pré-aquecidas, garantindo respostas consistentes com baixa latência.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;5. Conclusão&lt;/h2&gt;
&lt;p&gt;Construir uma Arquitetura Baseada em Células no Azure transforma vulnerabilidades monolíticas em unidades gerenciáveis e contidas. Ao utilizar o Terraform, estabelecemos uma linha de base reprodutível para ambientes isolados e implementamos uma camada de roteamento Python para encaminhar tráfego dinamicamente.&lt;/p&gt;
&lt;p&gt;Esse desacoplamento garante que picos massivos de tráfego ou implantações com problemas sejam restritos a domínios únicos. Ao amadurecer essa arquitetura, foque em automatizar o processo de &quot;Migração de Locatários&quot; — mover dados ativos entre instâncias do Cosmos DB sem tempo de inatividade — e padronizar seus módulos do Terraform para garantir que esse padrão possa ser adotado rapidamente em vários ambientes de nuvem.&lt;/p&gt;
</content:encoded></item><item><title>Practical Guide: Building a Cell-Based Architecture on AWS with Terraform</title><link>https://sertaoseracloud.com/posts/practical-guide-building-a-cell-based-architecture-on-aws-with-terraform-and-python-n1p/</link><guid isPermaLink="true">https://sertaoseracloud.com/posts/practical-guide-building-a-cell-based-architecture-on-aws-with-terraform-and-python-n1p/</guid><description>In cloud-native systems, failures in centralized components risk global outages. Cell-Based Architecture isolates failures by dividing systems into identical, standalone cells.</description><pubDate>Mon, 20 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;1. Introdução&lt;/h2&gt;
&lt;p&gt;Na era cloud-native, sistemas muitas vezes chegam a um ponto onde escalar uma arquitetura única introduz riscos inaceitáveis. Uma falha em um componente central pode resultar em um desastre global, afetando todos os usuários simultaneamente. A Arquitetura Baseada em Células resolve isso dividindo o sistema em múltiplas instâncias isoladas, autônomas e idênticas chamadas &quot;células&quot;. Ao colocar usuários (ou locatários) em células específicas, você reduz drasticamente o impacto de falhas.&lt;/p&gt;
&lt;p&gt;Embora este tutorial se concentre em uma implementação AWS, projetar estratégias celulares é um pilar da engenharia multinuvem robusta. Os princípios de isolar estado e rotear tráfego com base em chaves de partição se aplicam perfeitamente a outros provedores, como o Microsoft Azure, garantindo alta disponibilidade independentemente da nuvem subjacente.&lt;/p&gt;
&lt;p&gt;No final deste tutorial, você entenderá como prover uma infraestrutura celular na AWS usando Terraform. Criaremos um blueprint para uma &quot;célula&quot;, estamparemos múltiplas instâncias idênticas e construiremos uma camada de roteador em Python para direcionar tráfego ao ambiente isolado correto.&lt;/p&gt;
&lt;h2&gt;2. Pré-requisitos&lt;/h2&gt;
&lt;p&gt;Para implementar este padrão arquitetônico com sucesso, você precisará de:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uma conta ativa da Amazon Web Services (AWS) com privilégios administrativos.&lt;/li&gt;
&lt;li&gt;Terraform instalado localmente (versão 1.0 ou superior) para Infraestrutura como Código.&lt;/li&gt;
&lt;li&gt;Python (versão 3.9 ou superior) para escrever a lógica de roteamento.&lt;/li&gt;
&lt;li&gt;Credenciais AWS configuradas em seu ambiente (&lt;code&gt;aws configure&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Compreensão fundamental de arquitetura de software e lógica de particionamento.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. Passo a Passo&lt;/h2&gt;
&lt;p&gt;Uma Arquitetura Baseada em Células introduz uma &quot;Camada de Roteamento Fino&quot; na frente de sua infraestrutura principal. O diagrama abaixo ilustra como uma requisição de entrada é avaliada e encaminhada para uma pilha celular estritamente isolada.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkaftk1syfa2tem9oys3.png&quot; alt=&quot;Diagrama de Sequência&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.1 Definindo o Blueprint da Célula (Módulo Terraform)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Crie um módulo Terraform reutilizável que defina exatamente o que é uma única &quot;Célula&quot;.
&lt;strong&gt;Por que fazer isso:&lt;/strong&gt; O princípio central da arquitetura celular é que toda célula é idêntica. Ao usar um módulo Terraform, você garante que qualquer atualização na infraestrutura seja aplicada uniformemente em todas as células isoladas, evitando desvio de configuração (&lt;em&gt;configuration drift&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;
Crie uma pasta chamada &lt;code&gt;modules/cell&lt;/code&gt; e adicione um arquivo &lt;code&gt;main.tf&lt;/code&gt; dentro dela. Este blueprint contém um API Gateway, uma função Lambda e uma tabela DynamoDB isolada.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# modules/cell/main.tf
variable &quot;cell_id&quot; {
  type        = string
  description = &quot;Identificador único para a célula (ex: cell-1)&quot;
}

resource &quot;aws_dynamodb_table&quot; &quot;cell_state&quot; {
  name           = &quot;app-state-${var.cell_id}&quot;
  billing_mode   = &quot;PAY_PER_REQUEST&quot;
  hash_key       = &quot;id&quot;

  attribute {
    name = &quot;id&quot;
    type = &quot;S&quot;
  }
}

resource &quot;aws_lambda_function&quot; &quot;cell_compute&quot; {
  filename      = &quot;cell_worker.zip&quot;
  function_name = &quot;worker-${var.cell_id}&quot;
  role          = aws_iam_role.cell_role.arn
  handler       = &quot;worker.handler&quot;
  runtime       = &quot;python3.11&quot;

  environment {
    variables = {
      CELL_ID    = var.cell_id
      TABLE_NAME = aws_dynamodb_table.cell_state.name
    }
  }
}

// (Configurações adicionais de IAM Gateway e API Gateway seguem aqui)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 Criando Múltiplas Células&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; No seu &lt;code&gt;main.tf&lt;/code&gt; raiz, itere sobre uma lista de identificadores de célula para provisionar múltiplos ambientes isolados e idênticos.
&lt;strong&gt;Por que fazer isso:&lt;/strong&gt; Isso permite que você escale horizontalmente, adicionando novas pilhas de infraestrutura completamente independentes, em vez de aumentar o tamanho de um monólito de banco de dados ou cluster de computação.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;
No seu diretório raiz, crie um &lt;code&gt;main.tf&lt;/code&gt; para invocar o módulo.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# main.tf
provider &quot;aws&quot; {
  region = &quot;us-east-1&quot;
}

module &quot;cell_alpha&quot; {
  source   = &quot;./modules/cell&quot;
  cell_id  = &quot;alpha&quot;
}

module &quot;cell_beta&quot; {
  source   = &quot;./modules/cell&quot;
  cell_id  = &quot;beta&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 Desenvolvendo a Camada de Roteamento em Python&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Escreva a Função Lambda do AWS que atuará como roteador de entrada global. Ela deve inspecionar as requisições HTTP de entrada, determinar o mapeamento apropriado de locatários e encaminhar a requisição para a célula correta.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por que fazer isso:&lt;/strong&gt; O roteador abstrai a topologia interna do cliente. Aplicações interagem com um único endpoint de API, sem saber que sua requisição está sendo roteada para &lt;code&gt;cell-alpha&lt;/code&gt; ou &lt;code&gt;cell-beta&lt;/code&gt;. Esse mapeamento dinâmico permite realizar migrações ao vivo de locatários entre células para balancear a carga sem tempo de inatividade.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;
Crie o código Python para sua função de roteamento (&lt;code&gt;router.py&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import json
import hashlib
import urllib.request

# Em produção, obtenha isso de um armazenamento de configuração global
CELL_ENDPOINTS = {
    &quot;cell-alpha&quot;: &quot;https://alpha.execute-api.us-east-1.amazonaws.com/prod&quot;,
    &quot;cell-beta&quot;: &quot;https://beta.execute-api.us-east-1.amazonaws.com/prod&quot;
}

def get_target_cell(partition_key: str) -&amp;gt; str:
    &quot;&quot;&quot;Hashes consistentemente a chave de partição para uma célula específica.&quot;&quot;&quot;
    hash_val = int(hashlib.md5(partition_key.encode(&apos;utf-8&apos;)).hexdigest(), 16)
    
    # Distribuição simples por módulo
    if hash_val % 2 == 0:
        return &quot;cell-alpha&quot;
    return &quot;cell-beta&quot;

def lambda_handler(event, context):
    try:
        body = json.loads(event.get(&apos;body&apos;, &apos;{}&apos;))
        tenant_id = body.get(&apos;tenant_id&apos;)
        
        if not tenant_id:
            return {&quot;statusCode&quot;: 400, &quot;body&quot;: &quot;Missing partition key: tenant_id&quot;}
            
        target_cell = get_target_cell(tenant_id)
        target_endpoint = CELL_ENDPOINTS[target_cell]
        
        # Encaminha a requisição para a célula isolada (Simplificado para demonstração)
        req = urllib.request.Request(
            f&quot;{target_endpoint}/process&quot;, 
            data=event.get(&apos;body&apos;).encode(&apos;utf-8&apos;),
            headers={&apos;Content-Type&apos;: &apos;application/json&apos;}
        )
        
        with urllib.request.urlopen(req) as response:
            cell_response = response.read()
            
        return {
            &quot;statusCode&quot;: 200,
            &quot;body&quot;: json.dumps({
                &quot;routed_to&quot;: target_cell,
                &quot;cell_response&quot;: json.loads(cell_response)
            })
        }
        
    except Exception as e:
        return {&quot;statusCode&quot;: 500, &quot;body&quot;: str(e)}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 Provisionando a Camada de Roteamento&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Adicione a camada de roteamento à sua configuração Terraform raiz para expor um ponto de entrada unificado aos seus usuários.
&lt;strong&gt;Por que fazer isso:&lt;/strong&gt; Isso centraliza o controle de acesso. Todo o tráfego externo atinge o roteador, que então proxyf os dados sobre a rede backbone da AWS para as células respectivas, garantindo controle de acesso estrito no limite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;
Adicione isso ao seu &lt;code&gt;main.tf&lt;/code&gt; raiz:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;data &quot;archive_file&quot; &quot;router_zip&quot; {
  type        = &quot;zip&quot;
  source_file = &quot;router.py&quot;
  output_path = &quot;router.zip&quot;
}

resource &quot;aws_lambda_function&quot; &quot;cell_router&quot; {
  filename      = data.archive_file.router_zip.output_path
  function_name = &quot;GlobalCellRouter&quot;
  role          = aws_iam_role.router_role.arn # (Assume-se que um papel básico de execução foi criado)
  handler       = &quot;router.lambda_handler&quot;
  runtime       = &quot;python3.11&quot;
}

resource &quot;aws_lambda_function_url&quot; &quot;router_url&quot; {
  function_name      = aws_lambda_function.cell_router.function_name
  authorization_type = &quot;NONE&quot;
}

output &quot;global_entrypoint&quot; {
  value       = aws_lambda_function_url.router_url.function_url
  description = &quot;A URL única com a qual os clientes interagem.&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. Solução de Problemas Comuns&lt;/h2&gt;
&lt;p&gt;Implantar arquiteturas celulares muda a complexidade do dimensionamento de infraestrutura para roteamento de tráfego e gerenciamento de estado. Esteja preparado para lidar com estes desafios comuns:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Desvio de Partição (Vizinhos Ruidosos):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Problema:&lt;/em&gt; Uma célula fica sobrecarregada enquanto outras ficam ociosas porque um &lt;code&gt;tenant_id&lt;/code&gt; específico gera 80% do tráfego.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Solução:&lt;/em&gt; Monitore as métricas das células de perto. Se um locatário ultrapassar o tamanho de uma célula compartilhada, você deve implementar um processo de &quot;migração de locatário&quot; para mover seus dados para uma célula exclusiva de locatário único, atualizando a lógica de mapeamento do roteador.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Agregação de Dados entre Células:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Problema:&lt;/em&gt; Você precisa gerar um relatório global, mas os dados estão divididos por várias tabelas DynamoDB isoladas.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Solução:&lt;/em&gt; Não consulte as células diretamente para dados globais. Em vez disso, implemente uma estratégia assíncrona de data lake onde cada célula transmite suas mudanças de estado (por exemplo, via DynamoDB Streams e Kinesis) para um armazenamento analítico central.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gargalo na Camada de Roteamento:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Problema:&lt;/em&gt; A Camada de Roteador de Célula cai, causando uma interrupção global — exatamente o que a arquitetura celular tenta evitar.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Solução:&lt;/em&gt; A camada de roteamento deve ser incrivelmente leve e depender de serviços de borda altamente resilientes e geograficamente distribuídos (como Amazon Route 53 ou CloudFront) em vez de uma única instância de computação.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;5. Conclusão&lt;/h2&gt;
&lt;p&gt;Ao implementar uma Arquitetura Baseada em Células, você estabelece fronteiras definitivas de isolamento de falhas. Nós utilizamos o Terraform para definir um blueprint de célula repetível e criamos uma camada de roteamento Python fina para direcionar o tráfego dinamicamente.&lt;/p&gt;
&lt;p&gt;Esta abordagem minimiza o raio de impacto de falhas localizadas, tornando seus sistemas inerentemente mais resilientes. À medida que você expande este conceito, considere como esta estratégia de roteamento desacoplado se traduz em cenários de multinuvem, permitindo que você roteie o tráfego perfeitamente entre uma célula AWS e uma célula Azure baseada em desempenho, custo ou requisitos regulatórios.&lt;/p&gt;
</content:encoded></item><item><title>Practical Guide: Event-Driven Infrastructure on Azure with Terraform</title><link>https://sertaoseracloud.com/posts/practical-guide-building-an-event-driven-infrastructure-on-microsoft-azure-with-terraform-and-1g7k/</link><guid isPermaLink="true">https://sertaoseracloud.com/posts/practical-guide-building-an-event-driven-infrastructure-on-microsoft-azure-with-terraform-and-1g7k/</guid><description>Event-driven architectures decouple system components, replacing direct synchronous communication with a highly scalable publish-subscribe model.</description><pubDate>Sun, 19 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;1. Introdução&lt;/h2&gt;
&lt;p&gt;Arquiteturas orientadas a eventos desacoplam componentes do sistema, substituindo a comunicação síncrona direta por um modelo de publicação/assinatura altamente escalável. Ao final deste tutorial, você será capaz de provisionar uma infraestrutura completa baseada em eventos no Microsoft Azure. Essa configuração utiliza o Azure Event Grid como espinha dorsal de roteamento de eventos, o Azure Service Bus para enfileiramento confiável de mensagens e o Azure Functions para processamento computacional serverless.&lt;/p&gt;
&lt;p&gt;Dominar essa topologia é um requisito estrutural para o design de software moderno. Isolar produtores de consumidores garante que falhas localizadas não cascateiem pelo sistema, permitindo a escala independente de microsserviços. Além disso, traduzir esses conceitos entre diferentes provedores de nuvem fortalece uma estratégia robusta de multicloud, permitindo mapear padrões arquiteturais (como Event Bus -&amp;gt; Fila -&amp;gt; Compute Serverless) perfeitamente em uma landing zone baseada no Azure.&lt;/p&gt;
&lt;h2&gt;2. Pré-requisitos&lt;/h2&gt;
&lt;p&gt;Para executar as configurações propostas neste guia, certifique-se de que possui os seguintes pré-requisitos estabelecidos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uma conta ativa da Microsoft Azure com permissões para criar Grupos de Recursos, Tópicos do Event Grid, Namespaces do Service Bus e Function Apps.&lt;/li&gt;
&lt;li&gt;Terraform instalado localmente (versão 1.0 ou superior) para provisionamento de Infraestrutura como Código (IaC).&lt;/li&gt;
&lt;li&gt;Python (versão 3.9 ou superior) instalado localmente para desenvolver a lógica da função.&lt;/li&gt;
&lt;li&gt;A CLI do Azure (&lt;code&gt;az&lt;/code&gt;) instalada e autenticada em seu ambiente local.&lt;/li&gt;
&lt;li&gt;Familiaridade com navegação em terminal e sintaxe HCL do Terraform.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. Passo a passo&lt;/h2&gt;
&lt;p&gt;Antes de mergulhar no código, é crítico visualizar o ciclo de vida do evento. O diagrama de sequência abaixo mapeia o fluxo de informações através dos serviços Azure provisionados.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7kbcd6voyf2c8w8wq4tl.png&quot; alt=&quot;Diagrama de sequência&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.1 Configurando o Provedor e Grupo de Recursos&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Defina o provedor Azure Resource Manager (&lt;code&gt;azurerm&lt;/code&gt;) no Terraform e crie um Grupo de Recursos fundamental para agrupar logicamente todos os ativos de infraestrutura.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por que fazer:&lt;/strong&gt; O Terraform requer definições de provedores para autenticar e interagir com a API específica da nuvem. O Grupo de Recursos é uma construção obrigatória do Azure que controla o ciclo de vida e o gerenciamento de acesso dos recursos que contém.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;&lt;br /&gt;
Crie um arquivo chamado &lt;code&gt;main.tf&lt;/code&gt; e adicione a seguinte configuração:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;terraform {
  required_providers {
    azurerm = {
      source  = &quot;hashicorp/azurerm&quot;
      version = &quot;~&amp;gt; 3.0&quot;
    }
  }
}

provider &quot;azurerm&quot; {
  features {}
}

resource &quot;azurerm_resource_group&quot; &quot;rg&quot; {
  name     = &quot;rg-event-driven-architecture&quot;
  location = &quot;East US&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 Criando o Tópico do Event Grid&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Provisionar um Tópico Personalizado (Custom Topic) no Azure Event Grid.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por que fazer:&lt;/strong&gt; Um Tópico Personalizado serve como o endpoint dedicado onde suas aplicações publicam eventos de negócios. Isolar eventos de negócios em um tópico personalizado evita misturar a lógica da aplicação com eventos de infraestrutura subjacentes do Azure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;&lt;br /&gt;
Acrescente o seguinte bloco ao seu &lt;code&gt;main.tf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;resource &quot;azurerm_eventgrid_topic&quot; &quot;custom_topic&quot; {
  name                = &quot;app-domain-events-topic&quot;
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 Provisionando o Namespace e Fila do Service Bus&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Criar um Namespace do Service Bus e uma Fila específica dentro dele para absorver os eventos recebidos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por que fazer:&lt;/strong&gt; Embora o Event Grid possa empurrar diretamente para uma Azure Function, o roteamento através de uma Fila do Service Bus introduz um buffer crítico. Isso garante alta disponibilidade, durabilidade de mensagens e evita sobrecarregar o serviço de computação downstream durante picos de tráfego.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;&lt;br /&gt;
Adicione as configurações do Service Bus:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;resource &quot;azurerm_servicebus_namespace&quot; &quot;sb_namespace&quot; {
  name                = &quot;sb-event-driven-demo&quot;
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  sku                 = &quot;Standard&quot;
}

resource &quot;azurerm_servicebus_queue&quot; &quot;event_queue&quot; {
  name         = &quot;event-processing-queue&quot;
  namespace_id = azurerm_servicebus_namespace.sb_namespace.id
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 Desenvolvendo a Azure Function em Python&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Escrever o código Python usando o modelo de programação v2 das Azure Functions para processar mensagens que chegam na Fila do Service Bus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por que fazer:&lt;/strong&gt; A função representa a lógica de negócio que reage ao evento. O modelo v2 utiliza decoradores, fornecendo uma maneira limpa e concisa de definir gatilhos e vinculações diretamente no código, tratando automaticamente a desserialização da mensagem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;&lt;br /&gt;
Crie um arquivo chamado &lt;code&gt;function_app.py&lt;/code&gt; no diretório do seu projeto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import logging
import json
import azure.functions as func

# Inicializa o App da Function
app = func.FunctionApp()

@app.service_bus_queue_trigger(
    arg_name=&quot;msg&quot;, 
    queue_name=&quot;event-processing-queue&quot;,
    connection=&quot;ServiceBusConnection&quot;
)
def process_domain_event(msg: func.ServiceBusMessage):
    logger = logging.getLogger()
    logger.info(&quot;Initiating Service Bus event processing.&quot;)

    try:
        # Decodifica e carrega o corpo da mensagem
        msg_body = msg.get_body().decode(&apos;utf-8&apos;)
        event_payload = json.loads(msg_body)
        
        logger.info(f&quot;Complete event payload: {json.dumps(event_payload, indent=2)}&quot;)
        
        # O esquema do Event Grid tipicamente encapsula dados em um campo &apos;data&apos;
        data = event_payload.get(&apos;data&apos;, {})
        order_id = data.get(&apos;order_id&apos;)
        
        logger.info(f&quot;Successfully processed business operation for order: {order_id}&quot;)

    except json.JSONDecodeError:
        logger.error(&quot;Failed to decode message payload as JSON.&quot;)
    except Exception as e:
        logger.error(f&quot;Unexpected error during processing: {str(e)}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.5 Provisionando a Infraestrutura de Computação para a Function&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Definir a Conta de Armazenamento, o Plano de Serviço (Consumo), e o App de Function Linux via Terraform, injetando as strings de conexão necessárias como variáveis de ambiente.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por que fazer:&lt;/strong&gt; Azure Functions requerem uma conta de armazenamento de suporte para gerenciamento de estado e um plano de execução para definir escala e preço. Injetar &lt;code&gt;ServiceBusConnection&lt;/code&gt; vincula seguramente a camada de computação à camada de mensagens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;&lt;br /&gt;
Adicione os recursos de computação ao seu &lt;code&gt;main.tf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;resource &quot;azurerm_storage_account&quot; &quot;sa&quot; {
  name                     = &quot;saeventdrivendemo123&quot; # Deve ser globalmente único
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = azurerm_resource_group.rg.location
  account_tier             = &quot;Standard&quot;
  account_replication_type = &quot;LRS&quot;
}

resource &quot;azurerm_service_plan&quot; &quot;asp&quot; {
  name                = &quot;asp-event-driven&quot;
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  os_type             = &quot;Linux&quot;
  sku_name            = &quot;Y1&quot; # Plano de Consumo Serverless
}

resource &quot;azurerm_linux_function_app&quot; &quot;function_app&quot; {
  name                       = &quot;func-order-processor-app&quot;
  resource_group_name        = azurerm_resource_group.rg.name
  location                   = azurerm_resource_group.rg.location
  service_plan_id            = azurerm_service_plan.asp.id
  storage_account_name       = azurerm_storage_account.sa.name
  storage_account_access_key = azurerm_storage_account.sa.primary_access_key

  site_config {
    application_stack {
      python_version = &quot;3.11&quot;
    }
  }

  app_settings = {
    &quot;FUNCTIONS_WORKER_RUNTIME&quot; = &quot;python&quot;
    &quot;ServiceBusConnection&quot;     = azurerm_servicebus_namespace.sb_namespace.default_primary_connection_string
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.6 Criando a Assinatura do Event Grid (Regra de Roteamento)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;O que fazer:&lt;/strong&gt; Configurar uma Assinatura de Evento que filtra eventos que chegam ao Tópico Personalizado e os roteia para a Fila do Service Bus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Por que fazer:&lt;/strong&gt; Filtragem avançada garante que apenas eventos relevantes alcancem a camada de computação, evitando execuções desnecessárias e reduzindo custos. Isso atua como o roteador inteligente na arquitetura.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemplo:&lt;/strong&gt;&lt;br /&gt;
Complete o arquivo &lt;code&gt;main.tf&lt;/code&gt; com a assinatura de roteamento:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;resource &quot;azurerm_eventgrid_event_subscription&quot; &quot;queue_subscription&quot; {
  name  = &quot;route-order-created&quot;
  scope = azurerm_eventgrid_topic.custom_topic.id

  service_bus_queue_endpoint_id = azurerm_servicebus_queue.event_queue.id

  advanced_filter {
    string_in {
      key    = &quot;data.detail-type&quot;
      values = [&quot;OrderCreated&quot;]
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para implantar a infraestrutura, execute &lt;code&gt;terraform init&lt;/code&gt;, &lt;code&gt;terraform plan&lt;/code&gt; e &lt;code&gt;terraform apply&lt;/code&gt;. Note que, enquanto o Terraform provisiona a infraestrutura, a implantação atual do código Python é tipicamente tratada via Azure Functions Core Tools (&lt;code&gt;func azure functionapp publish func-order-processor-app&lt;/code&gt;) ou um pipeline de CI/CD como GitHub Actions.&lt;/p&gt;
&lt;h2&gt;4. Solução de Problemas Comuns&lt;/h2&gt;
&lt;p&gt;Implantar sistemas distribuídos pode introduzir desafios de integração. Aqui estão os problemas mais comuns e como resolvê-los:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;String de Conexão do Service Bus Ausente ou Inválida:&lt;/strong&gt;&lt;br /&gt;
Problema: A Azure Function falha ao disparar, e os logs mostram erros de vinculação.&lt;br /&gt;
Solução: Verifique as Configurações do Aplicativo (Application Settings) no Portal do Azure para o App de Function. Certifique-se de que &lt;code&gt;ServiceBusConnection&lt;/code&gt; corresponde exatamente à string de conexão primária do Namespace do Service Bus e está grafada corretamente no decorador &lt;code&gt;app.service_bus_queue_trigger&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Incompatibilidade de Esquema do Event Grid:&lt;/strong&gt;&lt;br /&gt;
Problema: Eventos são publicados com sucesso no tópico, mas nunca chegam na Fila do Service Bus.&lt;br /&gt;
Solução: Inspecione a estrutura da carga útil. O Azure Event Grid requer um esquema específico (id, subject, data, eventType, etc.). Se você está filtrando por &lt;code&gt;data.detail-type&lt;/code&gt; no Terraform, certifique-se de que seu JSON publicado contém explicitamente um objeto &lt;code&gt;data&lt;/code&gt; com uma chave &lt;code&gt;detail-type&lt;/code&gt; correspondente a &lt;code&gt;&quot;OrderCreated&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Conflitos de Nomeamento da Conta de Armazenamento:&lt;/strong&gt;&lt;br /&gt;
Problema: O Terraform falha durante a fase de &lt;code&gt;terraform apply&lt;/code&gt; ao criar o &lt;code&gt;azurerm_storage_account&lt;/code&gt;.&lt;br /&gt;
Solução: Nomes de contas de armazenamento no Azure devem ser globalmente únicos entre todos os clientes da Azure, puramente minúsculos, e ter entre 3 a 24 caracteres. Ajuste o atributo &lt;code&gt;name&lt;/code&gt; no bloco do Terraform para uma string altamente única.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;5. Conclusão&lt;/h2&gt;
&lt;p&gt;Este tutorial estabeleceu uma arquitetura resiliente e desacoplada nativa do Microsoft Azure. Ao utilizar o Terraform, provisionamos o Event Grid para roteamento inteligente de eventos, o Service Bus para enfileiramento robusto de mensagens e o Azure Functions para computação escalável.&lt;/p&gt;
&lt;p&gt;Implementar esses padrões fornece um paralelo claro com outros ecossistemas de nuvem, reforçando uma fundação sólida para projetar arquiteturas celulares e estratégias de multicloud. Como próximo passo, explore a implementação de Filas de Mensagens Mortas (DLQ) dentro do Service Bus para tratar sistematicamente mensagens venenosas, garantindo que sua aplicação distribuída permaneça robusta mesmo diante de dados não processáveis.&lt;/p&gt;
</content:encoded></item><item><title>Practical Guide: Building an Active-Active Multicloud Cell-Based Architecture</title><link>https://sertaoseracloud.com/posts/practical-guide-building-an-active-active-multicloud-cell-based-architecture-bk8/</link><guid isPermaLink="true">https://sertaoseracloud.com/posts/practical-guide-building-an-active-active-multicloud-cell-based-architecture-bk8/</guid><description>A multicloud cell-based architecture represents the pinnacle of fault isolation and vendor neutrality, distributing autonomous cells across cloud providers to eliminate single-provider outages.</description><pubDate>Thu, 20 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;1. Introduction&lt;/h1&gt;
&lt;p&gt;Uma arquitetura baseada em células multicloud representa o ápice de isolamento de falhas e neutralidade de fornecedor. Distribuindo autonomamente &quot;células&quot; entre diferentes provedores de cloud, como colocar a Célula Alpha no AWS e a Célula Beta no Azure, você elimina o risco de uma interrupção regional ou global de um único fornecedor derrubar toda a aplicação. Essa arquitetura impede o bloqueio de fornecedor ao impor uma camada de entrada agnóstica e exige princípios de Design Orientado por Domínio para manter limites de domínio isolados.&lt;/p&gt;
&lt;h2&gt;3. Step-by-Step&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Pré-requisitos&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Contas ativas em AWS e Azure com credenciais administrativas.&lt;/li&gt;
&lt;li&gt;Terraform (1.0+) instalado localmente, com os provedores &lt;code&gt;hashicorp/aws&lt;/code&gt; e &lt;code&gt;hashicorp/azurerm&lt;/code&gt; autenticados.&lt;/li&gt;
&lt;li&gt;Python (3.11+) para implementar a lógica de roteamento edge agnóstico.&lt;/li&gt;
&lt;li&gt;Nome de domínio registrado e acesso a um provedor de DNS na edge (ex.: Cloudflare).&lt;/li&gt;
&lt;li&gt;Conhecimento de Design Orientado por Domínio (DDD) para garantir que os workloads das células sejam completamente acoplados e stateless.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Provisionamento do Data Plane&lt;/strong&gt;
&lt;strong&gt;AWS Cell (Cell Alpha):&lt;/strong&gt; Implante DynamoDB, Lambda e API Gateway via &lt;code&gt;main.tf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;aws_dynamodb_table.aws_cell_state
aws_lambda_function.aws_cell_worker
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Azure Cell (Cell Beta):&lt;/strong&gt; Implante Cosmos DB, Azure Functions e Service Plans via &lt;code&gt;main.tf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;azurerm_resource_group.az_cell_rg
azurerm_cosmosdb_account.az_cell_db
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Roteador de Edge Agnóstico&lt;/strong&gt;
Implemente a lógica de roteamento em Python:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# GLOBAL_REGISTRY mapeia IDs de tenant para endpoints de cloud
def route_request(request_body: str) -&amp;gt; dict:
    ... # Encaminha requisições entre endpoints AWS/Azure
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Desafios&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fragmentação de Estado:&lt;/strong&gt; Utilize Data Lakes unificados (ex.: Snowflake ou Databricks) para análises cruzadas. Configure as funções Lambda e Azure para transmitir mudanças de estado para esse repositório central.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latência entre Nuvens:&lt;/strong&gt; Cache o registro de tenants na edge usando Cloudflare KV, Fastly Compute dictionaries ou Redis Enterprise Active-Active, garantindo que a metadata de roteamento esteja próximo ao roteador edge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Drift de CI/CD:&lt;/strong&gt; Imponha Arquitetura Hexagonal com lógica de domínio pura em Python compartilhada entre nuvens; apenas os adaptadores de I/O (DynamoDB vs. Cosmos DB) devem variar.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;5. Conclusão&lt;/h2&gt;
&lt;p&gt;Adotar uma arquitetura baseada em células multicloud garante defesa final contra lock‑in de fornecedor e falhas catastróficas. Usamos Terraform para orquestrar simultaneamente data planes isolados no AWS e no Azure, e desenvolvemos um roteador Python edge que direciona tráfego conforme o identity do tenant. Esse modelo permite escala horizontal ilimitada tratando AWS e Azure como utilidades intercambiáveis. No próximo passo, concentre‑se na automação da migração de tenants entre clouds, permitindo evacuar recursos do AWS para o Azure em tempo real caso métricas indiquem degradação em um provedor específico.&lt;/p&gt;
</content:encoded></item></channel></rss>