La plataforma de dades d'AlpinaShop funciona. Les comandes entren soles, Dataflow les neteja, Spark calcula la cistella mitjana, Data Fusion porta el fitxer del transportista i Workflows ho coordina tot cada nit amb la seva comprovació de qualitat. Tècnicament és un èxit.

I tanmateix, tres coses continuen sense funcionar.

Ningú no sap què hi ha. A alpinashop_analitica hi ha setze taules, sis vistes, dues vistes materialitzades i una anomenada pedidos_evento que ningú no recorda per què existeix. Quan algú de màrqueting pregunta "on és la dada de conversió?", la resposta és "pregunta-ho a la Lucía". I si la Lucía se'n va de vacances, la resposta és "no ho sé".

Ningú no sap què significa. La columna ventas_eur apareix en quatre taules. Inclou IVA? Descompta devolucions? Compta les comandes cancel·lades? La Marta creu que sí, la Lucía creu que no, i l'informe que va a direcció fa servir una de les dues definicions sense dir quina. Ningú no ho ha escrit mai.

Ningú no sap qui pot veure què. A 04-01 es va protegir email_cliente amb una etiqueta de política. Però des de llavors han passat sis lliçons: Dataflow escriu a pedidos_streaming, la subscripció de Pub/Sub bolca a pedidos_evento, l'arxiu d'esdeveniments va a Cloud Storage i Data Fusion va portar el CSV del transportista amb el nom del destinatari. Hi ha correus de clients en algun d'aquests llocs sense protecció? Ningú no ho ha comprovat.

I hi ha una quarta cosa que falta, la que direcció porta demanant des del mòdul 3: el tauler de control. Les dades són impecables i ningú no les veu.

En aquesta lliçó tanquem el mòdul resolent les quatre. Veuràs què és el govern de les dades i per què una pime el necessita tant com una multinacional, organitzaràs i documentaràs la plataforma amb Dataplex, definiràs regles de qualitat que es comproven soles, descobriràs i desidentificaràs les dades personals amb Sensitive Data Protection, i construiràs per fi a Looker Studio el tauler de control d'AlpinaShop, evitant l'error de permisos que converteix un informe compartit en una fuga de dades.

Contingut

  1. Què és el govern de les dades i per què també en una pime
  2. Dataplex: llacs, zones i actius
  3. El catàleg universal i la cerca de dades
  4. Etiquetes i plantilles: documentar alpinashop_analitica
  5. Perfils de dades: què hi ha realment a les teves taules
  6. Regles de qualitat i què fer quan fallen
  7. Llinatge automàtic d'extrem a extrem
  8. Sensitive Data Protection: descobrir on són les dades personals
  9. Desidentificació: emmascarament i tokenització
  10. Looker Studio: connectar i modelar
  11. El tauler de control de direcció d'AlpinaShop
  12. Cost i rendiment: extracció, consulta en directe i BI Engine
  13. Permisos: l'error clàssic de les credencials del propietari
  14. Looker i LookML: quan justifica el seu preu
  15. Checklist d'una plataforma de dades sana

  1. Què és el govern de les dades i per què també en una pime

El govern de les dades és el conjunt de pràctiques que garanteixen que les dades d'una organització siguin trobables, comprensibles, fiables, segures i gestionades al llarg de la seva vida. Cinc potes:

Pota Pregunta que respon Eina a Google Cloud
Catàleg Quines dades tenim i on són? Dataplex Universal Catalog
Llinatge D'on ve aquesta dada i què en depèn? Dataplex Lineage
Qualitat Em puc refiar d'aquest número? Dataplex Data Quality
Classificació Quines dades són sensibles i qui les veu? Sensitive Data Protection + etiquetes de política
Cicle de vida Quant de temps ho guardem i quan s'esborra? Polítiques de retenció, TTL, cicle de vida de Cloud Storage

L'objecció habitual és que això és cosa de bancs i multinacionals. És exactament al revés, i val la pena argumentar-ho:

El RGPD no distingeix per mida. Una empresa de 40 persones que tracta dades de clients europeus té les mateixes obligacions que una de 40.000: saber quines dades personals té, on són, amb quina base legal, quant de temps les conserva, i poder atendre un dret de supressió. Si no se sap on és el correu d'un client, no es pot esborrar quan ho demani.

El cost del desconeixement és proporcionalment més gran. En una empresa gran hi ha redundància: si algú se'n va, un altre ho sap. A AlpinaShop, si la Lucía se'n va, el coneixement de la plataforma se'n va amb ella. La documentació no és burocràcia, és continuïtat.

Els errors arriben abans a direcció. Amb tres persones i un tauler de control, una dada mal calculada no passa per cap filtre intermedi. Va directa a una decisió.

I el moment correcte és ara. Governar setze taules és una feina d'una tarda. Governar-ne dues-centes, després de tres anys de creixement desordenat, és un projecte de mesos. El deute de govern s'acumula amb interessos.

  1. Dataplex: llacs, zones i actius

Dataplex és la capa de gestió unificada de dades de Google Cloud. La seva idea central: les dades d'una organització estan repartides entre BigQuery, Cloud Storage i altres sistemes, i cal una capa lògica per sobre que les organitzi, les descrigui i els apliqui polítiques comunes sense moure-les.

La seva jerarquia:

Nivell Què és A AlpinaShop
Llac (lake) Domini de dades; agrupa tot el d'un àmbit alpinashop-lago-comercial
Zona (zone) Subdivisió per grau de refinament zona-cruda, zona-curada
Actiu (asset) Recurs concret: un bucket o un dataset El bucket del llac, el dataset analític

Les zones tenen dos tipus amb semàntica diferent:

  • Raw: dades en el seu format original, sense validar. Qualsevol format.
  • Curated: dades ja netes i estructurades, amb esquema. Només formats estructurats (Parquet, Avro, ORC) o taules de BigQuery. Dataplex valida que el que s'hi registra ho compleix.
flowchart TD
    L["Llac: alpinashop-lago-comercial"]
    Z1["Zona RAW: zona-cruda<br/>dades tal com arriben"]
    Z2["Zona CURATED: zona-curada<br/>dades netes i modelades"]
    A1["Actiu: gs://alpinashop-datalake<br/>esdeveniments, exportacions, quarantena"]
    A2["Actiu: gs://alpinashop-catalogo<br/>imatges i exportacions"]
    A3["Actiu: BQ alpinashop_analitica<br/>pedidos, lineas, productos, visitas"]
    A4["Actiu: gs://alpinashop-datalake/resultados<br/>Parquet de Spark"]

    L --> Z1 --> A1
    Z1 --> A2
    L --> Z2 --> A3
    Z2 --> A4

Creació:

gcloud config set project alpinashop-datos
gcloud services enable dataplex.googleapis.com \
  datacatalog.googleapis.com datalineage.googleapis.com

# 1) El llac: el domini de dades comercial d AlpinaShop
gcloud dataplex lakes create alpinashop-lago-comercial \
  --location=europe-west1 \
  --display-name="Llac comercial d AlpinaShop" \
  --description="Comandes, cataleg, navegacio i logistica" \
  --labels=entorno=produccion,equipo=datos,centro-coste=analitica

# 2) Zona crua: el que arriba tal qual
gcloud dataplex zones create zona-cruda \
  --location=europe-west1 --lake=alpinashop-lago-comercial \
  --type=RAW \
  --resource-location-type=SINGLE_REGION \
  --display-name="Dades en cru" \
  --discovery-enabled \
  --discovery-schedule="0 3 * * *"

# 3) Zona curada: el que ja esta net i modelat
gcloud dataplex zones create zona-curada \
  --location=europe-west1 --lake=alpinashop-lago-comercial \
  --type=CURATED \
  --resource-location-type=SINGLE_REGION \
  --display-name="Dades curades" \
  --discovery-enabled \
  --discovery-schedule="0 4 * * *"

# 4) Actius
gcloud dataplex assets create activo-datalake \
  --location=europe-west1 --lake=alpinashop-lago-comercial --zone=zona-cruda \
  --resource-type=STORAGE_BUCKET \
  --resource-name=projects/alpinashop-datos/buckets/alpinashop-datalake \
  --discovery-enabled

gcloud dataplex assets create activo-analitica \
  --location=europe-west1 --lake=alpinashop-lago-comercial --zone=zona-curada \
  --resource-type=BIGQUERY_DATASET \
  --resource-name=projects/alpinashop-datos/datasets/alpinashop_analitica \
  --discovery-enabled

L'opció --discovery-enabled és la que aporta valor immediat: Dataplex rastreja automàticament els actius, infereix esquemes dels fitxers del bucket, detecta particions per l'estructura de carpetes, i crea taules externes de BigQuery perquè aquests fitxers siguin consultables amb SQL sense que ningú les declari.

Per a AlpinaShop això significa que els Parquet que la feina de Spark deixa a gs://alpinashop-datalake/resultados/cesta/ apareixen com a taula consultable sense feina addicional. És descobriment automàtic de dades, i és la raó pràctica de registrar els buckets encara que el govern formal no interessi.

  1. El catàleg universal i la cerca de dades

L'Universal Catalog de Dataplex —hereu de Data Catalog— indexa automàticament les metadades de BigQuery, Cloud Storage, Pub/Sub, Spanner i Bigtable de tota l'organització. I sobre aquest índex hi ha un cercador.

La sintaxi de cerca:

# Tot el que esmenti 'pedido'
pedido

# Nomes taules de BigQuery
type=TABLE system=BIGQUERY pedido

# Per columna: on hi ha un camp que es digui email
column:email

# Per etiqueta de negoci
tag:sensibilidad.nivel=alto

# Per projecte i dataset
parent:alpinashop-datos.alpinashop_analitica

# Combinat: taules amb columna d import al dataset analitic
type=TABLE parent:alpinashop_analitica column:importe

Des de la línia de comandes:

