Módulo 11 · Engenharia de Software · Aula 13

Transformação
e Carga

Determinismo, testes que falham antes da transformação existir e estratégias de carga coerentes com a política de histórico, aplicados à silver e à gold do lakehouse construído nas aulas anteriores.

ETL e ELT Determinismo Testes de qualidade Merge e SCD

Computação 2 · Prof. Afonso Brandão · 10/09/2026

BronzeFidelidade à origem, com lote e carimbo
entrada
TestesEscritos antes, e falhando primeiro
contrato
SilverTipos, chaves e deduplicação
conformidade
GoldMétrica com definição acordada
semântica

Continuidade · da Aula 12 para a Aula 13

O dado que chegou à bronze precisa ser convertido em número confiável para o negócio

A Aula 12 garantiu que o dado entra com completude e possibilidade de replay. Esta aula trata do que acontece entre a bronze e a métrica que o negócio lê.

Aulas 2-4Modelagem dimensional
Aula 10Armazenamento
Aula 12Coleta e extração
Aula 13Transformação e carga
Aula 15Métricas e telemetria
A transformação é onde a regra de negócio entra no pipeline. Cada decisão tomada aqui — arredondamento, fuso, tratamento de nulo, critério de junção — altera o número apresentado, e por isso precisa ser reproduzível e testada.

Roteiro · 120 minutos · trinta de quiz, quarenta de exposição e cinquenta de prática

O quiz verifica a leitura, a exposição estabelece o critério e o card de trabalho constrói a transformação testada

O resultado do quiz por tema dirige a ênfase da exposição. Os cinquenta minutos finais são atividade em grupo: escrever os testes antes da transformação, construir a silver e a gold e provar que a carga é repetível.

Abertura · Quiz
30 min

Dez questões

Sobre o material lido, com a distribuição das respostas comentada.

Exposição · Bloco 01
12 min

ETL, ELT e determinismo

Onde transformar, e o que torna a transformação reproduzível.

Exposição · Bloco 02
16 min

Qualidade

Testes que falham antes, deduplicação e reconciliação com a origem.

Exposição · Bloco 03
12 min

Carga e orquestração

Append, overwrite, merge e snapshot; histórico, atomicidade e dependência.

Prática · Atividade
50 min

Card de trabalho

Silver e gold com testes escritos antes, e carga repetível.

Objetivo: escrever uma transformação determinística e uma carga coerente com a política de histórico, e provar as duas por testes que falham antes de a transformação existir.

Bloco 1 · Onde transformar

A escolha entre ETL e ELT define o que pode ser recalculado depois

A diferença relevante reside na versão do dado que permanece disponível quando a regra de negócio muda.

ETL

Transformar antes de gravar

O dado chega ao destino já conformado. Economiza armazenamento e processamento no destino, e permite mascarar dado sensível antes de ele cruzar a fronteira.

Custo: o bruto não é preservado. Quando a regra muda, o passado não pode ser recalculado, e a série histórica passa a misturar dois critérios.

ELT

Preservar o bruto e transformar no destino

A bronze guarda o dado como veio, e a transformação roda sobre ela. A mudança de regra se aplica retroativamente, porque a entrada continua disponível.

Custo: armazenamento e processamento no destino, e a necessidade de decidir onde o dado sensível pode legalmente ser processado.

Critério de decisão: a pergunta é se o pipeline precisará responder, no futuro, qual seria o número sob outra regra. Havendo essa necessidade, o bruto precisa ser preservado, e a escolha recai sobre ELT.
Transformar antes de guardar o bruto. Quando a definição de receita líquida mudar, não haverá como recalcular os três anos anteriores, e a comparação histórica deixa de ser possível.

Bloco 1 · Determinismo

A mesma entrada e os mesmos parâmetros produzem a mesma saída

Quatro construções quebram essa propriedade: data corrente, ordenação parcial, aleatoriedade e dependência da ordem física dos arquivos. As duas primeiras aparecem abaixo, com a correção de cada uma.

Data corrente dentro da transformação

WHERE data >= current_date - 30

O mesmo backfill executado em duas semanas distintas devolve conjuntos diferentes. A janela precisa ser parâmetro de entrada.

Janela declarada como parâmetro

WHERE data BETWEEN :inicio AND :fim

