Wiki Arquitetura e modelagem
Arquitetura medalhão (bronze, silver, gold)
Três camadas de qualidade crescente. Bronze guarda como chegou, silver limpa e padroniza, gold responde perguntas de negócio.
A arquitetura medalhão organiza o lakehouse em camadas, e cada camada tem um contrato.
- Bronze: o dado como chegou da fonte (JSON, CSV, CDC), com metadados de ingestão (quando, de onde). Nada é descartado: se a regra da silver estiver errada, dá para reprocessar daqui.
- Silver: limpo e conformado. Tipos certos, duplicados removidos, chaves válidas, nomes padronizados. É onde regras de qualidade barram ou separam o que não presta.
- Gold: agregado e modelado para quem consome: métricas de negócio, tabelas de dimensão e fato, o que alimenta Power BI e relatórios.
As três camadas, em SQL que roda aqui
A tabela eventos faz o papel de bronze (veio com um evento repetido). A silver deduplica e só aceita evento de pedido que existe. A gold responde "quantos pedidos foram pagos por dia".
CREATE VIEW silver_eventos AS
SELECT DISTINCT e.id_evento, e.pedido_id, e.tipo, e.ts
FROM eventos e
WHERE EXISTS (SELECT 1 FROM pedidos p WHERE p.id = e.pedido_id);
CREATE VIEW gold_pagamentos_por_dia AS
SELECT date(ts) AS dia, COUNT(*) AS pagamentos
FROM silver_eventos
WHERE tipo = 'pago'
GROUP BY date(ts);
SELECT (SELECT COUNT(*) FROM eventos) AS bronze,
(SELECT COUNT(*) FROM silver_eventos) AS silver,
(SELECT SUM(pagamentos) FROM gold_pagamentos_por_dia) AS gold_pagos;Saída
bronze | silver | gold_pagos ------ | ------ | ---------- 7 | 6 | 2
O que não fazer
- Ler da bronze no relatório "porque está mais atualizada": o relatório herda todo o lixo.
- Silver que já agrega: aí não há onde reaproveitar o dado limpo e detalhado para outra pergunta.
- Corrigir dado na gold à mão: a correção some na próxima carga. A regra mora na silver, no código.
No Databricks
Cada camada costuma ser um schema (ou catálogo) no Unity Catalog, com permissões diferentes: engenharia escreve em todas, analistas leem só a gold. A carga entre camadas é um job (ou um pipeline declarativo) com uma tarefa por etapa.
bronze = spark.readStream.table("bronze.eventos")
silver = (bronze
.dropDuplicates(["id_evento"])
.where("pedido_id IS NOT NULL"))
(silver.writeStream
.option("checkpointLocation", "/Volumes/lakehouse/silver/_checkpoints/eventos")
.toTable("silver.eventos"))Fontes
Conhecimento compartilhado pela Genesis Innovation.