# Cercar totes les columnes anomenades 'email' a l organitzacio
gcloud data-catalog search "column:email" \
  --include-project-ids=alpinashop-datos,alpinashop-prod \
  --format="table(relativeResourceName, searchResultSubtype)"

Aquesta consulta respon en segons a una pregunta que sense catàleg requereix obrir taula per taula: on hi ha correus? És el primer pas de l'apartat 8.

La resposta a la pregunta de màrqueting —"on és la dada de conversió?"— passa de "pregunta-ho a la Lucía" a buscar conversion al catàleg i trobar agg_embudo_dia amb la seva descripció. Aquest canvi és tot el valor del catàleg.

  1. Etiquetes i plantilles: documentar alpinashop_analitica

El catàleg indexa el que existeix, però no sap què significa. Per a això hi ha les etiquetes (tags), que afegeixen metadades de negoci a taules i columnes, estructurades segons una plantilla.

Primer la plantilla, que defineix quins camps es documenten i amb quins valors permesos:

cat > plantilla-gobierno.json <<'EOF'
{
  "displayName": "Govern de les dades AlpinaShop",
  "fields": {
    "propietario": {
      "displayName": "Propietari de la dada",
      "type": {"primitiveType": "STRING"},
      "isRequired": true,
      "description": "Persona o equip responsable d aquest conjunt de dades"
    },
    "dominio": {
      "displayName": "Domini de negoci",
      "type": {"enumType": {"allowedValues": [
        {"displayName": "ventas"},
        {"displayName": "catalogo"},
        {"displayName": "logistica"},
        {"displayName": "marketing"},
        {"displayName": "finanzas"}
      ]}},
      "isRequired": true
    },
    "sensibilidad": {
      "displayName": "Nivell de sensibilitat",
      "type": {"enumType": {"allowedValues": [
        {"displayName": "publico"},
        {"displayName": "interno"},
        {"displayName": "confidencial"},
        {"displayName": "datos-personales"}
      ]}},
      "isRequired": true
    },
    "frescura_esperada": {
      "displayName": "Frescor esperada",
      "type": {"enumType": {"allowedValues": [
        {"displayName": "tiempo-real"},
        {"displayName": "diaria"},
        {"displayName": "semanal"},
        {"displayName": "mensual"}
      ]}},
      "isRequired": true
    },
    "retencion_meses": {
      "displayName": "Retencio en mesos",
      "type": {"primitiveType": "DOUBLE"},
      "isRequired": true
    },
    "apto_para_informes": {
      "displayName": "Apte per a informes de direccio",
      "type": {"primitiveType": "BOOL"},
      "isRequired": true,
      "description": "Fals en taules de staging, temporals o experimentals"
    },
    "definicion_negocio": {
      "displayName": "Definicio de negoci",
      "type": {"primitiveType": "STRING"},
      "isRequired": false,
      "description": "Que significa exactament aquesta dada, en llenguatge de negoci"
    }
  }
}
EOF

gcloud data-catalog tag-templates create gobierno_alpinashop \
  --location=europe-west1 \
  --display-name="Govern de les dades AlpinaShop" \
  --field-file=plantilla-gobierno.json

I ara s'etiqueta cada taula:

# Taula de comandes: dada personal, diaria, apta per a informes
gcloud data-catalog tags create \
  --entry-group=@bigquery \
  --entry="//bigquery.googleapis.com/projects/alpinashop-datos/datasets/alpinashop_analitica/tables/pedidos" \
  --tag-template=gobierno_alpinashop \
  --tag-template-location=europe-west1 \
  --tag-file=- <<'EOF'
{
  "propietario": "[email protected]",
  "dominio": "ventas",
  "sensibilidad": "datos-personales",
  "frescura_esperada": "diaria",
  "retencion_meses": 84,
  "apto_para_informes": true,
  "definicion_negocio": "Capcalera de comanda confirmada. total_pedido INCLOU IVA i despeses d enviament, i NO descompta devolucions posteriors; per a vendes netes fer servir agg_ventas_categoria_dia."
}
EOF

Aquest camp definicion_negocio és la solució al segon problema de l'inici de la lliçó. Una frase escrita una vegada elimina per sempre la discussió sobre si total_pedido porta IVA. I queda on es busca, no en un document que ningú no obre.

La disciplina d'etiquetatge que AlpinaShop adopta:

Taula Sensibilitat Apta per a informes Nota
pedidos datos-personales Conté email_cliente
lineas_pedido interno Sense dades personals
productos confidencial coste_compra és marge comercial
visitas datos-personales cliente_id és pseudònim
pedidos_staging interno No Taula de treball del DAG
pedidos_evento datos-personales No Bolcat cru de Pub/Sub
agg_* interno Agregats, sense identificació

La columna apta per a informes és la més útil en el dia a dia: distingeix el que pot alimentar un tauler de control del que és lampisteria. Sense ella, algú acabarà construint un informe de direcció sobre pedidos_staging, que es buida cada nit.

  1. Perfils de dades: què hi ha realment a les teves taules

Abans de definir regles de qualitat cal saber com són les dades. El perfilatge de Dataplex analitza una taula i produeix estadístiques per columna: valors nuls, diferents, mínims, màxims, mitjanes, percentils i els valors més freqüents.

gcloud dataplex datascans create data-profile perfil-pedidos \
  --location=europe-west1 \
  --data-source-resource="//bigquery.googleapis.com/projects/alpinashop-datos/datasets/alpinashop_analitica/tables/pedidos" \
  --display-name="Perfil de la taula de comandes" \
  --on-demand \
  --data-profile-spec-sampling-percent=100

gcloud dataplex datascans run perfil-pedidos --location=europe-west1
gcloud dataplex datascans describe perfil-pedidos --location=europe-west1 --view=FULL

Un perfil típic revela coses que ningú no sospitava:

Columna Nuls Diferents Troballa
pedido_id 0 % 100 % Correcte: identificador únic
cliente_id 12 % 8.400 Compres com a convidat. Se sabia?
email_cliente 0 % 8.900 Dada personal a totes les files
canal 0 % 5 Hi ha 5 valors, no 3: apareixen WEB i Web
estado 0 % 6 Correcte
total_pedido 0 % mín. -45,00; màx. 4.120,00
envio.pais 3 % 14 Hi ha comandes sense país

Tres troballes accionables en una sola passada: el 12 % de compres sense client identificat (que pot ser normal o pot ser una fallada del registre), la inconsistència de majúscules a canal que trenca qualsevol GROUP BY, i un total_pedido negatiu, que probablement sigui un abonament mal modelat i que està distorsionant totes les mitjanes.

Aquest últim és l'exemple perfecte de per què el perfilatge va abans que les regles: la regla "total mai negatiu" no s'inventa, es descobreix.

  1. Regles de qualitat i què fer quan fallen

Amb el perfil a la mà es defineixen les regles. Un escaneig de qualitat executa un conjunt de comprovacions i dóna un resultat d'aprovat o fallit amb el detall per regla.

# calidad-pedidos.yaml
rules:
  # --- Completesa ---
  - column: pedido_id
    nonNullExpectation: {}
    dimension: COMPLETENESS
    threshold: 1.0                  # el 100 % de les files ho ha de complir
    name: pedido-id-obligatorio
    description: "Tota comanda ha de tenir identificador"

  - column: fecha_pedido
    nonNullExpectation: {}
    dimension: COMPLETENESS
    threshold: 1.0
    name: fecha-obligatoria

  # --- Unicitat ---
  - column: pedido_id
    uniquenessExpectation: {}
    dimension: UNIQUENESS
    threshold: 1.0
    name: pedido-id-unico
    description: "Detecta duplicats per reprocessos mal fets"

  # --- Validesa de rang ---
  - column: total_pedido
    rangeExpectation:
      minValue: "0"
      maxValue: "10000"
      strictMinEnabled: false
    dimension: VALIDITY
    threshold: 0.999                # es tolera un 0,1 % d excepcions
    name: total-en-rango
    description: "Imports no negatius i per sota de 10.000 EUR"

  # --- Conjunt de valors permesos ---
  - column: estado
    setExpectation:
      values: ["confirmado", "enviado", "entregado", "devuelto", "cancelado"]
    dimension: VALIDITY
    threshold: 1.0
    name: estado-valido

  - column: canal
    setExpectation:
      values: ["web", "movil", "telefono"]
    dimension: VALIDITY
    threshold: 1.0
    name: canal-normalizado
    description: "Detecta 'WEB' i 'Web' sense normalitzar"

  # --- Format amb expressio regular ---
  - column: email_cliente
    regexExpectation:
      regex: "^[^@\\s]+@[^@\\s]+\\.[a-zA-Z]{2,}$"
    dimension: VALIDITY
    threshold: 0.99
    name: email-con-formato-valido
    ignoreNull: true

  # --- Regla SQL a nivell de fila ---
  - sqlAssertion:
      sqlStatement: |
        SELECT pedido_id, subtotal, descuento, iva, total_pedido
        FROM ${data()}
        WHERE ABS(total_pedido - (subtotal - IFNULL(descuento,0)
                                  + IFNULL(iva,0))) > 0.01
    dimension: ACCURACY
    name: cuadre-de-importes
    description: "El total ha de quadrar amb els seus components (tolerancia 1 centim)"

  # --- Regla SQL agregada: frescor ---
  - sqlAssertion:
      sqlStatement: |
        SELECT 1 FROM (
          SELECT MAX(fecha_pedido) AS ultima FROM ${data()}
        ) WHERE DATE_DIFF(CURRENT_DATE(), ultima, DAY) > 2
    dimension: FRESHNESS
    name: datos-frescos
    description: "Hi ha d haver comandes dels ultims 2 dies"
gcloud dataplex datascans create data-quality calidad-pedidos \
  --location=europe-west1 \
  --data-source-resource="//bigquery.googleapis.com/projects/alpinashop-datos/datasets/alpinashop_analitica/tables/pedidos" \
  --display-name="Qualitat de la taula de comandes" \
  --data-quality-spec-file=calidad-pedidos.yaml \
  --schedule="0 6 * * *" \
  --export-results-table="//bigquery.googleapis.com/projects/alpinashop-datos/datasets/alpinashop_analitica/tables/resultados_calidad"