A execução se torna reproduzível, e o registro do parâmetro permite repetir exatamente o mesmo cálculo.

Ordenação ambígua no desempate

row_number() OVER (PARTITION BY id)

Sem cláusula de ordenação, o registro vencedor varia entre execuções conforme o plano escolhido pelo motor.

Ordenação total e determinística

ORDER BY atualizado_em DESC, id ASC

O segundo critério resolve o empate do primeiro, e o resultado passa a ser idêntico em toda execução.

Fixe timezone e arredondamento de forma explícita, e documente o critério adotado.

Versione o código e registre, em cada carga, a versão que a produziu.

Bloco 1 · Decisões silenciosas

Quatro decisões técnicas que alteram o número apresentado ao negócio

As quatro alteram o valor sem produzir erro, e o critério adotado permanece invisível ao negócio quando não é registrado.

DecisãoAlternativas plausíveisEfeito no resultadoOnde registrar
Fuso horárioHora da origem, UTC ou hora local do analistaVendas migram entre dias e o fechamento mensal mudaContrato da camada gold
Nulo em medidaTratar como zero, ignorar na média ou rejeitar a linhaA média muda de valor conforme a escolhaTeste de qualidade declarado
ArredondamentoArredondar por linha, ou somar e arredondar no fimDivergência de centavos que se acumula no totalDefinição da métrica
Junção sem correspondênciaJunção interna, externa, ou externa com membro desconhecidoLinhas de fato desaparecem sem que ninguém percebaDecisão de arquitetura registrada
A junção interna que descarta o fato sem dimensão correspondente é a mais perigosa das quatro, porque reduz o total sem emitir aviso. A defesa é comparar a contagem antes e depois de cada junção.

Bloco 2 · Qualidade

O teste é escrito antes da transformação e precisa falhar primeiro

A falha inicial constitui a única evidência de que o teste é capaz de detectar o defeito que declara verificar.

Escrever e ver falharO teste roda contra a tabela ainda inexistente ou vazia e acusa a falha. Esse é o momento em que ele prova que funciona.
Implementar até passarA transformação é escrita com o objetivo declarado de satisfazer o teste previamente definido.
Manter no pipelineO teste passa a rodar a cada carga e bloqueia a publicação quando a origem mudar de comportamento.
-- teste de unicidade da chave da silver SELECT order_id, count(*) AS n FROM silver.pedido GROUP BY 1 HAVING count(*) > 1; -- aprovado quando devolve zero linhas -- teste de relacionamento: fato sem dimensão SELECT count(*) FROM gold.fato_item_venda f ANTI JOIN gold.dim_produto d USING (product_id);

Schema: colunas, tipos e obrigatoriedade conforme o contrato.

Unicidade da chave declarada em cada camada.

Relacionamento: todo fato encontra a dimensão correspondente.

Faixa de valores e domínio aceito para cada campo categórico.

Atualidade e volume: variação em relação à carga anterior.

Bloco 2 · Deduplicação

A duplicidade se resolve por regra declarada de chave, ordenação e desempate

A escolha do registro vencedor é decisão de negócio. Delegá-la ao motor produz resultado que muda entre execuções.

-- regra declarada: chave, ordenação e desempate SELECT * FROM bronze.pedido QUALIFY row_number() OVER ( PARTITION BY order_id -- a chave do evento ORDER BY atualizado_em DESC, -- o mais recente vence ingerido_em DESC, -- empate: o último a chegar batch_id DESC -- desempate determinístico ) = 1;
Ordenação

Carimbo da origem antes do de chegada

O carimbo de chegada reflete a ordem em que o pipeline processou, e não a ordem em que os fatos ocorreram. Sob reprocessamento, a segunda ordem se inverte e o registro vencedor muda.

Observabilidade

Quantos registros foram descartados

Registre em cada execução o número de duplicatas removidas. O crescimento súbito desse número sinaliza mudança de comportamento na origem, antes que ela apareça no relatório.

Usar DISTINCT para resolver duplicidade. O operador remove linhas idênticas e preserva as que diferem em qualquer coluna, de modo que a duplicata com um campo atualizado permanece nas duas versões.

Bloco 2 · Reconciliação

A conferência com a origem é a verificação que o negócio reconhece

Os testes técnicos garantem consistência interna. A reconciliação garante que o número do painel corresponde ao número do sistema de origem.

