<h2>Padrão SAGA: Orquestrando Transações Distribuídas</h2>
<p>Em arquiteturas serverless, a ausência de transações ACID nativas exige uma abordagem inteligente para manter consistência em múltiplos serviços. O padrão SAGA resolve isso dividindo uma transação grande em uma sequência de transações locais, cada uma com sua própria compensação (rollback). Existem duas variações principais: coreografia (baseada em eventos) e orquestração (controlada centralmente).</p>
<p>A orquestração é mais explícita e recomendada em ambientes serverless. Um orquestrador centralizado coordena as etapas, chamando serviços em sequência e, se algo falhar, executa as ações de compensação. No exemplo abaixo, usamos AWS Step Functions (infraestrutura como código com Terraform):</p>
<pre><code class="language-hcl">resource "aws_sfn_state_machine" "pedido_saga" {
name = "pedido-saga"
role_arn = aws_iam_role.step_functions_role.arn
definition = jsonencode({
Comment = "Saga para processamento de pedido"
StartAt = "ReservarInventario"
States = {
ReservarInventario = {
Type = "Task"
Resource = "arn:aws:states:::lambda:invoke"
Parameters = {
FunctionName = "reservar-inventario"
Payload = {
pedido_id = "$.pedido_id"
items = "$.items"
}
}
Next = "ProcessarPagamento"
Catch = [{
ErrorEquals = ["States.ALL"]
Next = "CompensarReserva"
}]
}
ProcessarPagamento = {
Type = "Task"
Resource = "arn:aws:states:::lambda:invoke"
Parameters = {
FunctionName = "processar-pagamento"
Payload = {
pedido_id = "$.pedido_id"
valor = "$.valor"
}
}
Next = "ConfirmarPedido"
Catch = [{
ErrorEquals = ["States.ALL"]
Next = "CompensarPagamento"
}]
}
ConfirmarPedido = {
Type = "Pass"
Result = {
status = "confirmado"
}
End = true
}
CompensarReserva = {
Type = "Task"
Resource = "arn:aws:states:::lambda:invoke"
Parameters = {
FunctionName = "cancelar-reserva"
Payload = {
pedido_id = "$.pedido_id"
}
}
Next = "FalhaProcessamento"
}
CompensarPagamento = {
Type = "Task"
Resource = "arn:aws:states:::lambda:invoke"
Parameters = {
FunctionName = "reembolsar-pagamento"
Payload = {
pedido_id = "$.pedido_id"
}
}
Next = "CompensarReserva"
}
FalhaProcessamento = {
Type = "Fail"
Error = "PedidoNaoProcessado"
Cause = "Falha na compensação"
}
}
})
}</code></pre>
<h2>Circuit Breaker: Proteção Contra Falhas em Cascata</h2>
<p>O Circuit Breaker é essencial em sistemas serverless para evitar que falhas em um serviço provoquem sobrecarga em outros. O padrão funciona como um disjuntor elétrico: monitora requisições e, após um limite de falhas, "abre o circuito" bloqueando novas tentativas até recuperação.</p>
<p>Implementaremos usando Python com bibliotecas nativas e cache para rastrear falhas:</p>
<pre><code class="language-python">import json
import time
from enum import Enum
from datetime import datetime, timedelta
class CircuitState(Enum):
CLOSED = "closed"
OPEN = "open"
HALF_OPEN = "half_open"
class CircuitBreaker:
def __init__(self, failure_threshold=5, recovery_timeout=60):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.failure_count = 0
self.last_failure_time = None
self.state = CircuitState.CLOSED
def call(self, func, args, *kwargs):
if self.state == CircuitState.OPEN:
if self._should_attempt_reset():
self.state = CircuitState.HALF_OPEN
else:
raise Exception("Circuit breaker is OPEN")
try:
result = func(args, *kwargs)
self._on_success()
return result
except Exception as e:
self._on_failure()
raise e
def _on_success(self):
self.failure_count = 0
self.state = CircuitState.CLOSED
def _on_failure(self):
self.failure_count += 1
self.last_failure_time = datetime.now()
if self.failure_count >= self.failure_threshold:
self.state = CircuitState.OPEN
def _should_attempt_reset(self):
return (datetime.now() - self.last_failure_time).seconds >= self.recovery_timeout
Lambda handler com Circuit Breaker
circuit_breaker = CircuitBreaker(failure_threshold=3, recovery_timeout=30)
def chamar_servico_externo(pedido_id):
import requests
response = requests.post(
"https://api-externa.com/processar",
json={"pedido_id": pedido_id},
timeout=5
)
response.raise_for_status()
return response.json()
def lambda_handler(event, context):
try:
resultado = circuit_breaker.call(chamar_servico_externo, event['pedido_id'])
return {
"statusCode": 200,
"body": json.dumps(resultado)
}
except Exception as e:
return {
"statusCode": 503,
"body": json.dumps({"erro": str(e)})
}</code></pre>
<h2>Idempotência: Garantindo Segurança em Retentativas</h2>
<p>Idempotência significa que executar a mesma operação múltiplas vezes produz o mesmo resultado de uma única execução. Em serverless, onde timeouts e retentativas são comuns, este padrão é crítico. A solução envolve gerar e rastrear IDs únicos de requisição usando DynamoDB como armazenamento de estado.</p>
<pre><code class="language-python">import json
import hashlib
import boto3
from datetime import datetime, timedelta
dynamodb = boto3.resource('dynamodb')
idempotency_table = dynamodb.Table('operacoes-idempotentes')
def gerar_idempotency_key(pedido_id, operacao):
"""Gera uma chave única para a operação"""
conteudo = f"{pedido_id}-{operacao}"
return hashlib.sha256(conteudo.encode()).hexdigest()
def processar_com_idempotencia(pedido_id, operacao, funcao_negocio):
"""Wrapper que implementa idempotência"""
chave = gerar_idempotency_key(pedido_id, operacao)
Verificar se operação já foi executada
try:
resposta = idempotency_table.get_item(Key={'idempotency_key': chave})
if 'Item' in resposta:
print(f"Operação já executada, retornando resultado anterior")
return resposta['Item']['resultado']
except Exception as e:
print(f"Erro ao consultar idempotência: {e}")
Executar operação
try:
resultado = funcao_negocio(pedido_id)
Armazenar resultado para futuras retentativas
idempotency_table.put_item(
Item={
'idempotency_key': chave,
'pedido_id': pedido_id,
'operacao': operacao,
'resultado': resultado,
'timestamp': datetime.now().isoformat(),
'ttl': int((datetime.now() + timedelta(hours=24)).timestamp())
}
)
return resultado
except Exception as e:
raise e
def lambda_handler(event, context):
pedido_id = event['pedido_id']
def processar_pagamento(pid):
Lógica de negócio aqui
return {"status": "sucesso", "transacao_id": "TXN-123"}
resultado = processar_com_idempotencia(
pedido_id,
"processar_pagamento",
processar_pagamento
)
return {
"statusCode": 200,
"body": json.dumps(resultado)
}</code></pre>
<h2>Conclusão</h2>
<p>Dominar estes três padrões é fundamental para construir sistemas serverless resilientes e confiáveis. <strong>SAGA</strong> permite orquestrar transações complexas com compensações automáticas; <strong>Circuit Breaker</strong> protege seus serviços de falhas em cascata e degradação de performance; <strong>Idempotência</strong> garante que retentativas automáticas nunca causem duplicação de dados ou efeitos colaterais indesejados. Juntos, eles formam a base de uma arquitetura serverless pronta para produção.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://docs.aws.amazon.com/step-functions/" target="_blank" rel="noopener noreferrer">AWS Step Functions Documentation</a></li>
<li><a href="https://chrisrichardson.net/post/sagas/2019/01/02/sagas-part-1.html" target="_blank" rel="noopener noreferrer">The Saga Pattern - Chris Richardson</a></li>
<li><a href="https://martinfowler.com/bliki/CircuitBreaker.html" target="_blank" rel="noopener noreferrer">Circuit Breaker Pattern - Martin Fowler</a></li>
<li><a href="https://docs.aws.amazon.com/lambda/latest/dg/idempotent-functions.html" target="_blank" rel="noopener noreferrer">Idempotency in AWS Lambda - Official Guide</a></li>
<li><a href="https://www.oreilly.com/library/view/building-microservices/9781492025269/" target="_blank" rel="noopener noreferrer">Building Microservices with Serverless - O'Reilly</a></li>
</ul>