Les sis dimensions de qualitat estàndard, amb la seva interpretació a AlpinaShop:

Dimensió Pregunta Exemple
Completesa Falta alguna cosa? Comandes sense sku
Unicitat Hi ha duplicats? El mateix pedido_id dues vegades després d'un reprocés
Validesa Els valors són possibles? estado fora de la llista, import negatiu
Exactitud Quadren entre si? total ≠ subtotal - descuento + iva
Consistència Coincideixen entre sistemes? Comandes a BigQuery davant de Cloud SQL
Frescor Estan actualitzades? Sense comandes noves des de fa 3 dies

Què fer quan una regla falla. Aquesta és la part que més es descuida, i sense ella l'escaneig és decoratiu:

Gravetat Exemple Acció
Crítica Duplicats, frescor trencada Bloquejar: el tauler de control no s'ha d'actualitzar amb aquestes dades
Alta Imports que no quadren Alerta immediata al propietari, investigació en el dia
Mitjana canal sense normalitzar Tiquet de correcció a l'origen; el pipeline ho normalitza mentrestant
Baixa Un 0,3 % de correus amb format estrany Informe setmanal; pot ser acceptable

La integració amb l'orquestrador de 04-06 tanca el cercle:

from airflow.providers.google.cloud.operators.dataplex import (
    DataplexRunDataQualityScanOperator,
)

qualitat = DataplexRunDataQualityScanOperator(
    task_id="escaneo_calidad_pedidos",
    project_id="alpinashop-datos",
    region="europe-west1",
    data_scan_id="calidad-pedidos",
    asynchronous=False,
    fail_on_dq_failure=True,   # si falla la qualitat, FALLA EL DAG
)

consolidar_comandes >> qualitat >> llancar_dataflow

Amb fail_on_dq_failure=True, una fallada de qualitat atura el procés nocturn abans de propagar dades dolentes al tauler de control. És la versió industrialitzada de la comprovació de qualitat que vam escriure a mà a 04-06.

  1. Llinatge automàtic d'extrem a extrem

A 04-05 vam veure el llinatge de Data Fusion, limitat als seus pipelines. Dataplex Lineage ho fa de manera transversal: captura automàticament el llinatge de BigQuery (cada consulta que escriu en una taula), Dataflow, Data Fusion, Composer i Dataproc.

gcloud services enable datalineage.googleapis.com

I ja està: a partir d'aquí, cada feina registra el seu llinatge sola.

El graf complet d'AlpinaShop, que Dataplex construeix sense que ningú el dibuixi:

flowchart LR
    CS["Cloud SQL<br/>alpinashop-pedidos"]
    GCS["gs://alpinashop-datalake<br/>exportacions"]
    PS["Pub/Sub<br/>pedidos-nuevos"]
    ERP["MySQL ERP<br/>Sabadell"]
    ST["BQ pedidos_staging"]
    PE["BQ pedidos"]
    LP["BQ lineas_pedido"]
    CE["BQ costes_producto_erp"]
    AG["BQ agg_ventas_categoria_dia"]
    MV["MV mv_ventas_diarias_sku"]
    LS["Looker Studio<br/>Tauler de control"]

    CS -->|export| GCS -->|GCSToBigQuery| ST -->|MERGE| PE
    PS -->|Dataflow| PE
    PS -->|subscripcio BQ| PE
    ERP -->|Datastream| CE
    PE --> LP
    LP --> AG
    CE --> AG
    AG --> MV --> LS

Les preguntes que aquest graf respon en un clic:

  • "El marge de l'informe surt estrany. D'on ve?"agg_ventas_categoria_dialineas_pedido + costes_producto_erp ← Datastream ← MySQL de l'ERP. En quatre salts s'arriba a l'origen.
  • "Canviarem l'esquema de l'ERP. Què es trenca?" → Tot el que penja de costes_producto_erp, inclòs el tauler de control de direcció.
  • "Un client exerceix el seu dret de supressió. On és la seva dada?" → Es rastreja pedidos cap endavant i apareixen totes les taules derivades.
  • "Podem apagar l'exportació nocturna de Cloud SQL?" → El graf mostra que pedidos_staging en depèn. No.

Aquesta última pregunta és la que més diners estalvia a la pràctica: saber què es pot apagar. Tota plataforma de dades amb dos anys acumula processos que ja no alimenten res, i sense llinatge ningú no s'atreveix a tocar-los.

  1. Sensitive Data Protection: descobrir on són les dades personals

Avís de RGPD. Aquest apartat i el següent tracten dades personals. Les eines que s'hi descriuen són controls tècnics, no un dictamen jurídic. Determinar la base legal d'un tractament, els terminis de conservació, la necessitat d'una avaluació d'impacte o si una pseudonimització és suficient correspon a un professional de compliance o al delegat de protecció de dades, i s'ha de fer abans de posar res en producció. Totes les dades d'aquest curs són fictícies.

Sensitive Data Protection —el servei abans conegut com a Cloud DLP— fa dues coses: inspeccionar per descobrir on hi ha dades sensibles, i desidentificar perquè deixin de ser-ho.

Comencem per la inspecció, que respon al tercer problema de l'inici de la lliçó: hi ha correus de clients en llocs on no n'hi hauria d'haver?

gcloud services enable dlp.googleapis.com

Una feina d'inspecció sobre tot el dataset:

{
  "inspectJob": {
    "storageConfig": {
      "bigQueryOptions": {
        "tableReference": {
          "projectId": "alpinashop-datos",
          "datasetId": "alpinashop_analitica",
          "tableId": "pedidos_evento"
        },
        "sampleMethod": "RANDOM_START",
        "rowsLimitPercent": 10
      }
    },
    "inspectConfig": {
      "infoTypes": [
        {"name": "EMAIL_ADDRESS"},
        {"name": "PHONE_NUMBER"},
        {"name": "STREET_ADDRESS"},
        {"name": "PERSON_NAME"},
        {"name": "IBAN_CODE"},
        {"name": "CREDIT_CARD_NUMBER"},
        {"name": "ES_NIF_NUMBER"},
        {"name": "IP_ADDRESS"}
      ],
      "minLikelihood": "LIKELY",
      "includeQuote": false,
      "limits": {"maxFindingsPerRequest": 1000}
    },
    "actions": [
      {
        "saveFindings": {
          "outputConfig": {
            "table": {
              "projectId": "alpinashop-datos",
              "datasetId": "alpinashop_analitica",
              "tableId": "hallazgos_dlp"
            }
          }
        }
      },
      {"publishSummaryToCscc": {}}
    ]
  }
}
curl -X POST -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  "https://dlp.googleapis.com/v2/projects/alpinashop-datos/locations/europe-west1/dlpJobs" \
  -d @inspeccion-pedidos-evento.json

Dues decisions deliberades en aquesta configuració:

  • includeQuote: false: les troballes no inclouen el valor trobat. Si ho posessis a true, la taula de troballes contindria els correus reals, és a dir, hauries creat una còpia sense protegir de les dades que intentes protegir. És un error clàssic i greu.
  • rowsLimitPercent: 10: per descobrir si hi ha dades sensibles no cal escanejar el 100 %. Un mostreig del 10 % troba el problema i costa la desena part.

I una consulta sobre les troballes:

-- On hi ha dades personals i de quin tipus
SELECT
  info_type.name                        AS tipo_dato,
  location.container_name               AS tabla,
  location.content_locations[SAFE_OFFSET(0)]
    .record_location.field_id.name      AS columna,
  likelihood,
  COUNT(*)                              AS hallazgos
FROM `alpinashop-datos.alpinashop_analitica.hallazgos_dlp`
GROUP BY tipo_dato, tabla, columna, likelihood
ORDER BY hallazgos DESC;

Resultat típic a AlpinaShop, i per què fa mal:

Tipus Taula Columna Comentari
EMAIL_ADDRESS pedidos email_cliente Esperat: ja protegit amb etiqueta de política a 04-01
EMAIL_ADDRESS pedidos_evento payload No esperat: la subscripció de Pub/Sub bolca el JSON cru
PERSON_NAME envios incidencia No esperat: el transportista escriu noms en text lliure
PHONE_NUMBER opiniones texto No esperat: clients que deixen el seu telèfon a la ressenya
IP_ADDRESS visitas dispositivo No esperat: una IP és dada personal segons el RGPD

Quatre de les cinc troballes són sorpreses. Cap no és fruit de mala fe: són conseqüències naturals de moure dades. La subscripció de BigQuery de 04-04 bolca el missatge sencer. El transportista escriu el que vol al camp d'incidència. Els clients posen el seu telèfon en una ressenya. I una IP, que sembla tècnica, és dada personal.

Això és exactament el que cap organització no sap sense inspeccionar. I és la raó per la qual aquest apartat existeix.

Per fer-ho continu, Dataplex ofereix perfilatge de dades sensibles a nivell d'organització, que escaneja automàticament tot BigQuery i manté un inventari actualitzat d'on hi ha dades sensibles i amb quin nivell de risc, sense llançar feines a mà.

  1. Desidentificació: emmascarament i tokenització

Descobert el problema, cal actuar. Les tècniques, de menor a major utilitat conservada:

Tècnica Què fa Reversible? Quan utilitzar-la
Supressió Elimina el valor No La dada no es necessita per a res
Emmascarament [email protected]***@correo.com No Es necessita el domini, no la persona
Substitució Reemplaça per una constant No Només cal saber que hi havia alguna cosa
Hash cripto HMAC-SHA256 amb clau a KMS No Comptar clients diferents sense saber qui són
Tokenització determinista (FPE) Testimoni amb el format original , amb la clau Creuar entre sistemes i poder revertir
Generalització 34 anys → 30-39; 0801308 No Anàlisi demogràfica o geogràfica
Desplaçament de dates Desplaça totes les dates d'un subjecte Sèries temporals sense dates reals