Contagem

Linhas por período

Compare, por mês, a contagem na origem com a da camada de consumo. A diferença precisa ser explicada por regra declarada, como o filtro de status.

Soma

Medida financeira por período

A contagem pode coincidir e o valor divergir, por arredondamento ou por linhas com medida nula. A soma é o segundo eixo obrigatório da conferência.

Tolerância

O limite aceito, declarado

Alguma divergência é esperada por diferença de fuso e de janela. A divergência é aceitável quando o limite está declarado previamente como número.

Prática recomendada: a reconciliação roda como parte da carga e registra o desvio a cada execução. A série do desvio ao longo do tempo revela a degradação antes que ela atinja magnitude perceptível no relatório.
Testar apenas o que já passa. O conjunto de testes que nunca reprovou não oferece evidência sobre a capacidade de detectar defeito.

Bloco 3 · Carga

A estratégia de carga se alinha à política de histórico da tabela

Quatro estratégias, com garantias e custos distintos. A escolha decorre de quanto do passado precisa permanecer consultável.

Append

Acrescenta linhas sem tocar no que existe. Simples e barato.

Use quando o fato é imutável e a origem só insere, como um log de eventos.

Overwrite

Substitui a partição inteira pelo resultado recalculado.

Use quando a janela de correção é conhecida e a partição pode ser recomposta por completo.

Merge

Atualiza o que existe e insere o que é novo, por chave.

Use quando a origem corrige registros e a chave é estável ao longo do tempo.

Snapshot

Grava o estado completo por data e preserva cada versão.

Use quando a pergunta analítica é como o cadastro estava em determinada data.
Condição comum às quatro: atomicidade. O consumidor jamais deve enxergar carga pela metade, e a publicação se faz por troca de ponteiro, conforme discutido na Aula 10.
Merge sem chave estável. Cada execução insere linha nova em vez de atualizar a existente, e a dimensão duplica em silêncio ao longo das cargas.

Bloco 3 · Histórico

A política de histórico da dimensão define a carga que ela admite

Retomada da modelagem das Aulas 2 a 4, agora do ponto de vista de quem escreve a carga.

PolíticaO que acontece na mudançaPergunta que passa a ser respondívelCarga correspondente
SobrescritaO atributo é substituído e o valor anterior se perdeQual é o estado atual do cadastroMerge por chave natural
Nova versãoA linha anterior é encerrada e uma nova é aberta com vigênciaQual era o estado na data do fatoMerge com encerramento da versão vigente
Atributo anteriorO valor anterior é preservado em coluna própriaQual era o valor imediatamente anteriorMerge com cópia do valor antes da atualização
Instantâneo diárioO cadastro inteiro é gravado a cada diaComo o cadastro estava em qualquer dataAppend particionado por data do instantâneo
A escolha da política precede a escrita da carga. Implementar sobrescrita e depois descobrir que o negócio precisa da série histórica exige recompor o passado a partir da bronze, quando ela existe.

Bloco 3 · Orquestração

A dependência entre tarefas é declarada no grafo de orquestração

O agendamento fixo pressupõe que a tarefa anterior sempre termina no tempo previsto, e essa premissa falha exatamente nos dias de maior volume.

Dependência declarada

A tarefa parte da conclusão da anterior

O grafo de dependências torna explícito o que precisa existir antes de cada etapa. Quando a extração atrasa, a transformação aguarda em vez de processar dado incompleto.

Consequência: o atraso se propaga de forma visível, e o alerta indica a etapa que o originou.

Retry e backfill

O retry automático exige carga idempotente

Habilitar retry sobre carga não idempotente multiplica o defeito. O backfill de um período longo precisa ser parametrizado por janela e executado em lotes verificáveis.

Consequência: o procedimento de recuperação é testado fora de incidente.

Promova entre ambientes com o mesmo código e parâmetros distintos.

Registre a linhagem: de quais tabelas cada saída deriva, e por qual versão do código.

Defina o acordo de nível de serviço da carga e alerte pelo descumprimento, não pela falha isolada.

Encadear tarefas por agendamento fixo. Quando a anterior atrasa, a seguinte processa dado incompleto e publica um número menor sem emitir erro.

Card de trabalho em sala · 50 minutos

