La lezione precedente ha passato in rassegna i quattro grandi orchestrator e ha notato che uno di loro (Dagster) fa una scelta diversa su a cosa serve un orchestrator. Quella scelta merita una sua lezione, perché è il cambiamento architetturale più interessante nell’orchestrazione dei dati da Airflow stesso. Il cambiamento ha un nome (orchestrazione asset-oriented), un tool concreto che lo dimostra (Dagster, sempre più Prefect, a pezzi anche altri tool), e conseguenze che si dispiegano nel modo in cui i team parlano delle loro pipeline.
Questa lezione è la teoria. Spiega il modello task-oriented tradizionale, l’alternativa asset-oriented, cosa cambia quando adotti il modello asset-oriented, e dove atterrano i costi. L’argomento non è che l’orchestrazione asset-oriented sia universalmente superiore. È che a sufficiente scala e complessità, il modello task-oriented inizia a porre le domande sbagliate, e il modello asset-oriented inizia a rispondere a quelle giuste.
Il modello task-oriented
Airflow ha definito l’ortodossia. Una pipeline è un grafo aciclico orientato di task. Ogni task è un’unità di lavoro: una funzione Python, una query SQL, un comando shell, una run di container. L’orchestrator schedula i task, ne traccia lo stato, e impone le dipendenze tra di loro. I dati che quei task producono sono impliciti. L’orchestrator non sa cosa il task abbia scritto, dove l’abbia scritto, o chi a valle dipenda da ciò.
Un tipico DAG Airflow potrebbe avere questa forma:
with DAG("customer_pipeline", schedule="@daily") as dag:
extract = PythonOperator(task_id="extract_orders", ...)
transform = PythonOperator(task_id="transform_orders", ...)
load = PythonOperator(task_id="load_to_warehouse", ...)
customer_metrics = PythonOperator(task_id="customer_metrics", ...)
extract >> transform >> load >> customer_metrics
L’orchestrator sa: ci sono quattro task, ecco il loro ordine, ecco quando eseguire il DAG. L’orchestrator non sa: extract produce una tabella di staging degli ordini, transform la legge e scrive una tabella ripulita, load scrive la fact table del warehouse, customer_metrics legge la fact table e scrive una tabella di metriche giornaliere. Tutti quei dati vivono fuori dalla consapevolezza dell’orchestrator, in path S3 e tabelle del warehouse che esistono solo perché i task si sono trovati a scriverli.
Va bene quando il sistema è piccolo. Quattro task ti stanno in testa; i dati e l’orchestrazione si allineano nelle teste degli ingegneri che li hanno costruiti. Il modello si rompe su scala, in tre punti contemporaneamente.
La domanda sulle dipendenze diventa dura. Quando hai centinaia di DAG che producono migliaia di tabelle, chiedere “cosa dipende dalla tabella customer_clv?” richiede di leggere il codice dei task o di fare grep sulla codebase. L’orchestrator non può rispondere alla domanda, perché non sa nulla delle tabelle; sa solo dei task.
La domanda sulla freschezza diventa dura. Chiedere “la tabella customer_clv è fresca?” richiede di sapere quale task l’ha scritta per ultimo e quando, poi tradurlo in “la tabella ha X ore”. L’orchestrator può dirti quando il task customer_clv ha avuto successo l’ultima volta. Non può dirti quando la tabella è stata aggiornata l’ultima volta, perché le scritture sono un effetto collaterale che l’orchestrator non ha osservato.
La domanda sui retry diventa grossolana. Quando un task downstream fallisce, la domanda naturale è “quali asset vanno rigenerati?” L’orchestrator task-oriented non può rispondere, e quindi offre o “rilancia questo task” o “rilancia tutto il DAG”. Nessuno dei due è preciso.
Questi dolori sono tollerabili a piccola scala e sempre più intollerabili man mano che la piattaforma cresce.
Il modello asset-oriented
Il modello asset-oriented inverte la relazione. Invece di dichiarare task e lasciare che i dati cadano fuori come effetto collaterale, dichiari asset, che sono le cose che la pipeline produce (tabelle, file, artefatti di modello), e l’orchestrator deriva il lavoro dal grafo degli asset.
Un equivalente Dagster della pipeline qui sopra:
from dagster import asset
@asset
def orders_raw():
return extract_from_source()
@asset
def orders_cleaned(orders_raw):
return clean(orders_raw)
@asset
def orders_warehouse(orders_cleaned):
return load_to_warehouse(orders_cleaned)
@asset
def customer_metrics(orders_warehouse):
return compute_metrics(orders_warehouse)
Ogni funzione dichiara un asset. Gli argomenti della funzione dichiarano le dipendenze (customer_metrics dipende da orders_warehouse, che dipende da orders_cleaned, e così via). L’orchestrator legge questo grafo e sa: ci sono quattro asset, questa è la loro struttura di dipendenze, ecco com’è fatto ciascuno, ecco quando ciascuno è stato materializzato l’ultima volta.
Il cambio nel framing è sottile e pieno di conseguenze. Il team smette di parlare del “task customer_metrics” e inizia a parlare dell‘“asset customer_metrics”. L’orchestrator smette di essere un job runner e inizia a essere un catalogo dati con esecuzione attaccata. Le domande che puoi porre cambiano. “Quando customer_metrics è stato refreshato l’ultima volta?” è ora una query first-class. “Cosa dipende da orders_warehouse?” è una query first-class. “Customer_metrics deve essere fresco ogni sei ore; capisci cosa eseguire per tenerlo fresco” è una primitiva di scheduling first-class (Dagster la chiama freshness policy).
Un esempio lavorato
Prendi una pipeline di customer-data che produce, in ordine: una tabella di eventi grezzi dallo stream CDC, una tabella di eventi sessionizzati aggregata a granularità di sessione, customer feature per il team ML, e un artefatto del modello di customer lifetime value.
Il grafo degli asset:
flowchart LR
RE[raw_events] --> SE[sessionized_events]
SE --> CF[customer_features]
CF --> CLV[customer_clv_model]
SE --> DM[daily_metrics_mart]
CF --> CS[customer_segments]
Diagramma da creare: una versione rifinita del grafo degli asset qui sopra, organizzata in tre layer (raw, intermediate, mart). Il punto visivo è che il grafo è un grafo di dati, non un grafo di job. Ogni box è una cosa che esiste in storage; ogni freccia è una dipendenza che ti permette di tracciare da dove arrivano i dati.
Cosa l’orchestrator ora sa, che l’orchestrator task-oriented non sapeva:
- Il grafo è un grafo di lineage. Se raw_events è sbagliato, l’orchestrator può calcolare esattamente quali asset downstream sono stale: sessionized_events, customer_features, customer_clv_model, daily_metrics_mart, customer_segments.
- Ogni asset ha un timestamp di ultima materializzazione. Il team può vedere “customer_features è stato refreshato l’ultima volta tre ore fa” senza leggere i log dei task.
- Ogni asset ha una freshness policy. “customer_features deve essere fresco ogni sei ore; se non lo è, fai affiorare un alert.” L’orchestrator esegue qualunque asset upstream serva a soddisfare la policy, in ordine di dipendenza.
- Il retry è preciso. Se customer_features è fallito, l’orchestrator sa che customer_clv_model e customer_segments sono ora stale e possono essere rimaterializzati indipendentemente da daily_metrics_mart, che non è toccato.
Niente di tutto questo è teoricamente impossibile in Airflow; è però impilato sopra a un modello che non capisce nativamente nessuna di queste cose. In Dagster è il modello.
I benefici
Quattro vittorie cadono fuori dal framing asset-oriented.
Lineage gratis. Il grafo degli asset È il grafo del lineage. Il lineage in un sistema task-oriented è qualcosa che generi facendo il parse di SQL o scrivendo emettitori OpenLineage che traducono eventi di task in eventi di asset. In un sistema asset-oriented, il lineage è la struttura dati che l’orchestrator già mantiene. La UI del catalogo lo mostra nativamente. Le domande cross-team (“chi legge la tabella orders?”) diventano un click invece di un progetto di archeologia del codice.
Retry più intelligenti. Quando un fallimento o un cambio di codice invalida un asset, l’orchestrator può calcolare il preciso raggio d’esplosione downstream e rimaterializzare solo ciò che è colpito. Nei sistemi task-oriented questo o non succede (rilanci tutto il DAG) o richiede intervento manuale (un ingegnere capisce cosa rilanciare). Su scala, questa distinzione è la differenza tra minuti e ore di lavoro di riparazione.
Un modello mentale migliore. I team parlano di data product, non di job. Un data product è l’asset customer_clv, non il job customer_clv_dag che lo esegue. I nuovi ingegneri imparano il sistema leggendo il grafo degli asset, che descrive cosa esiste; nei sistemi task-oriented imparano leggendo i file DAG, che descrivono cosa gira. Il primo è più corto e più utile.
L’integrazione dbt diventa naturale. I modelli dbt sono asset. Il grafo orientato di modelli del progetto dbt è esso stesso un grafo di asset, con le dipendenze dichiarate via la funzione ref(). Dagster (e sempre più altri tool) legge il progetto dbt e lo integra direttamente nel grafo degli asset: i modelli dbt appaiono accanto agli asset definiti in Python, il lineage scorre attraverso il confine, e l’orchestrator e dbt concordano sullo stesso vocabolario. In un sistema task-oriented, l’intera run dbt è un task opaco, e il lineage interno a dbt è una preoccupazione separata che va ricostruita.
I costi
Il modello asset-oriented non è gratis. Si applicano tre costi reali.
Buy-in concettuale. Gli ingegneri abituati a pensare in task e DAG devono riapprendere il modello. “Cos’è questo DAG?” diventa “cos’è questo asset?” “Quando gira questo?” diventa “quando deve essere fresco questo, e cosa lo produce?” La transizione richiede settimane di pratica per un team ed è una frizione vera. Gli ingegneri junior che imparano asset-oriented dal primo giorno non la sentono; gli ingegneri con cinque anni di memoria muscolare Airflow sì.
Lock-in del tool. Dagster è il tool più committed asset-oriented. Prefect 3.x ha supporto per asset ma il modello è meno centrale. Airflow ha rosicchiato ai bordi con la Datasets API (introdotta nella 2.4 e ampliata da allora), ma è imbullonata sopra a un cuore task-oriented. Argo non ce l’ha proprio. Una volta che un team si è impegnato sull’orchestrazione asset-oriented, cambiare tool è una vera migrazione, non un cambio di config.
Costo di migrazione dai sistemi esistenti. Una shop con un centinaio di DAG Airflow che vuole muoversi verso Dagster sta firmando per un lavoro serio. I DAG non si traducono meccanicamente; i confini degli asset vanno ripensati, le freshness policy progettate, l’integrazione dbt allestita. La maggior parte dei team che fa questo passaggio lo fa incrementalmente, asset per asset, in mesi. Le migrazioni big-bang funzionano raramente.
Questi costi sono il motivo per cui l’orchestrazione asset-oriented non è il default per tutti. È una scelta, e come ogni scelta architetturale, vale la pena farla deliberatamente.
Perché questo conta di più su scala
Il vantaggio del modello asset-oriented scala con la dimensione del sistema.
Per un team piccolo con una dozzina di pipeline e cinquanta tabelle, le domande sulle dipendenze sono risolvibili leggendo la codebase. Le domande sulla freschezza sono risolvibili controllando i log dei task. Le domande sui retry sono risolvibili rilanciando il DAG colpito. Il framing asset-oriented è una piacevole comodità, non una vittoria trasformativa.
Per un platform team con mille pipeline e diecimila tabelle, le domande sulle dipendenze sono intrattabili a mano. “Da cosa dipende transitivamente la tabella customer_clv, e cosa dipende da lei?” è il tipo di domanda che va risposta in una UI da qualcuno che non è l’autore della pipeline. Le domande sulla freschezza richiedono dashboard. Le domande sui retry richiedono precisione, perché rilanciare mille DAG perché un cambio upstream ha invalidato qualche asset foglia non è una strategia ragionevole.
A quella scala, l’orchestrator che sa degli asset sta risolvendo il problema giusto. L’orchestrator che sa solo dei task ne sta risolvendo uno sbagliato e sta forzando il team a imbullonare lineage, tracking di freschezza, e retry precisi come sistemi separati.
Questo è l’argomento architetturale a favore dell’orchestrazione asset-oriented: man mano che la piattaforma dati cresce, la domanda che domina è “cosa dipende da cosa”. L’orchestrator asset-oriented risponde a quella domanda per costruzione. L’orchestrator task-oriented ci risponde alla fine, per accumulo di tool separati che cabli insieme.
Dove sta andando questa cosa
La lezione qui non riguarda solo Dagster. Il framing asset-oriented è un movimento, e altri tool ne stanno assorbendo pezzi. La Datasets API di Airflow, gli emettitori asset di Prefect, lo standard OpenLineage (che la lezione 59 copre nel contesto dell’osservabilità), e la convergenza più ampia tra orchestrazione e cataloghi dati puntano tutti nella stessa direzione. Il “task” è sempre stato un mezzo; l‘“asset” è sempre stato il fine. I tool si stanno mettendo al passo.
Per le piattaforme nuove nel 2026, il modello asset-oriented è il punto di partenza di default a meno che non ci sia un buon motivo per fare diversamente (infrastruttura Airflow esistente, workload a forte componente ML meglio serviti da Argo, una forte preferenza organizzativa). Per le piattaforme legacy, la domanda è se il costo di migrazione si ripaga, cosa che di solito succede per le shop abbastanza grandi da sentire il dolore del modello task-oriented e non per le shop abbastanza piccole da renderlo un dolore teorico.
La prossima lezione resta nello stesso quartiere: osservabilità per i dati, dove il framing asset-oriented incontra i tool di lineage (Marquez, OpenLineage, DataHub) che trasformano il grafo degli asset in qualcosa che il resto dell’organizzazione può navigare. Il filo che collega entrambe le lezioni: una piattaforma dati che non puoi vedere è una piattaforma dati che non puoi gestire, e “vedere” significa più di stato dei task. Significa sapere cosa hai, da dove viene, quando è stato toccato l’ultima volta, e chi lo sta leggendo. L’orchestrator asset-oriented è la metà di quel quadro che vive dove succede il lavoro; il tooling di lineage e osservabilità è la metà che vive dove guarda il resto dell’organizzazione.
Riferimenti e approfondimenti
- Dagster documentation, sezione “Assets”,
https://docs.dagster.io/concepts/assets/software-defined-assets(consultato 2026-05-01). Il riferimento canonico per il modello software-defined asset. - Nick Schrock, “Introducing Software-Defined Assets” (Dagster blog, 2022). Il post di framing originale, che fa il caso degli asset come unità di orchestrazione.
- Apache Airflow documentation, sezione “Datasets”,
https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/datasets.html(consultato 2026-05-01). La risposta di Airflow al framing asset-oriented, utile come termine di paragone. - “Data Pipelines Pocket Reference” (James Densmore, O’Reilly, 2021) e “Fundamentals of Data Engineering” (Joe Reis e Matt Housley, O’Reilly, 2022). Entrambi trattano l’orchestrazione nel contesto e discutono il framing data-product che l’orchestrazione asset-oriented formalizza.