La distinció crítica és entre hash i tokenització determinista:

  • El hash amb clau produeix el mateix resultat per al mateix valor, així que permet comptar i agrupar clients diferents, però no es pot tornar enrere. És el correcte per a analítica pura.
  • La tokenització determinista amb preservació de format produeix un testimoni que sembla un correu i es pot revertir amb la clau. És el correcte quan un sistema autoritzat necessita recuperar el valor original.

Configuració de desidentificació per a AlpinaShop:

{
  "deidentifyTemplate": {
    "displayName": "Desidentificacio analitica AlpinaShop",
    "description": "Aplica a les taules exposades a analisi i a Looker Studio",
    "deidentifyConfig": {
      "recordTransformations": {
        "fieldTransformations": [
          {
            "fields": [{"name": "email_cliente"}],
            "primitiveTransformation": {
              "cryptoHashConfig": {
                "cryptoKey": {
                  "kmsWrapped": {
                    "wrappedKey": "CiQA...",
                    "cryptoKeyName": "projects/alpinashop-prod/locations/europe-west1/keyRings/alpinashop-keyring/cryptoKeys/clave-pedidos"
                  }
                }
              }
            }
          },
          {
            "fields": [{"name": "codigo_postal"}],
            "primitiveTransformation": {
              "characterMaskConfig": {
                "maskingCharacter": "X",
                "numberToMask": 3,
                "reverseOrder": true
              }
            }
          },
          {
            "fields": [{"name": "texto_opinion"}],
            "infoTypeTransformations": {
              "transformations": [
                {
                  "infoTypes": [
                    {"name": "EMAIL_ADDRESS"},
                    {"name": "PHONE_NUMBER"},
                    {"name": "PERSON_NAME"}
                  ],
                  "primitiveTransformation": {
                    "replaceWithInfoTypeConfig": {}
                  }
                }
              ]
            }
          }
        ]
      }
    }
  }
}

Què fa cada transformació:

  • email_cliente → hash amb clau embolcallada a KMS. Reutilitza clave-pedidos del keyring alpinashop-keyring creat a 03-06. La clau no surt mai de KMS, així que ni tan sols qui tingui accés a la taula pot revertir el hash. La Lucía continua podent comptar clients únics amb COUNT(DISTINCT email_hash).
  • codigo_postal → emmascarament dels 3 últims. 08013 es converteix en 08XXX. Es conserva la província, que és el que serveix per a l'anàlisi geogràfica, i es perd el carrer, que és el que identifica.
  • texto_opinion → substitució per tipus dins del text lliure. "Truqueu-me al 611223344, soc l'Ana" es converteix en "Truqueu-me al [PHONE_NUMBER], soc l'[PERSON_NAME]". La ressenya continua sent analitzable —inclosa l'anàlisi de sentiment del mòdul 5— sense dades personals a dins.

L'última és la més valuosa i la que cap altra eina no fa bé: desidentificar dins de text lliure, on la dada personal no és en una columna sinó incrustada en una frase.

I la vista que exposa la Lucía i el tauler de control:

-- Vista desidentificada: es la UNICA que es connecta a Looker Studio
CREATE OR REPLACE VIEW `alpinashop-datos.alpinashop_analitica.v_pedidos_analitica` AS
SELECT
  pedido_id,
  fecha_pedido,
  momento_pedido,
  cliente_id,
  canal,
  estado,
  -- Provincia si, adreca no
  envio.pais                                   AS pais,
  SUBSTR(envio.codigo_postal, 1, 2)            AS provincia_cp,
  metodo_pago,
  subtotal, descuento, iva, total_pedido
FROM `alpinashop-datos.alpinashop_analitica.pedidos`;
-- Sense email_cliente, sense ciutat, sense codi postal complet.
-- Es una VISTA AUTORITZADA (04-01): qui la consulta NO necessita
-- permis sobre la taula base, i per tant no se la pot saltar.

El principi operatiu: les dades personals existeixen a la taula base, protegida i amb accés restringit a gcp-seguridad@; tota la resta —analítica, taulers de control, exploració— consumeix vistes desidentificades. La minimització no és una neteja puntual, és una arquitectura.

  1. Looker Studio: connectar i modelar

Looker Studio (abans Data Studio) és l'eina gratuïta de visualització de Google. Es connecta a BigQuery i a desenes d'orígens, i permet construir informes interactius sense codi.

Hi ha tres maneres de connectar-lo a BigQuery, i triar bé determina el cost i el rendiment:

Mode Què fa Cost Quan
Taula/vista Consulta en directe contra la taula Cada interacció costa Dades que canvien i es filtren molt
Consulta personalitzada SQL propi com a origen Cada interacció executa aquest SQL Modelatge complex; compte amb el cost
Extracció Copia les dades a la memòria cau de Looker Studio Gairebé zero Volums petits, refresc diari

La regla d'or: no connectis mai Looker Studio directament a una taula de detall gran. Connecta'l a una taula agregada o a una vista materialitzada. Un tauler de control amb sis gràfics que consulta lineas_pedido executa sis consultes cada vegada que algú canvia un filtre, i amb deu usuaris refrescant, la factura de BigQuery es dispara —exactament l'escenari de l'exercici 3 de 04-01.

Per a AlpinaShop, l'origen del tauler de control serà una taula preparada pel procés nocturn:

-- Taula base del tauler de control: petita, agregada, llesta per consumir
CREATE OR REPLACE TABLE `alpinashop-datos.alpinashop_analitica.bi_resumen_diario`
PARTITION BY dia
CLUSTER BY canal, pais AS
SELECT
  p.fecha_pedido                                    AS dia,
  p.canal,
  p.envio.pais                                      AS pais,
  pr.categoria,
  COUNT(DISTINCT p.pedido_id)                       AS pedidos,
  COUNT(DISTINCT p.cliente_id)                      AS clientes,
  SUM(l.cantidad)                                   AS unidades,
  ROUND(SUM(l.importe_linea), 2)                    AS ventas_eur,
  ROUND(SUM(l.cantidad * c.coste_medio_eur), 2)     AS coste_eur,
  COUNTIF(p.estado = 'devuelto')                    AS pedidos_devueltos
FROM `alpinashop-datos.alpinashop_analitica.pedidos`               AS p
JOIN `alpinashop-datos.alpinashop_analitica.lineas_pedido`         AS l
  ON l.pedido_id = p.pedido_id AND l.fecha_pedido = p.fecha_pedido
JOIN `alpinashop-datos.alpinashop_analitica.productos`             AS pr USING (sku)
LEFT JOIN `alpinashop-datos.alpinashop_analitica.costes_producto_erp` AS c USING (sku)
WHERE p.fecha_pedido >= DATE_SUB(CURRENT_DATE(), INTERVAL 800 DAY)
  AND p.estado NOT IN ('cancelado')
GROUP BY dia, p.canal, pais, pr.categoria;

Aquesta taula té uns pocs milers de files —dos anys × 3 canals × 14 països × 6 categories— davant dels milions de lineas_pedido. Consultar-la és pràcticament gratis.

Els camps calculats de Looker Studio es defineixen sobre l'origen i es comporten com a columnes:

# Tiquet mitja
SUM(ventas_eur) / NULLIF(SUM(pedidos), 0)

# Marge brut en euros
SUM(ventas_eur) - SUM(coste_eur)

# Marge percentual
100 * (SUM(ventas_eur) - SUM(coste_eur)) / NULLIF(SUM(ventas_eur), 0)

# Taxa de devolucio
100 * SUM(pedidos_devueltos) / NULLIF(SUM(pedidos), 0)

# Agrupacio de canals per a una vista simplificada
CASE
  WHEN canal IN ('web','movil') THEN 'Digital'
  ELSE 'Altres'
END

Consell de modelatge: defineix els camps calculats a la vista de BigQuery, no a Looker Studio, sempre que puguis. Un camp definit en SQL està versionat a Git, és reutilitzable per altres informes i per Spark, i no es perd si algú duplica l'informe. Un camp definit a Looker Studio viu dins d'aquest informe i ningú més no el veu.

  1. El tauler de control de direcció d'AlpinaShop

Dissenyem l'informe que direcció porta demanant des del mòdul 3.

Estructura en tres pàgines:

Pagina 1 — RESUM EXECUTIU
+----------------------------------------------------------+
|  [Selector de dates]  [Canal]  [Pais]  [Categoria]       |
+----------------------------------------------------------+
| Vendes mes  | Comandes  | Tiquet mitja | Marge %         |
| 42.180 EUR  |    387    |  108,99 EUR  |   38,2 %        |
| ^ +12,4 %   | ^ +8,1 %  |  ^ +4,0 %    |  v -1,2 pp      |
+----------------------------------------------------------+
| Evolucio de vendes (linia, 13 mesos, amb any anterior)   |
+----------------------------------------------------------+
| Top 10 productes (barres)  | Vendes per categoria (anell)|
+----------------------------------------------------------+

Pagina 2 — EMBUT I CONVERSIO
+----------------------------------------------------------+
| Embut: sessions > vist > cistella > pagament > compra    |
+----------------------------------------------------------+
| Taxa de conversio (linia)  | Cost d adquisicio (linia)   |
+----------------------------------------------------------+
| Conversio per canal i dispositiu (taula amb barres)      |
+----------------------------------------------------------+

Pagina 3 — PRODUCTE I LOGISTICA
+----------------------------------------------------------+
| Marge per categoria (barres) | Productes sense vendes    |
+----------------------------------------------------------+
| Taxa de devolucio per categoria | Termini lliurament     |
+----------------------------------------------------------+

Els indicadors clau, amb la seva definició explícita —que és el que evita la discussió de "aquest número no és el que jo tenia":