Três perguntas sobre a transformação que o grupo escreve

Em grupo, sobre a bronze da Aula 12: escrever os testes primeiro, construir silver e gold e provar que a carga é repetível.

1

O teste falhou antes de a transformação existir?

Registre a saída do teste antes e depois da implementação. A primeira falha constitui a prova de que ele detecta o defeito.

2

Quantas linhas cada junção descartou?

Compare a contagem do fato antes e depois de cada junção, e explique a diferença por regra declarada.

3

Duas execuções produzem o mesmo número?

Execute a carga duas vezes com os mesmos parâmetros e compare a soma da receita, além da contagem.

8minTestes escritos e falhando
12minSilver conformada
12minGold e junções
9minCarga incremental
9minReconciliação

Card de trabalho · Testes · 8 minutos

Os cinco testes são escritos antes da primeira linha de transformação

Todos rodam contra tabelas ainda inexistentes e devem falhar. Essa falha é o primeiro registro de evidência do encontro.

-- 1. unicidade: uma linha por pedido na silver SELECT count(*) AS falhas FROM ( SELECT order_id FROM silver.pedido GROUP BY 1 HAVING count(*) > 1); -- 2. obrigatoriedade: chave e data nunca nulas SELECT count(*) FROM silver.pedido WHERE order_id IS NULL OR comprado_em IS NULL; -- 3. domínio: status dentro do conjunto aceito SELECT count(*) AS falhas FROM silver.pedido WHERE order_status NOT IN ('delivered','shipped','canceled','unavailable', 'invoiced','processing','approved','created'); -- 4. relacionamento: todo item do fato encontra seu produto SELECT count(*) FROM gold.fato_item_venda f ANTI JOIN gold.dim_produto d USING (product_id); -- 5. faixa: nenhuma medida negativa SELECT count(*) FROM gold.fato_item_venda WHERE price < 0 OR freight_value < 0;
Critério: os cinco devem devolver zero ao final do encontro, e todos devem ter falhado no início. Anote as duas saídas.

Card de trabalho · Silver · 12 minutos

A silver declara tipos, aplica a chave e registra o que rejeitou

O contrato da camada é a conformidade, e o que não o satisfaz é separado em tabela própria, com o motivo registrado.

-- conformidade com deduplicação determinística CREATE OR REPLACE TABLE silver.pedido AS SELECT order_id, customer_id, order_status, CAST(comprado_em AS TIMESTAMP) AS comprado_em, batch_id, ingerido_em FROM read_parquet('s3://lakehouse/bronze/pedido/**/*.parquet') WHERE order_id IS NOT NULL AND comprado_em IS NOT NULL QUALIFY row_number() OVER (PARTITION BY order_id ORDER BY atualizado_em DESC NULLS LAST, -- carimbo da origem ingerido_em DESC, batch_id DESC) = 1; -- os rejeitados permanecem acessíveis, com o motivo CREATE OR REPLACE TABLE silver.pedido_rejeitado AS SELECT *, CASE WHEN order_id IS NULL THEN 'chave nula' ELSE 'data de compra nula' END AS motivo, now() AS rejeitado_em FROM read_parquet('s3://lakehouse/bronze/pedido/**/*.parquet') WHERE order_id IS NULL OR comprado_em IS NULL;
Conferência obrigatória: a soma de silver.pedido com silver.pedido_rejeitado mais as duplicatas removidas precisa igualar a contagem da bronze. Toda linha da entrada tem um destino conhecido.

Card de trabalho · Gold · 12 minutos

Cada junção é medida antes e depois, porque cada uma pode perder linhas

A junção interna descarta o fato sem dimensão correspondente sem emitir aviso, e o total apurado cai sem explicação visível.

-- medir antes: quantos itens existem SELECT count(*) AS itens_origem FROM silver.item; -- a view sobre a silver define o fato; membro desconhecido em vez de descarte CREATE OR REPLACE VIEW gold.v_fato_item_venda AS SELECT i.order_id, i.order_item_id, coalesce(d.product_id, 'DESCONHECIDO') AS product_id, coalesce(d.categoria, 'nao_informada') AS categoria, p.comprado_em, i.price, i.freight_value, i.price + i.freight_value AS valor_total FROM silver.item i JOIN silver.pedido p USING (order_id) LEFT JOIN gold.dim_produto d USING (product_id) WHERE p.order_status = 'delivered'; CREATE OR REPLACE TABLE gold.fato_item_venda AS SELECT * FROM gold.v_fato_item_venda; -- medir depois: a diferença precisa ser explicada SELECT count(*) AS itens_fato FROM gold.fato_item_venda;
A diferença entre as duas contagens deve corresponder exclusivamente ao filtro de status. Qualquer resíduo indica junção que perde linha.
O membro desconhecido preserva o fato e expõe a falta de correspondência como categoria própria.

