Pular para conteúdo

Containers (C4 nível 2)

"Container" aqui é o termo C4 (unidade deployável), não necessariamente um container Docker. São três apps no monorepo, mas quatro processos do Garantora em execução: o apps/api sobe duas vezes, com a mesma imagem — uma como API HTTP (uvicorn), outra como worker da fila (arq, spec 0050).

flowchart TB
    subgraph Cliente
        Browser["Navegador\n(PWA instalável)"]
    end

    subgraph Garantora["Plataforma Garantora"]
        Web["apps/web\nRefine v5 + React 19 + MUI v9\nbuild estático servido por nginx"]
        Api["apps/api — API\nFastAPI + SQLAlchemy 2.x async\nRouter → Service → Repository → Model"]
        Worker["apps/api — worker\narq (mesma imagem, outro comando)\nconsultas de crédito + jobs das 6h e 7h"]
        Db[("PostgreSQL 16\ndados de negócio e\narquivos dos documentos")]
        Redis[("Redis 7\nfila do arq")]
    end

    E2E["apps/e2e\nPlaywright — roda contra\no dev-remote, nunca contra prod"]
    APIFULL["API Full\n(externo)"]
    MAIL["SMTP\n(Mailpit / AWS SES)"]

    Browser -->|"HTTPS, mesma origem\n(/app em local e dev-remote, / em produção)"| Web
    Browser -->|"HTTPS /api/v1/...\nJWT Bearer"| Api
    Api -->|"asyncpg"| Db
    Api -->|"enfileira run_credit_check_job\n(depois do COMMIT)"| Redis
    Worker -->|"consome a fila,\ncron 6h e 7h (America/Sao_Paulo)"| Redis
    Worker -->|"asyncpg"| Db
    Worker -->|"HTTPS"| APIFULL
    Api -->|"HTTPS (token, extrato)"| APIFULL
    Api -->|"SMTP"| MAIL
    E2E -.->|"interage via browser,\nnunca chama a API direto"| Browser
Container O que é Onde vive o código
web SPA Refine, empacotada pelo Vite e servida como estático por nginx (em local, o próprio Vite com hot reload). PWA instalável, com service worker. Exibe a versão publicada e o painel de Novidades (specs 0043 e 0044), cujas notas são os .md de apps/web/src/versoes/ embutidos no build. apps/web/
api FastAPI sob /api/v1, Swagger em /api/docs, /health e /api/v1/health (os dois devolvem estado do banco, commit e versão da imagem). Uma transação por requisição (CommitRoute). apps/api/app/
worker arq app.worker.WorkerSettings. Um job enfileirado — run_credit_check_job, um por item de consulta de crédito — e dois agendados: daily_credit_finance_job (6h: purga do payload bruto vencido, sincronização do extrato da API Full, acerto de preço) e daily_billing_overdue_job (7h: marca OVERDUE a cobrança vencida dos canais da própria garantidora). Sem retry automático (max_tries = 1). apps/api/app/worker.py
PostgreSQL Todo o dado de negócio, multi-tenant lógico. Os arquivos de documento também moram aqui (documents.content, LargeBinary) — não há object storage. —
Redis Só a fila do arq. Não é cache nem sessão. —

Subir só a API deixa o worker mudo — a consulta de crédito fica PENDING e os jobs agendados não acontecem, sem erro nenhum. Por isso o worker existe nos três ambientes (ver deployment.md). A API não depende do Redis para subir: o pool do arq é criado na primeira vez que algo é enfileirado (app/core/queue.py), e uma falha de enfileiramento marca aquela consulta como FAILED em vez de derrubar a requisição.

A ordem de implementação segue a seta do contrato: apps/api define o contrato (model → schema → repository → service → router), apps/web consome, apps/e2e prova o fluxo ponta a ponta. Ver CLAUDE.md da raiz, seção "Mudanças que atravessam os três apps".

Origem única, roteada por path — não há CORS entre web e api: o front chama /api/v1/... relativo, mesma origem, em todos os três ambientes. O que muda entre eles é quem roteia (Caddy em local e produção, nginx do host no dev-remote) e onde o app fica: sob /app em local e dev-remote, na raiz em produção (VITE_BASE_PATH=/) — ver deployment.md.

apps/e2e nunca é um "container" de produção — é a suíte de testes, e roda sempre contra o dev-remote, nunca contra produção nem contra um ambiente isolado montado à mão (ver CLAUDE.md da raiz, seção "O e2e roda contra o dev-remote, sempre").