Indicador Fórmula Definició que cal escriure a l'informe
Vendes del mes SUM(ventas_eur) Import de línies de comanda, IVA inclòs, sense comandes cancel·lades
Evolució Comparació amb el període anterior Mateix nombre de dies, no mes natural complet
Tiquet mitjà ventas / pedidos Per comanda, no per línia
Top de productes SUM(unidades) per SKU Per unitats venudes, no per import
Taxa de conversió compras / sesiones Sessions amb activitat, no visites totals
Cost d'adquisició inversió / clients nous Requereix la dada d'inversió en màrqueting
Marge (ventas - coste) / ventas Cost de l'ERP; sense despeses generals ni logística

El cost d'adquisició mereix una nota honesta: requereix una dada que AlpinaShop encara no té a la plataforma —la inversió mensual en publicitat—. Hi ha dues opcions: carregar-la com una taula manual mantinguda per màrqueting, o integrar-la des de l'API de la plataforma publicitària. La primera és la sensata per començar. I cal mostrar-la a l'informe indicant d'on surt, perquè un indicador l'origen del qual ningú no coneix acaba desacreditant la resta.

Bones pràctiques de disseny, que són les que separen un informe que s'utilitza d'un que s'obre una vegada:

  • El més important a dalt a l'esquerra. És on va la vista.
  • Quatre targetes d'indicador, no dotze. Un tauler de control amb vint xifres no es llegeix: s'ignora.
  • Comparació sempre. Un número sense context no informa. 42.180 € no diu res; +12,4 % davant del mes anterior sí.
  • Un tipus de gràfic per pregunta: línia per a evolució, barres per comparar categories, taula per al detall. Res de gràfics de pastís amb dotze porcions.
  • Filtres a dalt i comuns a la pàgina, no repartits pels gràfics.
  • Colors amb significat i accessibles: verd i vermell tenen sentit per a bo i dolent, però cal assegurar-se que es distingeixen també per a daltònics, afegint fletxes o signes a més del color.
  • Data de darrera actualització visible. Un informe sense ella genera desconfiança i trucades.
  • Una nota metodològica al peu. On es defineix cada indicador. És la diferència entre un informe i una discussió.

  1. Cost i rendiment: extracció, consulta en directe i BI Engine

Els tres mecanismes, en ordre de cost creixent:

1. Extracció de dades. Looker Studio copia fins a un límit de dades a la seva pròpia memòria cau i les serveix des d'allà. Refresc programable de fins a una vegada al dia. Cost de BigQuery pràcticament nul, rendiment excel·lent. És l'opció per defecte per a un informe amb dades agregades que no necessita ser d'avui mateix.

2. Consulta en directe amb memòria cau. Looker Studio guarda a la memòria cau els resultats durant un temps configurable (fins a 12 hores). Amb la memòria cau activada, deu persones mirant el mateix informe generen una consulta, no deu. És el que s'ha d'utilitzar en un tauler de control compartit.

3. BI Engine. Un servei d'acceleració en memòria de BigQuery. Es reserva una capacitat i les consultes que hi caben es responen en mil·lisegons i sense cost de bytes escanejats.

# Reserva de BI Engine de 2 GB per al tauler de control
bq update --reservation --project_id=alpinashop-datos \
  --location=europe-west1 \
  --bi_reservation_size=2147483648

Amb la taula bi_resumen_diario, que ocupa uns pocs megabytes, 2 GB de BI Engine hi caben de sobres: el tauler de control sencer se serveix des de memòria, amb latència de mil·lisegons i sense facturar escaneig. El cost de la reserva és de l'ordre d'uns pocs euros al mes, i sol sortir més barat que pagar les consultes que evita.

Comprovar que BI Engine s'està utilitzant:

SELECT
  job_id,
  bi_engine_statistics.bi_engine_mode          AS modo,
  ROUND(total_bytes_billed / POW(1024,2), 2)   AS mb_facturados,
  TIMESTAMP_DIFF(end_time, start_time, MILLISECOND) AS ms
FROM `region-europe-west1`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
  AND job_type = 'QUERY'
  AND bi_engine_statistics IS NOT NULL
ORDER BY creation_time DESC
LIMIT 20;

Si modo és FULL, la consulta es va resoldre íntegrament en memòria. Si és DISABLED o PARTIAL, el camp bi_engine_statistics.acceleration_mode explica per què —normalment perquè la consulta utilitza una funció no admesa o la taula no hi cap.

La recepta completa d'AlpinaShop, resumida: taula agregada petita + memòria cau activada + BI Engine + informe connectat a la vista desidentificada. Cost mensual del tauler de control: uns pocs euros. Sense aquesta disciplina, els mateixos gràfics costarien centenars.

  1. Permisos: l'error clàssic de les credencials del propietari

Aquí hi ha l'error més perillós de tota la lliçó, i passa constantment perquè la interfície ho posa fàcil.

Quan es crea un origen de dades a Looker Studio cal triar amb quines credencials s'executen les consultes:

Mode Com funciona Conseqüència
Credencials del propietari Totes les consultes utilitzen els permisos de qui va crear l'origen Qui veu l'informe accedeix a les dades amb els permisos del propietari, encara que ell no en tingui cap
Credencials de l'espectador Cada consulta utilitza els permisos de qui mira Cada persona veu el que els seus permisos de BigQuery li permeten

L'escenari del desastre, pas a pas:

  1. La Lucía crea el tauler de control connectat a BigQuery amb credencials del propietari. És el còmode, i és el que la interfície suggereix.
  2. La Lucía té, a més d'accés als agregats, permís de lectura sobre pedidos complet.
  3. La Lucía comparteix l'informe amb "qualsevol persona amb l'enllaç" perquè un proveïdor extern vegi un gràfic.
  4. Aquest proveïdor —o qualsevol a qui reenviïn l'enllaç— pot explorar les dades amb els permisos de la Lucía. Pot afegir camps, canviar dimensions i, si l'origen és una taula amb dades personals, veure-les.
  5. Ningú a BigQuery no ha donat cap permís a aquesta persona. Els permisos de BigQuery no la protegeixen, perquè les consultes no es fan en nom seu.

Això és una bretxa de dades personals amb obligació de notificació sota el RGPD.

Les regles que AlpinaShop adopta, sense excepció:

  1. L'origen apunta sempre a una vista desidentificada i autoritzada, mai a una taula amb dades personals. Encara que el mode de credencials sigui l'equivocat, no hi ha res sensible al darrere. Aquesta és la defensa que no depèn que ningú se'n recordi.
  2. Credencials de l'espectador en qualsevol informe que surti de l'equip de dades.
  3. Mai "qualsevol persona amb l'enllaç" per a informes amb dades de negoci. Compartir sempre amb persones o grups concrets (gcp-datos@, direccion@).
  4. Revisió trimestral de amb qui estan compartits els informes. Els permisos s'acumulen sols.
  5. Si calen credencials del propietari —perquè els espectadors no tenen ni han de tenir permisos de BigQuery—, aleshores l'origen ha de ser una vista agregada sense cap dada personal ni confidencial. Sense negociació.

Verificació des de BigQuery de qui està consultant des de Looker Studio:

SELECT
  user_email,
  COUNT(*)                                      AS consultas,
  ROUND(SUM(total_bytes_billed)/POW(1024,3), 2) AS gb,
  MIN(creation_time)                            AS primera,
  MAX(creation_time)                            AS ultima
FROM `region-europe-west1`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
  AND job_type = 'QUERY'
  AND (labels.requestor = 'looker_studio'
       OR STRPOS(IFNULL(query, ''), 'Looker Studio') > 0)
GROUP BY user_email
ORDER BY consultas DESC;

Si en aquesta llista apareix una sola adreça de correu —la de la Lucía— quan l'informe el miren quinze persones, ets en mode credencials del propietari. És la comprovació de trenta segons que convé fer avui.

  1. Looker i LookML: quan justifica el seu preu

Looker (sense "Studio") és un producte diferent i molt més car. La seva diferència essencial és LookML, un llenguatge de modelatge on es defineix una sola vegada què significa cada mètrica i cada dimensió, i tots els informes de l'empresa consumeixen aquestes definicions.

# modelo.lkml — la definicio viu en UN lloc, versionada a Git
view: pedidos {
  sql_table_name: `alpinashop-datos.alpinashop_analitica.bi_resumen_diario` ;;

  dimension_group: dia {
    type: time
    timeframes: [date, week, month, quarter, year]
    sql: ${TABLE}.dia ;;
  }

  dimension: canal { type: string  sql: ${TABLE}.canal ;; }

  measure: ventas_eur {
    type: sum
    sql: ${TABLE}.ventas_eur ;;
    value_format_name: eur
    description: "Import de linies de comanda, IVA inclos, sense cancel·lades"
  }

  measure: pedidos { type: sum  sql: ${TABLE}.pedidos ;; }

  measure: ticket_medio {
    type: number
    sql: ${ventas_eur} / NULLIF(${pedidos}, 0) ;;
    value_format_name: eur
    description: "Vendes dividit entre nombre de comandes"
  }
}
Criteri Looker Studio Looker
Cost Gratuït (Pro té cost) Milers de € al mes
Modelatge centralitzat No: cada informe defineix el seu Sí, LookML
Versionat a Git No Sí, natiu
Coherència de mètriques Depèn de la disciplina Garantida per disseny
Permisos per fila Via BigQuery Natiu al model
API i contingut incrustat Limitat Complet
Corba d'aprenentatge Hores Setmanes

Quan justifica el seu preu: quan el cost de la incoherència supera el cost de la llicència. És a dir, quan hi ha desenes d'analistes creant informes, quan "vendes" significa coses diferents en tres departaments i les reunions es dediquen a discutir de qui és el número bo, o quan es necessita govern del model semàntic amb revisió per pull request.

Per a AlpinaShop, Looker Studio és la resposta òbvia: tres persones, un tauler de control, pressupost de pime. La coherència es garanteix amb la disciplina de l'apartat 10 —definir les mètriques en vistes de BigQuery versionades a Git— que és el 80 % del valor de LookML pel 0 % del preu.

  1. Checklist d'una plataforma de dades sana