Card de trabalho · Carga · 9 minutos

A carga da gold recompõe a partição inteira do período corrigido

O overwrite por partição é a estratégia mais simples quando a partição pode ser recomposta por completo a partir da silver. O COPY sobre Parquet não é atômico, e a publicação atômica exige a troca de ponteiro da Aula 10.

-- 1. remover do bucket as partições da janela, mês a mês -- mc rm -r --force local/lakehouse/gold/fato_item_venda/mes=2018-01 -- 2. recompor os meses da janela a partir da view sobre a silver COPY ( SELECT *, strftime(comprado_em, '%Y-%m') AS mes FROM gold.v_fato_item_venda WHERE comprado_em >= :inicio AND comprado_em < :fim ) TO 's3://lakehouse/gold/fato_item_venda' (FORMAT parquet, PARTITION_BY (mes), OVERWRITE_OR_IGNORE, COMPRESSION zstd);
Parâmetro

A janela vem de fora da consulta

O início e o fim são parâmetros da execução, independentes da data corrente, o que torna o backfill reproduzível meses depois.

Verificação

A soma da receita permanece igual

Após a recomposição, a soma do período coincide com a anterior, exceto pelas correções que a janela deveria capturar.

Remoção prévia: OVERWRITE_OR_IGNORE substitui apenas arquivos de mesmo nome, e arquivos excedentes de carga anterior permaneceriam na partição. OVERWRITE (DuckDB 1.1 ou superior) esvazia o diretório de destino inteiro, inclusive os meses fora da janela, e não é suportado em destino remoto como o S3.
Recompor a partição a partir da própria gold. A recomposição precisa partir da silver, porque reprocessar a saída propaga qualquer defeito já presente nela.

Card de trabalho · Conferência · 9 minutos

Os valores de referência para conferir a transformação

Números esperados em cada etapa, sobre a bronze de pedidos e itens construída nas aulas anteriores.

EtapaO que verificarReferência
Testes iniciaisSaída dos cinco testes antes da implementaçãoTodos falham, por tabela inexistente
SilverConformada mais rejeitados mais duplicatasSoma igual à contagem da bronze, sem resíduo
Junção com pedidoItens antes e depois da junção112 650 itens, e a queda decorre apenas do status
Fato entregueLinhas após o filtro de entreguesOrdem de 110 mil itens
Testes finaisSaída dos cinco testes após a implementaçãoTodos devolvem zero
RepetiçãoSoma da receita em duas execuções seguidasIdêntica até o centavo
A comparação por soma de receita é mais sensível que a contagem: junção que duplica linhas mantém a ordem de grandeza da contagem e altera o total de forma imediata.

Card de trabalho · Uso de IA · em paralelo

A IA escreve a transformação, e o grupo responde pelo teste

O assistente é mais útil na crítica do próprio resultado do que na geração, porque o defeito de transformação é plausível e silencioso.

Prompt 1 · Testes

Escrever a verificação primeiro

"A partir deste contrato de camada, escreva os testes de schema, unicidade, relacionamento e faixa que devem falhar enquanto a tabela não existir."

Prompt 2 · Geração

Transformar com parâmetro declarado

"Escreva a silver com tipos declarados, deduplicação por chave natural com ordenação total, e a separação dos rejeitados com o motivo registrado."

Prompt 3 · Crítica

Procurar o não determinismo

"Aponte, neste SQL, o que muda de resultado entre duas execuções, qual junção pode perder ou duplicar linhas e onde a data corrente torna o backfill irreproduzível."

Verifique se a função de janela declara ordenação total, além da partição.

Procure por data corrente e por funções aleatórias dentro da transformação.

Confirme a contagem antes e depois de cada junção que o assistente propôs.

Ponte com a Aula 1 · Spec-Driven Development

