Corso
I data contract sono la spina dorsale della qualità e della scalabilità dei dati nelle soluzioni distribuite. Specificano il formato, lo schema e i protocolli che regolano lo scambio tra entità del database. Questi accordi formali eliminano ambiguità e assunzioni non documentate sui dati.
In questo articolo chiarirò il concetto di data contract offrendo sia tecniche di base sia avanzate per facilitarne un'implementazione efficace.
Capire i data contract
Un singolo data contract delinea i parametri precisi per lo scambio dati tra due modelli. Questi accordi formali assicurano che non ci siano ambiguità riguardo a formati e schemi dei dati.
La definizione e la validazione dei data contract sono fondamentali per una collaborazione efficace tra team.
In poche parole, un data contract è un accordo formale tra il processo che modifica lo stato originale dei nostri dati (producer) e le destinazioni (consumer). È molto simile al funzionamento dei contratti commerciali: rappresentano obblighi tra fornitori e consumatori di un prodotto. I data contract fanno lo stesso con i data product, cioè tabelle, viste, modelli di dati, ecc.
L'obiettivo è ridurre le interruzioni a valle delle pipeline e rendere le trasformazioni dati stabili e affidabili.
I componenti principali di un data contract sono lo schema (colonne e formati), la parte di semantic layer (misure, calcoli e restrizioni), i service level agreement (SLA) e la data governance.
I vantaggi dei data contract includono:
- Automazione e verifiche della qualità dei dati quando si creano o si aggiornano nuovi output.
- Abilitare uno scaling efficiente, soprattutto per architetture dati distribuite, ad esempio il data mesh.
- Migliorare il ciclo di sviluppo dei dati, con un focus sulla creazione di strumenti per la validazione dei contratti.
- Favorire la collaborazione attraverso feedback tra producer e consumer di dati.

