Este é o repositório principal do Transaction Ledger, um serviço essencial que atua como porta de entrada e registro central para solicitações e ordens de transações. O projeto segue estritamente o Clean Architecture (Domain, Application, Infrastructure e Presentation).
O projeto utiliza Python 3.12+ e Poetry como gerenciador de dependências.
- Certifique-se de ter o
poetryinstalado. - Instale as dependências:
poetry install
- Execute os testes unitários da suíte para garantir a integridade da lógica de negócio e infraestrutura:
poetry run pytest -s -v
- Suba o servidor de desenvolvimento suportado pelo FastAPI:
poetry run uvicorn src.presentation.api:app --reload --host 0.0.0.0 --port 8000
Para emular o ambiente de produção da AWS localmente, utilizamos o docker-compose. Ele subirá automaticamente o LocalStack (simulando API Gateway, CloudWatch, DynamoDB e SQS) e o Wiremock (simulando o serviço validador).
Importante (Health Checks): A aplicação FastAPI possui verificações de resiliência na sua inicialização (lifespan). Ela não aceitará tráfego de rede até pingar e garantir que o Wiremock e a Tabela do DynamoDB no LocalStack estejam 100% online.
- Crie o arquivo de configuração local a partir do template:
cp .env.example .env
- Suba a infraestrutura pesada em background (o script
init-aws.shrodará automaticamente):docker-compose up -d
- Aguarde cerca de 10-15 segundos para a AWS local se estabilizar e execute a aplicação a partir do terminal hospedeiro:
# Exporte as variáveis do .env e inicie o uvicorn set -a && source .env && set +a poetry run uvicorn src.presentation.api:app --reload --host 0.0.0.0 --port 8000
- Verificando a Infraestrutura:
Ao invés de bater diretamente no Uvicorn, para fins de simulação corporativa, crie suas ordens através do Load Balancer / API Gateway da AWS que foi instanciado! Pegue o ID do API Gateway nos logs do docker e rode chamadas cURL:
# O API Gateway impõe Strict Throttling com Burst Limit (20) e Rate Limit (10) curl -X POST "http://localhost:4566/restapis/<ID_DO_GATEWAY>/prod/_user_request_/service-orders" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer my-valid-jwt-token" \ -d '{"idempotency_key": "unique-uuid-1234", "requester_id": "req-123", "amount": 100.00}'
- Estressando a Resiliência (K6):
k6 run stress.js
Os diagramas podem ser visualizados abaixo pelo código do mermaid ou dentro da pasta diagrams
---
id: 25b17bb1-3944-4fdf-aedf-faa99e9d885b
---
C4Context
title Transaction Ledger - Diagrama de Container (Nível 2)
Person(client, "Cliente API", "Solicita a criação e consulta de ordens de transação.")
System_Ext(val_service, "Microsserviço de Validação", "API externa frágil. Valida dados restritos de solicitantes.")
System_Ext(event_bus, "Barramento de Eventos (EventBridge/SNS SQS)", "Plataforma de mensageria assíncrona corporativa.")
System_Ext(dlq_bus, "Dead Letter Queues", "AWS SQS DLQ", "Isola mensagens corrompidas e previne Poison Pills.")
System_Ext(sns_alerts, "Tópico de Alertas (SNS)", "AWS SNS (Notifica Squads/PagerDuty).")
Enterprise_Boundary(b0, "Transaction Ledger System") {
Container(api_gateway, "API Gateway", "AWS API Gateway", "Ponto de entrada seguro (Throttling / Edge Caching habilitados).")
Container(auth_lambda, "JWT Lambda Authorizer", "AWS Lambda", "Valida Tokens e restringe acesso malicioso na borda.")
Container(app_service, "Transaction Service", "AWS Lambda (Python 3.12+ / FastAPI)", "Regras de Domain e Orquestração Use Case.")
ContainerDb(db, "Transaction Ledger DB", "Amazon DynamoDB", "Armazena ordens com Idempotência. Gera Streams de CDC.")
Container(cdc_lambda, "CDC Outbox Processor", "AWS Lambda (Python)", "Lê o DynamoDB Streams e publica na mensageria garantindo entrega.")
Container(cw_alarms, "CloudWatch Alarms", "AWS CloudWatch", "Monitora ativamente retenções retidas na DLQ.")
}
Rel(client, api_gateway, "Faz requisições HTTP (POST /service-orders)", "JSON/HTTPS")
Rel(api_gateway, auth_lambda, "Solicita verificação de token", "Internal Invocation")
Rel(api_gateway, app_service, "Roteia evento estruturado (Se autorizado)", "HTTPS/AWS Proxy")
Rel(app_service, val_service, "Valida solicitante (com Circuit Breaker & Retry)", "JSON/HTTPS")
Rel(app_service, db, "Restringe Duplicatas / Grava Transações", "AWS SDK Boto3")
Rel(db, cdc_lambda, "Trigger de Novo Item", "DynamoDB Streams")
Rel(cdc_lambda, event_bus, "Publica evento de 'Ordem Criada'", "AWS SDK Boto3")
Rel(cdc_lambda, dlq_bus, "Move pacotes falhos (Max Retry 3)", "Destination Config")
Rel(event_bus, dlq_bus, "Roteia falhas do consumidor", "SQS Redrive Policy")
Rel(dlq_bus, cw_alarms, "Ativa métrica de saturação (> 1)", "AWS Metrics")
Rel(cw_alarms, sns_alerts, "Dispara notificação de incidente", "HTTPS")
C4Component
title Transaction Ledger - Diagrama de Componente (Nível 3) - Transaction Service
Container(api_gateway, "API Gateway", "AWS API Gateway", "POST /service-orders (Throttling / Edge Cache)")
Container(auth_lambda, "JWT Authorizer", "AWS Lambda", "Inspeção de Headers HTTP (Custom Auth)")
ContainerDb(db, "Transaction Ledger DB", "Amazon DynamoDB", "Tabela de transações com Streams ativados")
System_Ext(val_service, "Serviço Validador", "HTTP REST")
System_Ext(event_bus, "Barramento de Mensageria", "AWS SQS (transaction-events)")
System_Ext(dlq_bus, "Fila de Erros Mortos", "AWS SQS DLQ (transaction-events-dlq)")
System_Ext(sns_alerts, "Alerta de Incidentes", "AWS SNS Topic (dlq-alerts)")
System_Ext(cw_alarms, "Monitoramento Ativo", "AWS CloudWatch Alarms")
Container_Boundary(api, "Transaction Service (AWS Lambda / Clean Arch)") {
Component(handler, "Presentation Layer / FastAPI App", "Python", "Lida com serialização HTTP e Tratamento Global de Exceções.")
Component(use_case, "Create Service Order Use Case", "Python", "Orquestra os Ports de negócio. Retorna entidade existente ou nova.")
Component(validator_client, "HTTP Requester Adapter", "Python/httpx", "Pattern Circuit Breaker e Retry Exponencial usando Tenacity.")
Component(repo, "DynamoDB Repository Adapter", "Python/Boto3", "Busca prévia na `idempotency-key` e valida estado atômico.")
}
Container_Boundary(cdc, "Outbox Processor (Standalone Lambda)") {
Component(stream_handler, "DynamoDB Stream Handler", "Python", "Lê o CDC passivamente em lotes e notifica mensageria.")
}
Rel(api_gateway, auth_lambda, "Verifica Bearer Token (401/403)")
Rel(api_gateway, handler, "Recebe payload HTTP (Se Autorizado)", "JSON")
Rel(handler, use_case, "Mapeia para Command e Executa")
Rel(use_case, validator_client, "Valida ID do Solicitante via interface")
Rel(validator_client, val_service, "Chama API REST sujeita à Circuit Breaks", "HTTPS")
Rel(use_case, repo, "Persiste Transação (Idempotência)")
Rel(repo, db, "GetItem / PutItem Conditional", "AWS SDK")
Rel(db, stream_handler, "Dispara novos INSERTS passivamente", "DynamoDB Streams")
Rel(stream_handler, event_bus, "Garante Gravação Contínua (At-Least-Once)", "AWS SDK")
Rel(stream_handler, dlq_bus, "Descarta Poison Pills (Retries > 3)", "Destination Config")
Rel(event_bus, dlq_bus, "Move mensagens rejeitadas pelo Consumer", "Redrive Policy")
Rel(dlq_bus, cw_alarms, "Dispara alarme de fila > 1", "Métrica SQS")
Rel(cw_alarms, sns_alerts, "Publica Notificação Webhook", "AWS SNS")
Status: Aprovado e Implementado
Contexto: Precisamos arquitetar e implementar um "Livro Razão" (Ledger) responsável por recepcionar altíssimos volumes de chamadas HTTP, validando requisições com base em dados de terceiros (microsserviço instável externo) e armazenando ordens permanentemente sob rigoroso escrutínio contra duplicação de pacotes (Idempotência). Além disso, a arquitetura demanda baixos custos de setup e infraestrutura flexível.
Decisões Arquiteturais e Justificativas:
-
Garantia de Idempotência e Teorema CAP (Consistência vs. Disponibilidade):
- Decisão: Uso do Amazon DynamoDB utilizando sua engine de
Conditional Writesfocado em um modelo altamente Consistente (CP) da chave primária de Idempotência. - Justificando pelo CAP: Em sistemas distribuídos financeiros, a presença de uma latência no Gateway causa retries silenciosos vindo de clientes. Se operássemos nossa base com viés de "Disponibilidade" e consistência eventual (AP), a tabela poderia aceitar gravações duplas num intervalo de milissegundos criando um viés no Ledger. Escolhendo Consistência (CP), delegamos o bloqueio transacional (
Conditional Check) isolado no momento exato doPUT. Se o armazenamento sofrer rebaixamento de rede/disponibilidade bloqueando essa transacionalidade, retornamos falha (preferimos perder ou adiar transibilidade do que criar duplicatas silenciosas atômicas). O Use Case confere o status e retorna "200 OK sem processar nada" se for um retry mapeado, garantindo segurança estrita de estado.
- Decisão: Uso do Amazon DynamoDB utilizando sua engine de
-
Estratégia Backend Serverless baseada em Princípios FinOps e YAGNI:
- Decisão: Adotou-se o framework FastAPI rodando "encapsulado" ou em conjunto com AWS API Gateway (via API REST nativa acionando proxy Integration), ligado ao Python executado no AWS Lambda, gravando no DynamoDB e disparando avisos à filas no Amazon SQS.
- Justificativa via FinOps e YAGNI: Transações costumam ter perfis espasmódicos (alto tráfego simultâneo após campanhas ou horários comerciais, seguido de silêncio na madrugada). Um cluster Kubernetes (EKS) ou instâncias grandes de EC2 demandariam pagamentos base fixos excessivos com recursos em "Idle" (ociosas). A estratégia Serverless permite escalar a custo de "Scale to Zero". Referenciando o YAGNI, não precisamos introduzir malha de serviços Kafka gerencialados ou orquestradores complexos porque uma modelagem estrita em Boto3/SQS/Lambdas cobre satisfatoriamente nosso throughput atual, respeitando a verba do projeto de forma limpa.
-
Abordagem de Resiliência usando Circuit Breaker e Retries:
- Decisão: A interligação com o Microsserviço de Validação deve ser encapsulada usando a biblioteca
tenacityimplementando Retries comExponential Backoffe padrão de Circuit Breaker. - Justificativa de Integração Isolamentosa: APIs externas frequentemente entram em degradação temporária ou longos timeouts. Se o código simplesmente engolisse timeouts em espera aguardando respostas, rapidamente as centenas de lambdas concorrendo esgotariam as conexões "Concurrent Executions" da nossa conta AWS — derrubando até soluções alheias; a famosa Cascading Failure.
- O Retry Exponencial (esperando 2s, depois 4s, até um teto) absorve "engasgos de internet" passivos (Transient Errors).
- Com um Throw Exception
ExternalServiceUnavailableExceptionprecoce (Fast-Fail) disparado pela lib de resiliência, forçamos o Controller HTTP (Presentation API) a devolver o Status503 Service Unavailable. Isso freia o cliente no Gateway sem travar hardware, sem queimar processamento Lambda atoa ("fail fast, save threads") e garantindo a saúde isolada da orquestração principal.
- Decisão: A interligação com o Microsserviço de Validação deve ser encapsulada usando a biblioteca
Status: Aprovado e Implementado
Contexto: Como uma arquitetura focada em resiliência síncrona e consistência básica já está estabelecida, a análise de Edge Cases em ambientes de altíssima volumetria exige a mitigação de falhas catastróficas ou silenciosas. Os cenários abaixo foram mapeados e suas respectivas evoluções arquiteturais foram ativamente construídas e entregues nesta solução, em total aderência aos pilares do AWS Well-Architected Framework:
Decisões Arquiteturais e Justificativas:
-
Prevenção Estrita de Duplicidade e Garantia de Entrega (Transactional Outbox Pattern):
- Requisito Resolvido: O principal requisito de negócio é "não registrar ordens em duplicidade", mas também precisamos garantir que falhas de comunicação não deixem o banco e a mensageria inconsistentes.
- Implementação Realizada: Utilizamos
Conditional Writesno DynamoDB acoplados à chave de idempotência para barrar duplicatas atômicas. Adicionalmente, implementamos o Transactional Outbox Pattern mitigando falhas de Dual-Write. O DynamoDB Streams dispara, de forma puramente reativa e assíncrona baseada no log transacional do banco, uma Lambda CDC garantindo a publicação "At-Least-Once" no SQS.
-
Segurança e Proteção de Saturação (Throttling / API Gateway):
- Problema Resolvido: Riscos de dependências na esteira de validação (serviço frágil) gerando degradação em cascata e vulnerabilidade a ataques de negação de serviço (DDoS).
- Implementação Realizada (Pilar de Segurança e Confiabilidade): Implementação rigorosa de limites de requisição por segundo utilizando AWS API Gateway LocalStack (Usage Plans / Quotas / Throttling). Isso protege a borda da aplicação, controlando a volumetria antes mesmo de instanciar processamento, alinhado aos princípios de Segurança e Confiabilidade do AWS WAF.
-
Ciclo de Vida de Falhas Assíncronas (Dead Letter Queues - DLQs):
- Problema Resolvido: Se o sistema consumidor (ex: Data Lake) ficar indisponível ou rejeitar o contrato, a ordem ficaria retida na mensageria assíncrona travando o Worker repetidamente até estourar a resiliência.
- Implementação Realizada (Pilar de Confiabilidade): Adoção de DLQs (Target Redrive Policy) acoplada na fila
transaction-events. Seguindo as melhores práticas operacionais da AWS, eventos que falham sucessivamente esgotam sua "Receive Count (Max: 3)" e são roteados à fila isoladatransaction-events-dlqpara futura análise ou replay, garantindo a continuidade da fila quente.
-
Bloqueio de Fila no DynamoDB Streams (O Problema da "Poison Pill"):
- Problema Resolvido: A Lambda de Change Data Capture (CDC) processa o Stream de eventos em lotes. Se um pacote corrompido causar erro, a AWS retentaria o envio ininterruptamente, bloqueando o Shard inteiro (Poison Pill).
- Implementação Realizada (Pilar de Confiabilidade): Configuração do
Event Source Mappingda CDC Lambda limitandoMaximumRetryAttempts=3e definindo umDestinationConfigenviando eventos reprovados direto para a Dead Letter Queue do SQS. Pílulas venenosas são expurgadas silenciosamente do log de streaming, permitindo que a linha de produção persista saudável.
Status: Proposto (Apenas Documentado / Não Integrado)
Visão Arquitetural para o Futuro:
-
Colisão de Falsos-Positivos no Controle de Idempotência:
- Cenário de Risco: O Cliente X retransmite acidentalmente a mesma
idempotency_key(chave "123") num novo payload (ex:amount: 50ontem, masamount: 1500hoje) referente a uma ordem diferente. A arquitetura atual devolve "HTTP 200 OK sem reprocessar" assumindo um retry cego, silenciando o erro da aplicação vendedora, provocando conciliação falsa. - Evolução: A rotina de Idempotência do Use Case deverá gerar e persistir o hash criptográfico (
SHA-256) do Payload original. Na recepção de um pacote com mesma chave "123", compara-se o novo hash contra o gravado. Retorna-seHTTP 200apenas perante hashes idênticos. Para hash clash, retorna-seHTTP 409 Conflict.
- Cenário de Risco: O Cliente X retransmite acidentalmente a mesma
-
Degradação Graciosa perante Circuitos Abertos (Patters: Saga e Worker):
- Cenário de Risco: Retornar um
HTTP 503num Lead bilionário (ou campanha de aprovação em feriado) porque o Validador de risco (microserviço terciário) encontrou time-out ou sobrecarga, pune severamente a empresa e o cliente. - Evolução: Ao invés de Fast-Fail rígido, engole-se temporariamente o erro do validador ausente e criamos a Ordem com o Status
"PENDING_VALIDATION". A resposta passa a ser "Aceito (Em Análise) - 202 Accepted". Implementaríamos um Padrão Saga ou rotina (Worker/Cron) rodando no CloudWatch Scheduler de 5-em-5-minutos para validar pacotes estagnados retidos na fila fria e concluir o faturamento ou estorno sem interrupção de entrada de receita.
- Cenário de Risco: Retornar um