Do tema à especificação executável

A decisão de transformação entra no projeto como requisito, decisão registrada e critério verificável.

RF-006

Requisito funcional

Publicar a receita entregue por mês e por categoria, com definição única aplicada a toda a série histórica.

RNF-006

Requisito não funcional

Divergência de reconciliação com a origem até 0,1% por mês; carga determinística sob mesmos parâmetros; backfill de 24 meses executável em lotes verificáveis.

ADR-TRF-01

Decisão de arquitetura

ELT com bronze preservada, em vez de transformação anterior à gravação. Contexto: a definição de receita líquida ainda está em discussão com o parceiro. Consequência: custo de armazenamento e de processamento no destino.

Cenário de aceite

Critério verificável

Dado o mês de agosto de 2018 já publicado, quando executo a carga novamente com os mesmos parâmetros, então a soma da receita permanece idêntica e os cinco testes devolvem zero.

A definição da métrica constitui artefato de projeto, registrado fora do SQL. Na ausência dessa definição, duas equipes produzem números distintos e igualmente plausíveis.

Entrega do card de trabalho

A transformação testada, as contagens de junção e a carga repetível

O que o grupo entrega ao final do card de trabalho, e o que será conferido.

Artefato

Silver, gold e a suíte de testes versionadas

O SQL das duas camadas e dos cinco testes; a saída dos testes antes e depois da implementação; as contagens medidas em cada junção; e a definição da métrica de receita declarada por escrito.

1

Os cinco testes falharam antes da implementação, com a saída registrada.

2

A silver tem tipos declarados, chave única e rejeitados separados com motivo.

3

Toda linha da bronze tem destino conhecido: conformada, rejeitada ou deduplicada.

4

A contagem antes e depois de cada junção está medida e a diferença explicada.

5

A janela da carga é parâmetro externo, sem data corrente dentro da transformação.

6

Duas execuções seguidas produzem a mesma soma de receita, até o centavo.

7

A definição da métrica de receita está declarada, com fuso, arredondamento e tratamento de nulo.

Aula 13 · Síntese

A transformação determina qual número o negócio lê, e o teste escrito antes dela determina quando o erro nesse número é descoberto.

dbt — Data tests Great Expectations — Expectations Kimball — Slowly Changing Dimensions DuckDB — QUALIFY
Pergunta final: quantos dos testes que o grupo escreveu hoje já falharam alguma vez fora do início do encontro?

Sobre este encontro

Transformação e Carga · 10/09/2026 · Prof. Afonso

Objetivo de aprendizagem

Escrever uma transformação determinística, especificar testes de qualidade que falham antes de a transformação existir e escolher a estratégia de carga coerente com a política de histórico da tabela, provando a repetibilidade por soma de receita idêntica entre execuções.

Estratégia do encontro

Quiz de abertura com dez questões sobre o material de leitura, cujo resultado por tema dirige a ênfase da exposição. Exposição dialogada em três blocos, cada um encerrado por checklist de aplicação e erro comum. Card de trabalho em grupo nos cinquenta minutos finais: sobre a bronze construída na Aula 12, os estudantes escrevem cinco testes de qualidade e os veem falhar, constroem a silver conformada com rejeitados separados por motivo, montam a gold medindo a contagem antes e depois de cada junção e provam que duas execuções da carga produzem a mesma soma de receita.

Estrutura do encontro

  1. Quiz de abertura (30 min) — Dez questões sobre o material, com noventa segundos cada e a última valendo o dobro, seguidas do comentário da distribuição das respostas por tema.
  2. Bloco 1 (12 min) — ETL e ELT pela possibilidade de recálculo, determinismo e as quatro decisões técnicas que alteram o número apresentado ao negócio.
  3. Bloco 2 (16 min) — Qualidade: o teste que falha antes de existir, deduplicação por regra declarada e reconciliação com a origem por contagem e por soma.
  4. Bloco 3 (12 min) — Carga e orquestração: append, overwrite, merge e snapshot, política de histórico da dimensão, atomicidade e dependência declarada.
  5. Card de trabalho (50 min) — Em grupo: testes escritos e falhando (8 min), silver conformada (12 min), gold com junções medidas (12 min), carga incremental parametrizada (9 min), reconciliação e conclusão escrita (9 min).