Um sistema serverless de gerenciamento de pedidos para cursos e vouchers de certificação em nuvem. Clientes se cadastram, navegam por um catálogo de cursos (AWS, Azure, GCP), compram com um clique e acompanham o ciclo de vida completo do pedido (processamento, atualização, cancelamento). A arquitetura e orientada a eventos: o barramento central EventBridge desacopla produtores de consumidores, filas SQS absorvem picos de carga e garantem resiliência a falhas temporárias, e o DynamoDB lida com idempotência via ConditionExpression. Nenhuma chamada síncrona cruza fronteiras de serviço. O projeto opera exclusivamente via AWS CLI e shell scripts, sem frameworks de Infrastructure as Code, expondo os parâmetros reais de cada serviço AWS.
Este projeto e material de portfolio. Cada decisão técnica foi tomada com consciência dos trade-offs, documentada em ARCHITECTURE.md, O objetivo e demonstrar pensamento sistêmico sobre arquitetura serverless, não apenas a implementação funcional.
Fluxo completo: cadastro > catálogo > compra > meus pedidos > cancelar
- Acesse o frontend (URL exibida apos deploy).
- Cadastre-se com email e senha.
- Navegue pelo catálogo, filtre por provedor (AWS/Azure/GCP) ou tipo (Curso/Voucher).
- Clique em "Comprar" em qualquer curso.
- Va para "Meus Pedidos" para ver o status.
- Clique em um pedido para detalhe: cancele ou atualize os itens.
Links apos deploy: Os endpoints sao exibidos no terminal ao final do deploy (./run.sh).
%%{init: {'flowchart': {'nodeSpacing': 15, 'rankSpacing': 25, 'padding': 6}}}%%
flowchart LR
Browser["Browser index.html"]:::s_cliente
QA["Browser qa.html"]:::s_qa
STATICSITE["S3 Static Website"]:::s_s3fe
subgraph APIGW["API Gateway (order-ingestion-api)"]
GW_POST["/orders POST"]
GW_GET["/orders GET"]
GW_GET_ID["/orders/{id} GET"]
GW_CANCEL["/orders/{id}/cancel POST"]
GW_PATCH["/orders/{id} PATCH"]
GW_CAT["/catalog GET"]
GW_CAT_ID["/catalog/{id} GET"]
GW_REG["/customers/register POST"]
GW_LOGIN["/customers/login POST"]
GW_ME["/customers/me GET"]
GW_TEST["/test POST (API Key)"]
end
subgraph LambdaIngestao["Lambdas de Ingestao"]
PRE["pre_validator"]
VAL["order_validator"]
end
subgraph LambdaProduto["Lambdas de Produto"]
GWL["order_gateway"]
CAT["catalog_reader"]
AUTH["customer_auth"]
CTRL["test_controller"]
end
subgraph LambdaProcessamento["Lambdas de Processamento"]
PROC["order_processor"]
CANCEL["lifecycle_ops (cancel)"]
UPDATE["lifecycle_ops (update)"]
BATCH["batch_processor"]
end
subgraph SQS_Filas["Filas SQS"]
FIFO["order-validation-buffer (FIFO)"]
PERSQ["order-persister-queue (Standard)"]
CANCELQ["cancel-order-queue (Standard)"]
UPDATEQ["update-order-queue (Standard)"]
BATCHQUEUE["order-s3-batch-queue (Standard)"]
end
subgraph DynamoDB_Tables["DynamoDB"]
PROD["order-production-data + GSI clientId-index"]
AUDIT["order-batch-audit (TTL 90 dias)"]
CATALOG["course-catalog"]
CUSTOMERS["customer-data"]
end
EB["orders-event-bus"]:::s_eb
subgraph SNS_CW["SNS + CloudWatch"]
SNS["order-notifications (email)"]
CW["CloudWatch Alarms (5 DLQs)"]
end
DATABUCKET["order-files-bucket"]:::s_s3d
Browser --> STATICSITE
QA --> STATICSITE
Browser --> GW_POST & GW_GET & GW_GET_ID & GW_CANCEL & GW_PATCH & GW_CAT & GW_CAT_ID & GW_REG & GW_LOGIN & GW_ME
QA --> GW_TEST
GW_POST --> PRE --> FIFO --> VAL --> EB
GW_GET & GW_GET_ID & GW_CANCEL & GW_PATCH --> GWL
GW_CAT & GW_CAT_ID --> CAT
GW_REG & GW_LOGIN & GW_ME --> AUTH
GW_TEST --> CTRL
GWL --> PROD
GWL --> EB
CAT --> CATALOG
AUTH --> CUSTOMERS
CTRL --> EB & DATABUCKET
EB --> PERSQ --> PROC --> PROD
EB --> CANCELQ --> CANCEL --> PROD
EB --> UPDATEQ --> UPDATE --> PROD
DATABUCKET --> BATCHQUEUE --> BATCH --> AUDIT
subgraph Legend["Legenda"]
L1["Acesso do Cliente (HTTP)"]:::l_http
L2["Pipeline de Ingestao"]:::l_ing
L3["Roteamento para Lambdas de Dominio"]:::l_route
L4["Consultas e Persistencia em Dados"]:::l_data
L5["Processamento Orientado a Eventos"]:::l_event
L6["Processamento Batch S3"]:::l_batch
end
style APIGW fill:#3F51B5,color:#fff,stroke:#3949AB
style LambdaIngestao fill:#3949AB,color:#fff,stroke:#303F9F
style LambdaProduto fill:#303F9F,color:#fff,stroke:#283593
style LambdaProcessamento fill:#283593,color:#fff,stroke:#1A237E
style SQS_Filas fill:#00838F,color:#fff,stroke:#006064
style DynamoDB_Tables fill:#1A237E,color:#fff,stroke:#283593
style SNS_CW fill:#37474F,color:#fff,stroke:#455A64
style Legend fill:#F5F5F5,color:#333,stroke:#9E9E9E
classDef s_cliente fill:#7986CB,color:#fff,stroke:#5C6BC0
classDef s_qa fill:#9FA8DA,color:#fff,stroke:#7986CB
classDef s_s3fe fill:#5C6BC0,color:#fff,stroke:#3F51B5
classDef s_eb fill:#006064,color:#fff,stroke:#00838F
classDef s_s3d fill:#283593,color:#fff,stroke:#1A237E
classDef l_http fill:#5C6BC0,color:#fff,stroke:#5C6BC0
classDef l_ing fill:#3949AB,color:#fff,stroke:#3949AB
classDef l_route fill:#303F9F,color:#fff,stroke:#303F9F
classDef l_data fill:#1A237E,color:#fff,stroke:#1A237E
classDef l_event fill:#00838F,color:#fff,stroke:#00838F
classDef l_batch fill:#006064,color:#fff,stroke:#006064
linkStyle 0,1,2,3,4,5,6,7,8,9,10,11,12 stroke:#5C6BC0,stroke-width:2px
linkStyle 13,14,15,16 stroke:#3949AB,stroke-width:2px
linkStyle 17,18,19,20,21,22,23,24,25,26 stroke:#303F9F,stroke-width:2px
linkStyle 27,28,29,30,31,32 stroke:#1A237E,stroke-width:2px
linkStyle 33,34,35,36,37,38,39,40,41 stroke:#00838F,stroke-width:2px
linkStyle 42,43,44 stroke:#006064,stroke-width:2px
Para decisões detalhadas de design, veja ARCHITECTURE.md.
| Servico | Papel no sistema | Alternativa avaliada |
|---|---|---|
| API Gateway | Ponto de entrada REST (11 endpoints) com Request Validator, CORS, API Key para /test | N/A (único serviço de API HTTP serverless da AWS) |
| Lambda | 11 funções Python 3.12 para lógica de negócio serverless | ECS/Fargate: overhead operacional desnecessário para funções de curta duração |
| SQS FIFO | Buffer de validação com ordenação por pedido e ContentBasedDeduplication | Processamento síncrono: sem resiliência a falhas temporárias |
| SQS Standard | 3 filas de processamento + 1 fila S3 batch, paralelismo garantido | FIFO para processamento: forjava sequencial sem ganho de corretude |
| EventBridge | Barramento central de eventos, roteia por detail-type e source | SNS fanout: menor expressividade de filtro e sem suporte a Content-Based Filtering |
| DynamoDB | 4 tabelas: pedidos (com GSI), auditoria (TTL 90d), catálogo, clientes | RDS: custo e operação mais altos para volume variável de laboratório |
| SNS | Notificações de erro (duplicata, schema inválido, DLQ) para email | N/A (único serviço de pub/sub email da AWS) |
| S3 | 2 buckets: dados (batch files) e frontend (static website) | EFS: sem necessidade de sistema de arquivos compartilhado |
| IAM | 11 roles com politicas de menor privilegio, inline e gerenciadas | N/A (único serviço de autorização AWS) |
| CloudWatch | Logs (retenção 14d), Alarmes (5 DLQs), métricas | X-Ray: não disponível na conta de laboratório |
| LocalStack | Emulação local de serviços AWS via Docker | AWS real: custo para desenvolvimento iterativo |
.
├── scripts/ # IaC: deploy, validação, utilitários (26 funções lib.sh)
├── src/ # Codigo-fonte das 11 Lambdas + modulo common/
├── frontend/ # 2 frontends: CloudCert (index.html) + QA Dashboard (qa.html)
├── samples/ # Payloads de teste (api_request.json, batch JSONs)
├── docs/ # Documentação individual por componente (16 arquivos)
├── ARCHITECTURE.md # Decisões de design por tema (este documento)
├── run.sh # Orquestrador principal: deploy completo + validação
└── cleanup.sh # Remoção completa de recursos (idempotente)
- AWS CLI v2 configurado
- Python 3.12
- Docker e Docker Compose (para LocalStack)
- Utilitário
zip
cp .env.example .env
docker-compose up -d
./run.shEdite .env: defina DEPLOY_TARGET=aws, preencha AWS_REGION, RESOURCE_SUFFIX, NOTIFICATION_EMAIL.
./run.sh./scripts/validate-flow.sh25 testes que cobrem: criação de pedidos, processamento S3 batch, lifecycle (cancelar/atualizar), duplicatas, consultas, alertas SNS, filas DLQ, catálogo, autenticação JWT, gateway de pedidos, e frontends.
Idempotência por ConditionExpression no DynamoDB em vez de deduplicação na fila. A janela de 5 minutos do SQS FIFO impedia testes de duplicidade no frontend. A solução foi usar MessageDeduplicationId = uuid4() (sempre único) e delegar a deduplicação de negócio ao ConditionExpression: attribute_not_exists(orderId) no DynamoDB, que e permanente e gera alerta SNS. Detalhes em ARCHITECTURE.md#3-idempotência-conditionexpression-vs-deduplicação-na-fila.
JWT implementado manualmente em stdlib Python sem dependencias externas. A conta de laboratório não tem Cognito, Secrets Manager nem KMS CMK. O modulo common/auth.py implementa PBKDF2-SHA256 (200.000 iterações), HMAC-SHA256, e compare_digest contra timing attack. Sem requirements.txt ou camada Lambda. Detalhes em ARCHITECTURE.md#4-seguranca-sem-waf-cognito-e-kms.
Resource Policy do API Gateway com padrão Allow geral + Deny condicional. A implementação inicial usava Allow-only com IpAddress, que bloqueava endpoints públicos (POST /orders, GET /orders) quando a restrição de IP era ativada. A correção usou o padrão Allow geral para toda a API + Deny condicional restrito a */*/POST/test, respeitando a precedência do Deny sobre Allow. Detalhes em ARCHITECTURE.md#4-seguranca-sem-waf-cognito-e-kms.
batchItemFailures em todas as Lambdas SQS para reprocessamento parcial de lote. Sem essa configuração, uma falha em uma das 5 mensagens do lote derrubava o lote inteiro, reprocessando mensagens ja bem-sucedidas. Com ReportBatchItemFailures, apenas os messageId com erro retornam na resposta, e as mensagens bem-sucedidas são confirmadas. Detalhes em ARCHITECTURE.md#2-resiliência-dlq-batchitemfailures-e-visibilitytimeout.
Distribuido sob licença MIT. Projeto desenvolvido por Jose Anderson.