Organització i descobriment

  • [ ] Tots els datasets i buckets registrats en un llac de Dataplex amb les seves zones.
  • [ ] Descobriment automàtic activat als actius de Cloud Storage.
  • [ ] Tota taula amb description i amb etiquetes de govern.
  • [ ] Distinció explícita entre taules aptes per a informes i taules de lampisteria.
  • [ ] Cada taula amb un propietari nomenat.

Qualitat

  • [ ] Perfilatge executat sobre les taules principals.
  • [ ] Regles de qualitat definides per a completesa, unicitat, validesa, exactitud i frescor.
  • [ ] Escaneigs programats diàriament.
  • [ ] L'orquestrador bloqueja el procés si falla una regla crítica.
  • [ ] Resultats històrics exportats a BigQuery per veure la tendència.

Llinatge

  • [ ] API de llinatge activada.
  • [ ] Graf revisat després de cada canvi d'arquitectura.
  • [ ] Processos sense consumidors identificats i apagats.

Seguretat i privacitat

  • [ ] Inspecció de dades sensibles executada sobre tot el dataset, no només on se sospita.
  • [ ] Columnes amb dades personals etiquetades amb política d'accés.
  • [ ] Vistes desidentificades com a única superfície d'exposició analítica.
  • [ ] Cap eina de BI connectada a una taula amb dades personals.
  • [ ] Claus de hash i tokenització a KMS, mai al codi.
  • [ ] Revisió per un professional de compliance o el DPD abans de producció.

Cost

  • [ ] Taules particionades i clusteritzades; require_partition_filter a les grans.
  • [ ] Taulers de control connectats a agregats, mai a taules de detall.
  • [ ] Memòria cau d'informes activada; BI Engine si compensa.
  • [ ] Pressupostos i alertes per etiqueta de centre de cost.
  • [ ] Res permanentment encès que no ho necessiti: sense clústers de Dataproc oblidats, sense instàncies de Data Fusion enceses, sense pipelines de streaming orfes.
  • [ ] Auditoria periòdica de consultes cares amb INFORMATION_SCHEMA.

Operació

  • [ ] Tots els processos orquestrats, cap en un cron d'una VM.
  • [ ] Tasques idempotents; reprocessos segurs.
  • [ ] Alertes que arriben a una persona concreta, no a un correu genèric.
  • [ ] Codi —DDL, DAG, pipelines, fluxos— versionat a Git.
  • [ ] Documentació de què fa cada procés i a qui avisar.

Errors Habituals i Consells

Documentar només al principi. Un catàleg amb etiquetes de fa un any és pitjor que cap, perquè genera confiança injustificada. Documentar ha de ser part de crear la taula, no un projecte a part.

Posar regles de qualitat sense perfilar abans. S'acaben definint llindars inventats que fallen a diari, tothom ignora les alertes, i el sistema deixa de servir. Perfila, mira les dades reals, i després defineix.

Regles de qualitat que no bloquegen res. Si una fallada de qualitat no atura el procés ni avisa ningú, és decoració.

Inspeccionar amb includeQuote: true. Es crea una taula de troballes que conté les dades sensibles que es volien protegir. És empitjorar el problema.

Confondre hash amb anonimització. Un hash sense sal ni clau d'un correu és reversible per força bruta —l'espai de correus plausibles no és tan gran—. Continua sent dada personal a efectes del RGPD. La clau ha d'estar a KMS.

Connectar Looker Studio a una taula de detall. Cada filtre dispara consultes sobre milions de files. És el camí directe a una factura sorpresa.

Credencials del propietari més enllaç públic. L'error més greu de la lliçó. Qualsevol amb l'enllaç consulta amb els permisos del propietari.

No posar la data d'actualització a l'informe. Genera desconfiança, trucades i decisions basades en dades de la setmana passada creient que són d'avui.

Definir les mètriques a Looker Studio en comptes de a BigQuery. Queden tancades en aquest informe, no es versionen, i en duplicar-lo divergeixen.

Consell: escriu la definició de negoci de cada mètrica i publica-la. La meitat de les discussions sobre dades són discussions sobre definicions, no sobre números.

Consell: posa un enllaç al catàleg al peu del tauler de control. Qui vulgui saber què significa un número, que hi pugui arribar sol.

Consell: fes la inspecció de dades sensibles avui, no quan hi hagi un incident. Costa una tarda i gairebé sempre troba alguna cosa que ningú no sabia.

Exercicis

Exercici 1: governar la taula de ressenyes

Per a la taula alpinashop_analitica.opiniones: (a) escriu la comanda que la registra com a actiu a la zona curada del llac; (b) crea l'etiqueta de govern indicant propietari, domini, sensibilitat, frescor, retenció, aptitud per a informes i una definició de negoci precisa; (c) defineix un escaneig de qualitat amb almenys cinc regles que cobreixin completesa, unicitat, validesa de rang, conjunt de valors i una regla SQL pròpia; (d) explica quina acció prendries davant de la fallada de cada regla.

Exercici 2: inspecció i desidentificació de ressenyes

El camp texto d'opiniones és text lliure escrit per clients. Dissenya: (a) la feina d'inspecció que descobreixi quins tipus de dades personals conté, amb les opcions correctes per no crear una còpia de les dades sensibles; (b) la plantilla de desidentificació que permeti conservar el text analitzable —per a l'anàlisi de sentiment del mòdul 5— eliminant les dades personals; (c) la vista que s'exposaria a Looker Studio; (d) l'advertiment de RGPD que acompanyaria la proposta abans de portar-la a producció.

Exercici 3: redissenyar un tauler de control problemàtic

El tauler de control de direcció porta dos mesos funcionant i hi ha quatre problemes. Un: triga 40 segons a carregar i el cost de BigQuery del projecte ha passat de 12 € a 310 € al mes. Dos: INFORMATION_SCHEMA mostra que totes les consultes de l'informe les fa [email protected], encara que el miren dotze persones. Tres: l'informe està compartit com a "qualsevol persona amb l'enllaç" perquè es va haver d'ensenyar a un consultor extern. Quatre: direcció diu que el número de vendes de l'informe no coincideix amb el de comptabilitat.

Diagnostica cada problema, indica la seva gravetat relativa i proposa la solució concreta amb l'ordre d'actuació.

Solucions

Solució 1

(a) Registrar l'actiu. La taula ja és dins del dataset alpinashop_analitica, que es va registrar complet com a actiu activo-analitica a zona-curada. Per tant no cal registrar la taula individualment: Dataplex la descobreix en rastrejar el dataset. Si es volgués un actiu separat —per exemple, per a un dataset diferent— seria:

gcloud dataplex assets create activo-opiniones \
  --location=europe-west1 \
  --lake=alpinashop-lago-comercial --zone=zona-curada \
  --resource-type=BIGQUERY_DATASET \
  --resource-name=projects/alpinashop-datos/datasets/alpinashop_opiniones \
  --discovery-enabled

gcloud dataplex assets describe activo-opiniones \
  --location=europe-west1 --lake=alpinashop-lago-comercial --zone=zona-curada

(b) Etiqueta de govern.

gcloud data-catalog tags create \
  --entry-group=@bigquery \
  --entry="//bigquery.googleapis.com/projects/alpinashop-datos/datasets/alpinashop_analitica/tables/opiniones" \
  --tag-template=gobierno_alpinashop \
  --tag-template-location=europe-west1 \
  --tag-file=- <<'EOF'
{
  "propietario": "[email protected]",
  "dominio": "catalogo",
  "sensibilidad": "datos-personales",
  "frescura_esperada": "diaria",
  "retencion_meses": 36,
  "apto_para_informes": false,
  "definicion_negocio": "Ressenyes de clients sobre productes. puntuacion es de 1 a 5, entera. El camp texto es LLIURE i pot contenir dades personals incrustades (noms, telefons): NO exposar directament; fer servir la vista v_opiniones_analitica, ja desidentificada. Una ressenya correspon a un client i un SKU; un client pot opinar diverses vegades del mateix producte."
}
EOF

sensibilidad: datos-personales i apto_para_informes: false són les dues decisions clau: encara que el camp texto sembli innocu, és text lliure i per tant imprevisible.

(c) Escaneig de qualitat.

# calidad-opiniones.yaml
rules:
  - column: opinion_id
    nonNullExpectation: {}
    dimension: COMPLETENESS
    threshold: 1.0
    name: opinion-id-obligatorio

  - column: opinion_id
    uniquenessExpectation: {}
    dimension: UNIQUENESS
    threshold: 1.0
    name: opinion-id-unico

  - column: puntuacion
    rangeExpectation:
      minValue: "1"
      maxValue: "5"
    dimension: VALIDITY
    threshold: 1.0
    name: puntuacion-de-1-a-5

  - column: pais
    setExpectation:
      values: ["ES","FR","PT","IT","DE","AD","BE","NL","AT","CH","GB","IE","LU","PL"]
    dimension: VALIDITY
    threshold: 0.99
    name: pais-en-lista

  - column: sku
    nonNullExpectation: {}
    dimension: COMPLETENESS
    threshold: 1.0
    name: sku-obligatorio

  # Regla SQL: integritat referencial contra el cataleg
  - sqlAssertion:
      sqlStatement: |
        SELECT o.opinion_id, o.sku
        FROM ${data()} AS o
        LEFT JOIN `alpinashop-datos.alpinashop_analitica.productos` AS p
          ON p.sku = o.sku
        WHERE p.sku IS NULL
    dimension: CONSISTENCY
    name: sku-existe-en-catalogo

  # Regla SQL: frescor
  - sqlAssertion:
      sqlStatement: |
        SELECT 1 FROM (SELECT MAX(fecha) AS ultima FROM ${data()})
        WHERE DATE_DIFF(CURRENT_DATE(), ultima, DAY) > 7
    dimension: FRESHNESS
    name: opiniones-recientes
