L’architettura medallion in Microsoft Fabric non è un obbligo tecnico ma una convenzione di responsabilità: ogni layer risponde a una domanda diversa e, soprattutto, ha una diversa tolleranza alla riscrittura. Capire dove collocare deduplica, tipizzazione e business logic è ciò che l’esame DP-600 verifica davvero.
I tre layer e le loro responsabilità
| Layer | Contenuto | Operazione dominante | Chi lo consuma |
|---|---|---|---|
| Bronze | Copia fedele della sorgente, nessuna trasformazione | APPEND (immutabile, storicizzato) |
Solo pipeline interne |
| Silver | Dati puliti, tipizzati, deduplicati, conformati | MERGE (upsert) |
Data scientist, analisti avanzati |
| Gold | Modello dimensionale a star schema, aggregati | MERGE / overwrite selettivo |
Semantic model, report Power BI |
Bronze è append-only per un motivo preciso: se sbagli la logica di pulizia, devi poter ricostruire silver e gold senza ri-interrogare la sorgente. Qui non si fa casting, non si scartano righe “sporche”, non si applica filtro di business. Si conserva anche il timestamp di ingestion (_ingested_at) e la provenienza del file.
Silver è dove avviene il lavoro sporco: cast espliciti dei tipi, normalizzazione di date e valute, deduplica sulla business key, gestione dei valori nulli, join di conformazione fra sistemi diversi (il cliente CRM e il cliente ERP diventano un’unica anagrafica). Silver resta ancora in forma normalizzata o quasi.
Gold modella per il consumo: dimensioni e fatti, surrogate key, SCD Type 2 dove serve storicizzare gli attributi, misure precalcolate. È il layer su cui costruisci il semantic model in Direct Lake, quindi deve essere in Delta con V-Order abilitato e senza eccessiva frammentazione di file (usa OPTIMIZE periodico).
Il carico incrementale con watermark
Il pattern canonico in Data Pipelines di Fabric si compone di tre passaggi.
- Lookup del watermark corrente: un’attività Lookup legge il valore massimo già caricato, tipicamente da una tabella di controllo nel lakehouse o dal warehouse (
SELECT MAX(LastModifiedDate) FROM silver.orders). - Copy activity parametrizzata: la query verso la sorgente filtra
WHERE LastModifiedDate > @watermark AND LastModifiedDate <= @nuovoWatermark. L’estremo superiore fisso evita di perdere righe scritte durante l’esecuzione. - MERGE su Delta: un notebook Spark (o T-SQL sul warehouse) applica l’upsert.
from delta.tables import DeltaTable
target = DeltaTable.forName(spark, "silver.orders")
(target.alias("t")
.merge(staging.alias("s"), "t.OrderId = s.OrderId")
.whenMatchedUpdateAll(condition="s.LastModifiedDate > t.LastModifiedDate")
.whenNotMatchedInsertAll()
.execute())
Il MERGE è ciò che rende il carico idempotente: rieseguire la stessa finestra non duplica nulla, perché le righe già presenti vengono aggiornate anziché inserite. È la differenza sostanziale rispetto all’append cieco, che a ogni rerun (retry automatico, riesecuzione manuale dopo un fallimento parziale) moltiplica le righe e falsa ogni conteggio a valle.
Full reload, append, MERGE: quando usare cosa
- Full reload (
overwrite): tabelle piccole (dimensioni anagrafiche sotto il milione di righe), sorgenti senza colonna di modifica affidabile, o quando la storicizzazione non serve. Semplice e sempre corretto, ma non scala. - Append: legittimo solo su bronze e solo su dati intrinsecamente immutabili (log, eventi, telemetria), dove ogni riga è un fatto nuovo e non un aggiornamento.
- MERGE: la scelta di default per silver e gold quando la sorgente aggiorna le righe esistenti.
CDC e cancellazioni logiche
Quando la sorgente espone Change Data Capture (SQL Server, Azure SQL con CDC/change tracking, oppure un mirroring), non serve un watermark: il feed fornisce già le operazioni I/U/D. In questo caso il MERGE estende la clausola con whenMatchedDelete(condition="s.__op = 'D'").
Se invece la sorgente non propaga le cancellazioni fisiche, hai due strade: un soft delete (colonna IsDeleted valorizzata dal MERGE, con le viste gold che filtrano) oppure una riconciliazione periodica full che confronta le chiavi presenti in sorgente con quelle in silver e marca le mancanti. Il soft delete è quasi sempre preferibile perché preserva l’auditabilità e non rompe le foreign key verso le fatti storiche.
Ricorda infine che Delta mantiene lo storico delle versioni: DESCRIBE HISTORY e il time travel (VERSION AS OF) permettono di verificare cosa ha fatto ogni MERGE e di ripristinare uno stato precedente dopo un carico errato — a patto che VACUUM non abbia già rimosso i file (retention di default 7 giorni).
Trappole tipiche d’esame
- Scenario: dopo un retry della pipeline i totali di vendita raddoppiano → la causa è un
APPENDsu una tabella che riceve aggiornamenti. La risposta giusta è sostituirlo con un MERGE sulla business key, non aggiungere una deduplica a valle. - Scenario: si chiede dove applicare deduplica e cast dei tipi → silver, mai bronze. Bronze deve restare una copia fedele e append-only per consentire il rebuild dei layer superiori.
- Scenario: la sorgente è grande, ha una colonna
LastModifiedDateaffidabile e serve minimizzare il tempo di caricamento → watermark + Lookup + Copy parametrizzata + MERGE, non full reload. Il full reload è la risposta corretta solo per tabelle piccole o senza colonna di modifica. - Scenario: alcune righe spariscono dal sistema sorgente ma restano nei report → serve gestire le cancellazioni, con
whenMatchedDeletesu feed CDC o soft delete tramite flagIsDeleted. Il watermark da solo non intercetta mai le delete fisiche. - Scenario: il report Power BI in Direct Lake è lento dopo mesi di carichi incrementali → i MERGE frequenti hanno generato molti file piccoli. La risposta è
OPTIMIZE(con V-Order) sulle tabelle gold, non passare a Import mode.