ADR-0017 — Object storage para mídias (fotos, editais, matrículas)
- Status: Aceito
- Data: 2026-07-18 · Aceito em: 2026-07-18
Contexto
O enriquecimento (ADR-0010) baixa fotos e PDFs (edital, matrícula) e o backup prevê arquivar frio a coleta_bruta. Falta desenhar onde e como esses binários são guardados, servidos e expirados. imovel_foto.url precisa de um significado concreto.
Decisão
- MinIO (S3-compatível), self-hosted no cluster (chart Helm — ADR-0015), como storage de objetos do MVP; migração para S3 real fica trivial (mesma API).
- Layout de buckets:
imoveis(público via CDN):imoveis/{codigo}/fotos/{ordem}.jpg.documentos(privado):documentos/{codigo}/edital.pdf,.../matricula.pdf— acesso por URL assinada (expira), nunca link público direto.coleta-fria: arquivamento dacoleta_brutaapós a retenção quente (ADR-0012).
imovel_foto.urlguarda a chave do objeto (ou URL pública via CDN), não o binário no banco.- CDN: fotos servidas atrás do Cloudflare (cache), reduzindo carga no MinIO.
- Lifecycle: expiração/arquivo por prefixo; imagens reprocessáveis a partir da
coleta_bruta.
Arquitetura, robustez e escala
- Abstração S3 (porta): o código fala S3 API (SDK); endpoint/credenciais por env. Trocar MinIO ↔ S3/Backblaze é só configuração — sem lock-in.
- Content-addressing + dedup: nome do objeto inclui
sha256do binário → mesma foto não é regravada; casa com opayload_hashdacoleta_bruta. - Pipeline de imagem: gerar derivados (thumbnail/card, médio, original) no worker de mídia; servir WebP quando possível;
Cache-Controllongo + CDN (Cloudflare) na frente das fotos públicas. - Documentos privados (edital/matrícula): URLs assinadas com TTL curto (ex.: 5–15 min); nunca públicas. Opcional: varredura antivírus de PDFs antes de liberar.
- Segurança: buckets privados por padrão, política mínima por prefixo, credenciais em Secret, criptografia em repouso (SSE) e TLS em trânsito.
- Lifecycle concreto:
imoveis/**mantém enquanto o imóvel existir;coleta-fria/**expira/move conforme retenção (ADR-0012); versão anterior de foto expira em N dias. - HA & capacidade: MVP em MinIO single-node (backup do volume); plano de crescimento para MinIO distribuído (erasure coding) ou S3 gerenciado quando o volume exigir. Cotas/limite de tamanho por imóvel para evitar abuso.
- Backup/DR: replicação do bucket (ou snapshot do volume) alinhada ao Backup & DR; tudo é reprocessável a partir da
coleta_brutacomo último recurso. - Observabilidade: uso por bucket/prefixo, taxa de erro de upload, latência, hit de CDN, objetos órfãos (sem
imovel_fotocorrespondente).
Consequências
- + Banco leve (sem BLOBs); servir mídia barato e cacheável.
- + Documentos sensíveis protegidos por URL assinada; troca para S3 sem mudar código.
- − Mais um serviço stateful para operar/backupar (bucket + credenciais).
- − Precisa de política de dedup/limite de tamanho por imóvel.
Alternativas consideradas
- Binários no PostgreSQL (bytea): infla o banco e os backups — rejeitado (só
coleta_brutacomprimida fica no banco por prazo curto). - Servir direto do site da Caixa (hotlink): frágil, sem controle de disponibilidade/ToS — rejeitado.