gcloud dataplex datascans create data-quality calidad-opiniones \
  --location=europe-west1 \
  --data-source-resource="//bigquery.googleapis.com/projects/alpinashop-datos/datasets/alpinashop_analitica/tables/opiniones" \
  --data-quality-spec-file=calidad-opiniones.yaml \
  --schedule="0 6 * * *" \
  --export-results-table="//bigquery.googleapis.com/projects/alpinashop-datos/datasets/alpinashop_analitica/tables/resultados_calidad"

(d) Acció davant de cada fallada:

Regla Gravetat Acció
opinion-id-obligatorio Crítica Bloquejar el procés. Un identificador nul trenca la idempotència del MERGE
opinion-id-unico Crítica Bloquejar i investigar: indica reprocés mal fet, exactament la fallada de 04-06
puntuacion-de-1-a-5 Alta Alerta i quarantena d'aquestes files. Una puntuació de 9 falseja totes les mitjanes
pais-en-lista Mitjana Tiquet a l'equip web. Pot ser un país nou legítim: revisar i ampliar la llista
sku-obligatorio Alta Alerta: una ressenya sense producte no és analitzable
sku-existe-en-catalogo Mitjana Investigar: producte descatalogat i esborrat del mestre, o error d'escriptura
opiniones-recientes Alta Alerta a l'equip de plataforma: probablement el pipeline d'ingesta està trencat i ningú no ho sap

Solució 2

(a) Inspecció.

{
  "inspectJob": {
    "storageConfig": {
      "bigQueryOptions": {
        "tableReference": {
          "projectId": "alpinashop-datos",
          "datasetId": "alpinashop_analitica",
          "tableId": "opiniones"
        },
        "identifyingFields": [{"name": "opinion_id"}],
        "sampleMethod": "RANDOM_START",
        "rowsLimitPercent": 20
      }
    },
    "inspectConfig": {
      "infoTypes": [
        {"name": "EMAIL_ADDRESS"},
        {"name": "PHONE_NUMBER"},
        {"name": "PERSON_NAME"},
        {"name": "STREET_ADDRESS"},
        {"name": "ES_NIF_NUMBER"},
        {"name": "IBAN_CODE"},
        {"name": "CREDIT_CARD_NUMBER"},
        {"name": "URL"}
      ],
      "minLikelihood": "POSSIBLE",
      "includeQuote": false,
      "limits": {"maxFindingsPerRequest": 3000}
    },
    "actions": [
      {"saveFindings": {"outputConfig": {"table": {
        "projectId": "alpinashop-datos",
        "datasetId": "alpinashop_analitica",
        "tableId": "hallazgos_dlp_opiniones"
      }}}}
    ]
  }
}

Les tres decisions que s'avaluen:

  • includeQuote: false: sense això, la taula hallazgos_dlp_opiniones contindria els telèfons i noms reals trobats. S'hauria creat una segona còpia sense protegir d'exactament el que es vol protegir.
  • minLikelihood: POSSIBLE en comptes de LIKELY: en text lliure escrit per persones, les dades apareixen en formats irregulars ("truqueu-me al sis onze..."). Un llindar baix genera més falsos positius, cosa que és preferible a no detectar una dada real. En una columna estructurada s'utilitzaria LIKELY.
  • identifyingFields: permet saber quina ressenya conté la troballa sense guardar el valor, cosa que fa el resultat accionable.

(b) Plantilla de desidentificació.

{
  "deidentifyTemplate": {
    "displayName": "Desidentificacio de ressenyes",
    "deidentifyConfig": {
      "recordTransformations": {
        "fieldTransformations": [
          {
            "fields": [{"name": "texto"}],
            "infoTypeTransformations": {
              "transformations": [
                {
                  "infoTypes": [
                    {"name": "PERSON_NAME"}, {"name": "PHONE_NUMBER"},
                    {"name": "EMAIL_ADDRESS"}, {"name": "STREET_ADDRESS"},
                    {"name": "ES_NIF_NUMBER"}, {"name": "IBAN_CODE"},
                    {"name": "CREDIT_CARD_NUMBER"}
                  ],
                  "primitiveTransformation": {
                    "replaceWithInfoTypeConfig": {}
                  }
                }
              ]
            }
          }
        ]
      }
    }
  }
}

Per què replaceWithInfoTypeConfig i no supressió. Substitueix cada troballa pel seu tipus entre claudàtors:

  • Original: "Molt comoda. Si teniu dubtes escriviu-me a [email protected] o al 611223344, soc Ana Ruiz"
  • Resultat: "Molt comoda. Si teniu dubtes escriviu-me a [EMAIL_ADDRESS] o al [PHONE_NUMBER], soc [PERSON_NAME]"

El text continua sent analitzable. La ressenya conserva la seva estructura, la seva longitud aproximada i —el que importa per al mòdul 5— el seu sentiment: "Molt còmoda" continua sent-hi. Amb supressió pura es perdria la coherència sintàctica i l'anàlisi de sentiment es degradaria.

(c) Vista exposada.

CREATE OR REPLACE VIEW `alpinashop-datos.alpinashop_analitica.v_opiniones_analitica` AS
SELECT
  opinion_id,
  sku,
  fecha,
  puntuacion,
  texto_desidentificado                              AS texto,
  pais,
  CASE WHEN puntuacion >= 4 THEN 'positiva'
       WHEN puntuacion = 3  THEN 'neutra'
       ELSE 'negativa' END                           AS valoracion,
  LENGTH(texto_desidentificado)                      AS longitud_texto
FROM `alpinashop-datos.alpinashop_analitica.opiniones_desidentificadas`;

El pipeline nocturn escriu a opiniones_desidentificadas aplicant la plantilla, i la taula original opiniones queda amb accés restringit a gcp-seguridad@. Looker Studio, la Lucía i els futurs models del mòdul 5 consumeixen exclusivament la vista.

(d) Advertiment de RGPD.

Les ressenyes de clients contenen dades personals, tant en camps estructurats (pais combinat amb sku i fecha pot ser reidentificable si el volum és baix) com incrustades en text lliure. Aquesta proposta aplica dues mesures tècniques: desidentificació automàtica del text mitjançant Sensitive Data Protection i restricció d'accés a la taula original.

Abans de passar a producció és necessari que un professional de compliance o el delegat de protecció de dades determini: la base legal del tractament analític de les ressenyes; si la desidentificació aplicada constitueix anonimització efectiva o mera pseudonimització —cosa que determina si el RGPD continua aplicant-se al resultat—; el termini de conservació adequat; el risc de reidentificació per combinació de camps aparentment no identificatius; i si l'ús previst al mòdul 5 (anàlisi de sentiment) requereix informació addicional a l'interessat. La desidentificació automàtica no és infal·lible: un client que escrigui "soc el que va comprar la motxilla blava el dimarts a la botiga de Sabadell" no serà detectat per cap tipus de dada predefinit. Totes les dades d'aquest curs són fictícies.

Solució 3

Gravetat relativa, de major a menor:

Problema 3 (enllaç públic) + Problema 2 (credencials del propietari) = INCIDENT DE SEGURETAT. No són dos problemes: són un de sol i greu. Que totes les consultes les faci la Lucía significa mode credencials del propietari. Combinat amb "qualsevol persona amb l'enllaç", qualsevol que tingui o rebi aquest enllaç consulta BigQuery amb els permisos de la Lucía, que inclouen la taula pedidos amb email_cliente. Si el consultor extern va reenviar l'enllaç, o si va acabar en un correu o un xat, hi ha una possible bretxa de dades personals amb obligació de notificació. S'actua primer, avui, en minuts.

Problema 4 (el número no quadra amb comptabilitat) = CRISI DE CONFIANÇA. És el segon en gravetat perquè destrueix el valor de tot el construït. Un tauler de control en què direcció no confia deixa d'utilitzar-se, i la feina de set lliçons es perd.

Problema 1 (lent i car) = OPERATIU. 310 € al mes fan mal, però no comprometen ni la seguretat ni la confiança. S'arregla en una tarda.

Actuació en tres fases:

Fase 1 — Avui, en minuts: tallar l'exposició.

1. Canviar la comparticio a "Persones concretes": gcp-datos@ i direccion@.
   Eliminar "qualsevol persona amb l enllac".
2. Canviar l origen de dades a CREDENCIALS DE L ESPECTADOR.
3. Comprovar a que apunta l origen. Si es una taula amb email_cliente,
   reapuntar-lo a la vista desidentificada v_pedidos_analitica.
-- Comprovar l abast real: qui ha consultat i quant, en 60 dies
SELECT
  user_email,
  COUNT(*)             AS consultas,
  MIN(creation_time)   AS primera,
  MAX(creation_time)   AS ultima,
  COUNT(DISTINCT DATE(creation_time)) AS dias_distintos
FROM `region-europe-west1`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 60 DAY)
  AND job_type = 'QUERY'
  AND STRPOS(IFNULL(query, ''), 'alpinashop_analitica') > 0
GROUP BY user_email
ORDER BY consultas DESC;

I en paral·lel: avisar el responsable de seguretat i el DPD. La valoració de si això constitueix una bretxa notificable sota el RGPD no la pren l'equip tècnic. Cal documentar quines dades eren accessibles, durant quant de temps i a qui es va compartir l'enllaç.

Fase 2 — Aquesta setmana: recuperar la confiança en el número.

La discrepància amb comptabilitat té tres causes candidates, i cal determinar quina és abans de tocar res:

-- Comparar les tres definicions possibles de "vendes del mes"
SELECT
  'A) Brut, IVA inclos, sense cancel·lades'     AS definicion,
  ROUND(SUM(total_pedido), 2)                   AS importe
FROM `alpinashop-datos.alpinashop_analitica.pedidos`
WHERE fecha_pedido BETWEEN DATE '2026-03-01' AND DATE '2026-03-31'
  AND estado != 'cancelado'
UNION ALL
SELECT
  'B) Brut sense IVA, sense cancel·lades',
  ROUND(SUM(subtotal - IFNULL(descuento,0)), 2)