Data contract. Immagine dell'autore.
Esempio di data contract con dbt
In un data contract, gli schemi definiscono i nomi degli attributi, i tipi di dato e se gli attributi sono obbligatori. Possono anche specificare formato, lunghezza e intervalli di valori accettabili per le colonne.
Consideriamo uno schema di modello dbt definito così in un file YAML. Lo schema della nostra tabella è definito sotto columns:
models:
- name: dim_orders
config:
materialized: table
contract:
enforced: true
columns:
- name: order_id
data_type: int
constraints:
- type: not_null
- name: order_type
data_type: string
Ora immaginiamo di definire il nostro modello dim_orders così:
select
'abc123' as order_id,
'Some order type' as order_type
Avere un contract con enforced: true nella definizione del modello genererà il seguente errore se proviamo a materializzare dim_orders in una tabella nella nostra piattaforma dati:
20:53:45 Compilation Error in model dim_customers (models/dim_orders.sql)
20:53:45 This model has an enforced contract that failed.
20:53:45 Please ensure the name, data_type, and number of columns in your contract match the columns in your model's definition.
20:53:45
20:53:45 | column_name | definition_type | contract_type | mismatch_reason |
20:53:45 | ----------- | --------------- | ------------- | ------------------ |
20:53:45 | order_id | TEXT | INT | data type mismatch |
20:53:45
20:53:45
20:53:45 > in macro assert_columns_equivalent (macros/materializations/models/table/columns_spec_ddl.sql)
Lo stesso accadrebbe con colonne extra, controlli SLA e metadati mancanti, qualora scegliessimo di definirli.
Un esempio dbt più avanzato includerebbe vincoli di modello applicati:
# models/schema.yaml
models:
- name: orders
# required
config:
contract:
enforced: true
# model-level constraints
constraints:
- type: primary_key
columns: [id]
- type: FOREIGN_KEY # multi_column
columns: [order_type, SECOND_COLUMN, ...]
expression: "OTHER_MODEL_SCHEMA.OTHER_MODEL_NAME (OTHER_MODEL_FIRST_COLUMN, OTHER_MODEL_SECOND_COLUMN, ...)"
- type: check
columns: [FIRST_COLUMN, SECOND_COLUMN, ...]
expression: "FIRST_COLUMN != SECOND_COLUMN"
name: HUMAN_FRIENDLY_NAME
- type: ...
columns:
- name: FIRST_COLUMN
data_type: DATA_TYPE
# column-level constraints
constraints:
- type: not_null
- type: unique
- type: foreign_key
expression: OTHER_MODEL_SCHEMA.OTHER_MODEL_NAME (OTHER_MODEL_COLUMN)
- type: …
Fornire una definizione di schema come insieme di regole e vincoli applicati alle colonne di un dataset offre informazioni molto importanti per l'elaborazione e l'analisi dei dati.
Gli schemi tendono a cambiare nel tempo.
È uno scenario comune. Immaginiamo che alla nostra tabella sorgente venga aggiunta una colonna contrattualizzata extra:
select
'abc123' as order_id,
'Some order type' as order_type,
'USD' as currency
È essenziale considerare i cambiamenti di schema durante gli aggiornamenti incrementali perché, altrimenti, l'output del modello incrementale a valle invaliderebbe il contratto.
Questo si può risolvere aggiungendo on_schema_change: append alla strategia incrementale di dbt.
Le validazioni di schema possono essere esplicite o implicite.
Alcuni formati file big data, come AVRO e Parquet, supportano di default uno schema a bordo e definizioni implicite, quindi non servono validazioni esterne aggiuntive.
Al contrario, formati file senza schema come JSON richiedono una validazione esterna. Alcune librerie Python, come pydantic o un semplice @dataclass, possono occuparsene:
from pydantic import BaseModel
class ConnectionDataRecord(BaseModel):
user: str
ts: int
record = ConnectionDataRecord(user="user1", ts=123456789)
Se infrangiamo le regole assegnando valori che non rispettano i criteri, verrà sollevata un'eccezione. Ad esempio, l'eccezione verrà generata se proviamo a chiamare ConnectionDataRecord('', 1).
Data contract semantici
Le validazioni semantiche garantiscono che i dati siano logicamente coerenti e allineati alla logica di business.
Le validazioni semantiche devono essere applicate esplicitamente.
Infatti, a differenza dei controlli di schema, i data contract semantici si basano sulla logica di business e vanno implementati esternamente.
In molti casi d'uso, la semantica appare come un'estensione dei contratti validati dallo schema. Spesso dipende da regole di business e rappresenta un insieme di condizioni di riga a cui il nostro modello dati deve conformarsi.
Un esempio di contratto semantico può essere il seguente:
- Deviazione metrica: dalla media mobile o da un'altra soglia. Possiamo tollerare che il numero di utenti attivi di ieri scenda sotto il 75% della media mobile a 7 giorni, ma non accetteremo lo 0%.
- Logica di business: flag di allerta nel monitoraggio delle transazioni e punteggi di prevenzione frodi. In questo scenario, il payout deve essere 0.
- Data lineage: descrive come si evolvono le entità dati. Per esempio,
transaction_completed_atnon può avvenire prima dicreated_at. - Integrità referenziale: le relazioni tra entità sono importanti e possono essere descritte anche con contratti semantici. Considera il codice dbt sotto: spiega che ogni
refund_iddi un rimborso punta a untransaction.idvalido.
- name: refunds
enabled: true
description: An incremental table
columns:
- name: refund_id
tests:
- relationships:
tags: ['relationship']
to: ref('transactions')
field: id
Spetta all'utente finale impostare il livello di allerta sui contratti semantici, spesso implementato con test di qualità dati personalizzati.
Varie regole di logica di business fanno spesso riferimento all'integrità dei dati. Scenari di dati illogici possono emergere da errori di configurazione di database e server o dall'iniezione involontaria di dati di test in produzione. I controlli di integrità servono a validare i dati rispetto a tali regole, aiutando a identificare valori anomali o illogici.
Le entità del database sono sempre correlate tra loro. Questo si chiama relazione tra entità ed è spesso rappresentata da un entity relationship diagram (ERD). Un'inadeguata integrità referenziale può portare a problemi come dati mancanti o incompleti. I data contract devono affrontare questi potenziali rischi per garantire integrità e accuratezza dei dati.
Per esempio, una relazione comune è l'associazione uno-a-molti tra customers e orders, dove un singolo cliente può avere più orders. In tale scenario, un ordine è valido solo se contiene un customer_id valido all'interno del dataset customers.
Questo tipo di vincolo, un vincolo di integrità referenziale, assicura che la relazione tra entità sia mantenuta correttamente.
Service Level Agreement (SLA) nei data contract
Gli SLA sono un livello extra di controlli di qualità dati che si possono implementare tramite data contract.
Gli SLA si riferiscono alla freschezza dei dati.
Poiché i modelli dati vengono aggiornati regolarmente con nuovi dati, gli SLA possono includere controlli sull'orario limite entro il quale si prevede la disponibilità dei nuovi dati o sul ritardo massimo consentito. In dbt, ad esempio, si può ottenere con i test di freschezza:
- name: orders
enabled: true
description: A source table declaration
tests:
- dbt_utils.recency: # https://github.com/dbt-labs/dbt-utils#recency-source
tags: ['freshness']
datepart: day
field: timestamp
interval: 1
Supponiamo di voler generare un report sui dati di ieri; per farlo vogliamo assicurarci che i dati esistano. Sarebbe infatti strano non vedere i nuovi ordini per alcuni giorni o più.
Nell'esempio seguente possiamo testare il nostro dataset per ritardi imprevisti degli ordini usando la configurazione dei test in dbt. Questo ti permette di selezionare e referenziare il test usando quel nome specifico.
version: 2
models:
- name: orders
columns:
- name: status
tests:
- accepted_values:
name: unexpected_order_status_today
values: ['placed', 'shipped', 'completed', 'returned']
config:
where: "order_date = current_date"
Definendo un nome personalizzato, hai il pieno controllo su come il test viene mostrato nei log e negli artefatti di metadati.
Allo stesso modo, nelle pipeline in tempo reale, i dati in genere non dovrebbero essere più vecchi di poche ore. Gli SLA sono cruciali per le applicazioni di stream processing, dove i dati vengono elaborati in tempo reale con latenze di minuti o addirittura secondi.
Nelle app di streaming vogliamo verificare il ritardo massimo consentito per eventi in arrivo in ritardo e metriche come Mean Time Between Failures (MTBF) e Mean Time to Recovery (MTTR). Implementarlo implica tracciare meticolosamente gli incidenti ed estrarre i dati rilevanti da strumenti di monitoring e incident management come PagerDuty, Datadog e Grafana.
Data governance contract
La gestione corretta delle informazioni personali identificabili (PII) è parte integrante del processo di trasformazione dei dati. Per molte aziende è fondamentale che questi dataset siano almeno conformi al GDPR e rispettino normative sulla privacy come HIPAA o PCI DSS.
I data governance contract garantiscono che siano in atto adeguate politiche di pseudonimizzazione o di data masking.
Considera il codice dbt qui sotto. I contratti applicati come tests richiedono che user_email sia un hash SHA256 (mascherato):
models:
- name: customer_data
columns:
- name: user_email
tests:
- dbt_expectations.expect_column_values_to_not_match_regex:
regex: "^(?!.*\b@\b).* # Ensure identifiers do not contain emails
flags: i # Case-insensitive matching
D'altra parte, potremmo voler imporre il pattern matching per altre tabelle. Per esempio, nell'esempio seguente, il campo transaction_reference deve seguire il pattern [“TRX-%”, “%-2023”]:
models:
- name: transaction_data
columns:
- name: transaction_reference
tests:
- dbt_expectations.expect_column_values_to_match_like_pattern_list:
like_pattern_list: ["TRX-%", "%-2023"]
match_on: any
I data governance contract possono essere utili anche quando includono colonne con dati sensibili, metadati (proprietari dei dati, ecc.) e ruoli utente autorizzati ad accedere a un data product.
Per esempio, possiamo indicare l'owner del nostro modello e lo stadio del ciclo di vita dello sviluppo usando il campo meta in dbt:
# models/schema.yaml
version: 2
models:
- name: users
meta:
owner: "@data_mike"
model_maturity: in dev
contains_pii: true
columns:
- name: email
meta:
contains_pii: true
Il campo meta ti consente di impostare metadati per una risorsa, che vengono poi compilati nel file manifest.json generato da dbt e possono essere visualizzati nella documentazione generata automaticamente.
Possiamo usare pacchetti come dbt-checkpoint per scansionarlo durante la pull request e assicurarci che i campi meta obbligatori siano presenti. L'hook fallisce se un qualsiasi modello (dal manifest o dai file YAML) non ha le chiavi meta specificate.
Le chiavi meta del modello devono essere nel file YAML o nel manifest.
Pattern di implementazione dei data contract
La validazione dei contratti può essere eseguita nelle pipeline di streaming (per riga) prima dell'ingestione dei dati e dopo — alla sorgente (layer del modello dati sorgente).
Quando ingestiamo i dati "as is", il processo di validazione agisce come uno step di trasformazione, applicando le regole del contratto e filtrando i dati non validi, ad esempio in una vista o tabella dedicata dove i dati possono essere analizzati ulteriormente.
I controlli di validazione possono essere applicati post-facto quando i dati vengono ingestiti direttamente in un data lake grezzo.
Il principale vantaggio della validazione dei data contract in tempo reale è la capacità di filtrare i record non validi prima che raggiungano la destinazione finale — un data lake o un data warehouse. Questo approccio è comune nei processi event-driven o real-time, come gli eventi di Change Data Capture (CDC). In questo tipo di validazione, alcuni aspetti del contratto vengono verificati mentre i dati scorrono nella pipeline.
Strumenti per i data contract
La community dei dati sta gradualmente riconoscendo i potenziali benefici dei data contract, un'area dell'ingegneria dei dati in continua evoluzione. Esistono molti strumenti in questo spazio, alcuni ancora nelle fasi iniziali. dbt può essere considerato un framework universale per i data contract.
Contratti simili possono essere implementati usando Dataform di Google alla sorgente (layer del modello dati sorgente), cioè quando i dati sono stati ingestiti con successo nel nostro data warehouse. In questo caso useremmo semplici condizioni di riga.
Considera l'esempio qui sotto. Applica alcune condizioni di riga alla nostra tabella:
-- my_table.sqlx
config {
type: "table",
assertions: {
nonNull: ["user_id", "customer_id", "email"]
}
}
SELECT …
Qui sotto c'è un altro esempio di implementazione di data contract, ottenibile con Soda.io, un framework di qualità dei dati di nicchia:
# Checks for basic validations
checks for dim_customer:
- row_count between 10 and 1000
- missing_count(birth_date) = 0
- invalid_percent(phone) < 1 %:
valid format: phone number
- invalid_count(number_cars_owned) = 0:
valid min: 1
valid max: 6
- duplicate_count(phone) = 0
Regolando le impostazioni di allerta puoi configurare un controllo perché emetta un avviso invece di fallire la validazione. Una scansione Soda esegue i controlli specificati in un accordo, controlla il file YAML o inline all'interno di un'invocazione programmatica e restituisce per ciascun controllo un risultato: pass, fail o error.
Per esperienza, l'introduzione dei data contract in molte aziende resta frammentata, a seconda di fattori come i pattern di progettazione delle pipeline (batch o real-time), le scelte di serializzazione dei dati e i sistemi usati per storage ed elaborazione.
L'implementazione dei data contract dipende dai requisiti e dalla logica di business.
Great Expectations, una libreria Python, può essere usata per implementare data contract a livello semantico. Si installa con pip: pip install great_expectations.
Dopo aver eseguito great_expectations init, possiamo procedere con la validazione dei dati:
Using v3 (Batch Request) API
___ _ ___ _ _ _
/ __|_ _ ___ __ _| |_ | __|_ ___ __ ___ __| |_ __ _| |_(_)___ _ _ ___
| (_ | '_/ -_) _ | _| | _|\ \ / '_ \/ -_) _| _/ _ | _| / _ \ ' \(_-<
\___|_| \___\__,_|\__| |___/_\_\ .__/\___\__|\__\__,_|\__|_\___/_||_/__/
|_|
~ Always know what to expect from your data ~
Let's create a new Data Context to hold your project configuration.
Great Expectations will create a new directory with the following structure:
great_expectations
|-- great_expectations.yml
|-- expectations
|-- checkpoints
|-- plugins
|-- .gitignore
|-- uncommitted
|-- config_variables.yml
|-- data_docs
|-- validations
OK to proceed? [Y/n]:
Considera lo snippet di codice qui sotto. Spiega come creare una definizione di controllo per la colonna price:
"expectation_type": "expect_column_values_to_match_regex",
"kwargs": {
"column": "price",
"mostly": 1.0,
"regex": "^\\$([0-9],)*[0-9]+\\.[0-9]{2}$"
},
Best practice per i data contract
Non esiste una risposta giusta o sbagliata perché, per esperienza, un'implementazione di successo dei data contract dipende molto dai requisiti di business. Per farli funzionare in modo efficace, segui queste best practice chiave:
- Scalabilità: consiglio di aggiungere meccanismi di estensibilità (ad es.
on_schema_change: append) e versionamento, che consentano di modificare i termini del contratto senza interrompere le integrazioni esistenti. Progetta i data contract pensando a cambiamenti futuri e scalabilità. Questo approccio assicura che il contratto possa adattarsi a esigenze ed evoluzioni. - Regole chiare: usa un linguaggio semplice e conciso nel data contract per evitare fraintendimenti. Il contratto dovrebbe essere scritto e nominato in modo accessibile e comprensibile a tutte le parti coinvolte, a prescindere dal loro livello tecnico.
- Collaborazione: lo sforzo collaborativo garantisce una comprensione completa dei requisiti, coprendo prospettive e bisogni diversi. Quando crei data contract, coinvolgi stakeholder eterogenei, inclusi data producer, data engineer, data scientist e rappresentanti di business, IT, legale e compliance.
- Metadati: fornisci ai data contract documentazione e metadati accurati. Una documentazione completa facilita l'applicazione e la manutenzione del contratto. Dovrebbe includere descrizioni dettagliate, definizioni dei campi, regole di validazione e altre informazioni rilevanti per supportare una chiara comprensione e un'implementazione efficace.
- Revisioni regolari: un riesame continuo aiuta a mantenere rilevanza ed efficacia del contratto nel tempo. Implementa un approccio strutturato per il monitoraggio e gli aggiornamenti dei data contract. Revisioni regolari assicurano che il contratto resti aggiornato, allineato ai requisiti di business in evoluzione e conforme a nuove o mutate normative.
Conclusione
Di recente si è verificato un passaggio verso una proprietà distribuita dei dati, dove i team di dominio sono responsabili dei propri data product. Questo cambiamento ha costretto le organizzazioni a ridefinire le aspettative sulla qualità, formalizzate tramite i data contract.
Le validazioni semantiche assicurano che i dati siano logicamente coerenti e allineati alla logica di business. Aiutano a controllare le pipeline per eventuali outlier in termini di dati, valori, lineage e integrità referenziale. Un'integrità referenziale inadeguata può portare a dati mancanti o incompleti, per questo è importante.
I data governance contract possono essere implementati anche nelle pipeline CI/CD e risultano molto utili quando indicano colonne con dati sensibili, metadati (proprietari dei dati, ecc.) e ruoli utente autorizzati ad accedere a un data product.
I metadati di un modello, insieme a eventuali altri campi meta obbligatori definiti dallo sviluppatore, aiutano a monitorare l'uso delle risorse e le prestazioni del modello.
I Service Level Agreement (SLA) all'interno dei data contract definiscono impegni specifici relativi a freschezza, completezza e ripristino in caso di errore.
I data contract sono fondamentali per le tecniche moderne di modellazione dei dati e aiutano a garantire che la piattaforma sia resiliente agli errori e si scali bene. Risolvere problemi di qualità può essere costoso. Per le aziende enterprise, massimizzare il ritorno sull'investimento (ROI) dai dati diventa sempre più importante sfruttando gli strumenti di qualità esistenti che supportano la validazione dei data contract.
FAQ
In cosa differiscono i data contract dalla validazione e dal testing dei dati?
I data contract sono accordi formali che specificano formato, schema e protocolli di scambio tra producer e consumer di dati. Stabiliscono aspettative e responsabilità chiare sulla qualità dei dati. La validazione e il testing dei dati, invece, sono processi che verificano se i dati rispettano tali aspettative. La validazione assicura la conformità al contratto definito, mentre il testing valuta accuratezza e affidabilità dei dati.
I data contract possono essere usati nelle pipeline di streaming in tempo reale?
Sì, i data contract possono essere utilizzati nelle pipeline di streaming in tempo reale. Aiutano a filtrare i dati non validi prima che raggiungano la destinazione, assicurando che vengano elaborati solo dati conformi a regole e formati predefiniti. Questo approccio è utile nei processi event-driven o real-time, dove integrità e tempestività dei dati sono critiche.
Come gestisci il versioning nei data contract quando gli schemi cambiano spesso?
Gestire il versioning nei data contract significa mantenere la compatibilità all'indietro e tracciare l'evoluzione degli schemi. Un approccio è usare schemi versionati, dove ogni versione del contratto è associata a una specifica versione di schema. Strumenti come on_schema_change: append in dbt aiutano a gestire i cambiamenti di schema senza interrompere le integrazioni esistenti, consentendo transizioni e aggiornamenti graduali.
Che ruolo hanno i data contract in un'architettura data mesh?
In un'architettura data mesh, la proprietà dei dati è distribuita tra i team di dominio; i data contract assicurano qualità e interoperabilità coerenti tra domini. Formalizzano le aspettative tra producer e consumer, aiutando ad allineare i team sugli standard dei dati e riducendo il rischio di problemi di qualità quando i dati fluiscono tra domini diversi.
Come possono i data contract aiutare a rispettare normative sulla privacy come GDPR o HIPAA?
I data contract possono far rispettare politiche di data governance, incluse normative sulla privacy come GDPR o HIPAA. Possono definire regole per data masking, pseudonimizzazione e controlli di accesso, assicurando che i dati sensibili siano gestiti correttamente. Integrando tali regole nel contratto, le organizzazioni possono automatizzare i controlli di conformità e ridurre il rischio di violazioni della privacy.
Appassionato e con una forte attenzione al digitale, do il meglio di me nelle sfide del digital marketing.
Prima di trasferirmi nel Regno Unito, ho maturato oltre dieci anni di esperienza in vendite, rischio nel corporate banking e digital marketing, sviluppando competenze in risk management, modellazione matematica, analisi statistica, amministrazione aziendale e marketing.
Dopo aver completato il mio MBA a Newcastle, ora sono desideroso di intraprendere una carriera nel marketing data-driven, nell'informatica o nell'AI, con la possibilità di progredire fino a un dottorato. Questi ambiti offrono l'applicazione pratica della scienza, sviluppo professionale continuo, innovazione e l'opportunità di contribuire a un settore dinamico.


