Databricks EmpregaDados — Como foi feito (explicado fácil)
Imagina uma fábrica com 3 estações: (1) chega o material cru, (2) alguém limpa e organiza, (3) outro monta o produto final. Pipeline medallion é isso pra dado — Bronze (cru), Silver (limpo), Gold (pronto). Aqui fiz pro EmpregaDados em Databricks + Delta Lake.
O que é isso, em uma frase?
É um pipeline de dados em 3 camadas rodando no Databricks, com PySpark e Delta Lake. Pega CSVs crus de pedidos de delivery e devolve tabelas analíticas prontas pro Power BI.
O nome chique é "arquitetura medallion" — vem da analogia das medalhas (bronze é o menor valor, ouro o maior). Não é frescura: é o padrão de fato de qualquer Lakehouse moderno (Databricks, Snowflake, Microsoft Fabric).
Por que importa? Porque a alternativa — "joga CSV direto no BI" — não escala. Quebra em mil pontos: tipos errados, duplicatas silenciosas, mudança numa fonte arrebenta todos os relatórios. Medallion separa responsabilidades.
As 3 camadas, na ordem
1 · Bronze — "armazém de backup". Leio o CSV cru e jogo num Delta Lake sem mexer em nada, só anexando metadados de origem (nome do arquivo, hora da carga). Se algo der errado depois, eu volto aqui.
df_bronze = (
spark.read
.schema(schema_pedidos) # schema explicito, sem inferencia
.option("header", True)
.csv(INPUT_PATH)
.withColumn("_arquivo_origem", F.input_file_name())
.withColumn("_ingestao_ts", F.current_timestamp())
)
df_bronze.write.format("delta").mode("overwrite").save(BRONZE_PATH)
Detalhe importante: schema explícito. Não confio na inferência do Spark — ele erra em valores brasileiros tipo "R$ 1.234,56".
2 · Silver — "estação de limpeza". Aqui acontece tudo: deduplico (mantenho o mais recente por id_pedido usando window function), filtro status válidos, removo valores negativos, e adiciono colunas derivadas (ano, mês, dia da semana).
df_dedup = (
df.withColumn(
"rank",
F.row_number().over(
F.Window.partitionBy("id_pedido").orderBy(F.desc("_ingestao_ts"))
),
)
.filter(F.col("rank") == 1)
.drop("rank")
)
df_silver = (
df_dedup.filter(F.col("status_pedido").isin(STATUS_VALIDOS))
.filter(F.col("valor_total") > 0)
.withColumn("ano", F.year("data_pedido"))
.withColumn("mes", F.month("data_pedido"))
)
df_silver.write.format("delta").mode("overwrite") \
.partitionBy("ano", "mes").save(SILVER_PATH)
A partição por ano/mês é o tipo de detalhe que economiza minutos: query de "vendas em maio" lê só 1 pastinha em vez do dataset inteiro.
3 · Gold — "produto final". Agrego: vendas total por pedido + total por estabelecimento por mês. Power BI consulta essa camada diretamente — refresh em segundos, não em minutos.
gold_total = (
gold_vendas.groupBy("id_estabelecimento", "ano", "mes")
.agg(
F.sum("valor_pedido").alias("faturamento_mes"),
F.count("id_pedido").alias("qtd_pedidos"),
F.avg("valor_pedido").alias("ticket_medio"),
)
)
gold_total.write.format("delta").mode("overwrite").save(GOLD_TOTAL)
4 · Por que Delta Lake (e não Parquet puro)? Delta dá ACID (transações garantidas, sem dado a meio caminho), time travel (consultar versão de ontem), e otimização automática (OPTIMIZE / Z-ORDER). É Parquet + diário de bordo.
A arquitetura
Em uma linha: o que esse projeto ensina
Que boa engenharia é boa preguiça. Cada camada tem 1 trabalho. Mudou a fonte? Refaço só a Bronze. Mudou a regra de negócio? Só a Silver. Mudou o gráfico? Só a Gold. É manutenção barata — coisa que se nota só quando precisa mudar.
Coisas técnicas que apareceram aqui: arquitetura medallion (Bronze/Silver/Gold), PySpark com schema explícito, Window.partitionBy().orderBy() para dedup, Delta Lake (ACID + time travel), particionamento por ano/mês, agregações pra camada de consumo.