FROM `alpinashop-datos.alpinashop_analitica.pedidos`
WHERE fecha_pedido BETWEEN DATE '2026-03-01' AND DATE '2026-03-31'
  AND estado != 'cancelado'
UNION ALL
SELECT
  'C) Net sense IVA, sense cancel·lades NI retornades',
  ROUND(SUM(subtotal - IFNULL(descuento,0)), 2)
FROM `alpinashop-datos.alpinashop_analitica.pedidos`
WHERE fecha_pedido BETWEEN DATE '2026-03-01' AND DATE '2026-03-31'
  AND estado NOT IN ('cancelado','devuelto');

Gairebé amb tota seguretat, comptabilitat utilitza la definició C —vendes netes sense IVA— i l'informe mostra la A. No és un error de dades: és un error de definició no documentada, exactament el segon problema de l'inici d'aquesta lliçó.

La correcció:

  1. Acordar amb comptabilitat una definició canònica i escriure-la.
  2. Implementar-la a la taula bi_resumen_diario, exposant totes dues magnituds amb noms inequívocs: ventas_brutas_iva_incl i ventas_netas_sin_iva.
  3. Escriure-la a l'etiqueta definicion_negocio del catàleg de Dataplex.
  4. Afegir una nota metodològica visible al peu del tauler de control.

Fase 3 — Aquest mes: cost i rendiment.

El diagnòstic primer:

SELECT
  SUBSTR(REGEXP_REPLACE(query, r'\s+', ' '), 1, 200) AS consulta,
  COUNT(*)                                           AS ejecuciones,
  ROUND(AVG(total_bytes_billed)/POW(1024,3), 2)      AS gb_por_ejecucion,
  ROUND(SUM(total_bytes_billed)/POW(1024,4), 2)      AS tb_totales,
  COUNTIF(cache_hit)                                 AS desde_cache
FROM `region-europe-west1`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time >= TIMESTAMP_TRUNC(CURRENT_TIMESTAMP(), MONTH)
  AND job_type = 'QUERY'
GROUP BY consulta
ORDER BY tb_totales DESC
LIMIT 10;

El resultat esperat: l'informe està connectat directament a lineas_pedido o a pedidos, i cadascun dels vuit gràfics executa la seva pròpia consulta cada vegada que algú mou un filtre. Dotze persones × diversos filtres al dia × vuit gràfics = milers de consultes sobre milions de files.

Les quatre correccions, per ordre d'impacte:

  1. Reapuntar l'informe a bi_resumen_diario, la taula agregada d'uns pocs milers de files que produeix el procés nocturn. Sola, aquesta mesura sol dividir el cost per cent i baixar la càrrega de 40 segons a menys de 3.
  2. Activar la memòria cau de l'informe amb 12 hores de vigència. Dotze persones mirant generen una consulta, no dotze.
  3. Reservar 2 GB de BI Engine. La taula agregada hi cap sencera en memòria: resposta en mil·lisegons i zero bytes facturats.
  4. Posar una quota diària de bytes al projecte i una alerta de pressupost sobre l'etiqueta centro-coste:analitica, perquè un problema equivalent es detecti als 20 € i no als 310 €.

Resultat esperat: de 310 €/mes a menys de 10 €, i de 40 segons a menys de 3.

La lliçó transversal de l'exercici: els quatre problemes eren evitables amb el que ja sabíem. El cost, amb el de 04-01. Els permisos, amb el de 03-04 i l'apartat 13. La discrepància de definicions, amb l'etiquetatge de l'apartat 4. Cap no era una fallada tècnica difícil: els quatre eren fallades de govern.

Conclusió

El mòdul 4 es tanca aquí, i es tanca amb la plataforma completa.

Has entès què és el govern de les dades —catàleg, llinatge, qualitat, classificació i cicle de vida— i per què una pime el necessita igual o més que una multinacional: el RGPD no distingeix per mida, el coneixement no té redundància quan l'equip són tres persones, els errors arriben a direcció sense filtres intermedis, i el deute de govern s'acumula amb interessos.

Has organitzat la plataforma a Dataplex: el llac alpinashop-lago-comercial amb la seva zona crua i la seva zona curada, amb els buckets i el dataset registrats com a actius i amb descobriment automàtic que converteix els Parquet de Spark en taules consultables sense declarar res. Has utilitzat el catàleg universal per respondre en segons a "on és la dada de conversió?" i a "on hi ha columnes que es diguin email". I has documentat amb una plantilla d'etiquetes que obliga a declarar propietari, domini, sensibilitat, frescor, retenció, aptitud per a informes i —el més valuós— la definició de negoci, aquella frase escrita una vegada que resol per sempre si total_pedido inclou IVA.

Has perfilat les taules abans de jutjar-les, descobrint el que ningú no sospitava: un 12 % de comandes sense client, cinc valors a canal on n'hi havia d'haver tres, i un total_pedido negatiu que distorsionava totes les mitjanes. I sobre aquest coneixement has definit regles de qualitat en les sis dimensions, amb llindars, amb exportació de resultats i —això és el que les fa útils— amb una acció assignada a cada fallada i amb integració a l'orquestrador perquè una fallada crítica aturi el procés abans de propagar dades dolentes.

Has activat el llinatge automàtic i tens el graf complet des de Cloud SQL i l'ERP de Sabadell fins al tauler de control, capaç de respondre d'on ve un número, què es trenca si canvies un esquema, on és la dada d'un client que exerceix el seu dret de supressió, i quin procés es pot apagar sense por.

Has inspeccionat amb Sensitive Data Protection i has trobat el que sempre es troba: correus al bolcat cru de Pub/Sub, noms al camp d'incidències del transportista, telèfons dins de ressenyes de clients, i IP a les dades de navegació. Quatre sorpreses de cinc troballes, cap per mala fe. I has desidentificat amb criteri: hash amb clau de KMS per poder comptar clients sense saber qui són, emmascarament del codi postal conservant la província, i substitució per tipus dins del text lliure perquè les ressenyes continuïn sent analitzables sense dades personals a dins. Amb l'advertiment exprés, repetit on tocava, que això són controls tècnics i que la decisió jurídica correspon a un professional de compliance o al DPD.

I has construït per fi el tauler de control que direcció demanava: connectat a una taula agregada petita i no a la taula de detall, amb memòria cau, amb BI Engine, amb camps calculats definits en SQL versionat i no tancats a l'informe, amb quatre indicadors i no dotze, amb comparació sempre, amb data d'actualització visible i amb nota metodològica al peu. I amb la lliçó de permisos que val per si sola tota la lliçó: credencials de l'espectador, compartició amb persones concretes, i l'origen apuntant sempre a una vista desidentificada, perquè és l'única defensa que no depèn que ningú se'n recordi. Saps quan Looker amb LookML justifica el seu preu —quan el cost de la incoherència supera el de la llicència— i per què AlpinaShop no és en aquest cas.

Mira el que existeix ara i que no existia fa set lliçons. Les comandes entren per Pub/Sub sense acoblar la botiga a res. Dataflow les transforma per lots i en streaming, amb el temps de l'esdeveniment ben entès. Dataproc calcula allò algorítmic en clústers que viuen quatre minuts. Data Fusion porta el que arriba brut de fora. BigQuery ho guarda i ho respon en segons, particionat, clusteritzat i sense arruïnar ningú. Workflows ho orquestra cada nit amb comprovacions que bloquegen si alguna cosa no quadra. Dataplex ho cataloga, ho mesura i ho rastreja. I Looker Studio ho ensenya. AlpinaShop ha passat de no saber quina motxilla es ven més a Catalunya a tenir una plataforma de dades governada per la qual la Lucía no demana res a ningú.

Però fixa't en el que tot això té en comú: descriu el que ja va passar. Explica les vendes d'ahir, l'embut del mes, el marge del trimestre, quins productes es van comprar junts. És memòria, i la memòria és valuosíssima. No és predicció, i no és automatització.

I AlpinaShop té ara mateix quatre preguntes que la memòria no pot respondre. Quan un client posa uns grampons al carretó, què li hauria de suggerir el web en aquell instant, aprofitant la matriu de coocurrència que va calcular Spark? Quan entren 60 GB d'imatges noves al catàleg, les ha d'etiquetar algú a mà o pot la màquina dir "això és una motxilla blava de 40 litres"? Amb 8.000 ressenyes en text lliure ja desidentificades, algú se les llegirà totes per saber què falla als frontals? I amb 2.400 fitxes de producte per descriure, qui les escriu?

Al mòdul 5, Aprenentatge automàtic i IA, AlpinaShop passa de descriure a predir i automatitzar. Començarem per 05-01, Vertex AI, la plataforma que unifica tot el cicle de vida del model —dades, entrenament, avaluació, desplegament i monitoratge— i que connectarà directament amb el dataset alpinashop_analitica que acabes de governar. Després arribaran AutoML per entrenar sense escriure codi, TensorFlow per quan calgui control total, les API de llenguatge natural i de visió que responen a les preguntes de les ressenyes i de les imatges sense entrenar res, la IA generativa amb Gemini per a les descripcions, i MLOps amb Vertex AI Pipelines perquè un model no sigui un experiment al portàtil d'algú sinó un sistema de producció amb la mateixa disciplina d'idempotència, qualitat, llinatge i cost que has aplicat en tot aquest mòdul.

Les dades ja són netes, governades, documentades i consultables. Això no era l'objectiu: era el requisit. Ara comença el que és interessant.

Curs de Google Cloud Platform (GCP)

Mòdul 1: Introducció a Google Cloud Platform

Mòdul 2: Serveis principals de GCP

Mòdul 3: Xarxes i seguretat

Mòdul 4: Dades i anàlisi

Mòdul 5: Aprenentatge automàtic i IA

Mòdul 6: DevOps i monitoratge

Mòdul 7: Temes avançats de GCP

Mòdul 8: Projecte final

© Copyright 2026. Tots els drets reservats