Création d'un module de socle de données

Bien que Cortex Framework fournisse des modules de socle de données prêts à l'emploi pour les systèmes ERP d'entreprise tels que SAP (cortex.sap), vous pouvez également créer de nouveaux modules de socle de données personnalisés dans un espace de noms personnalisé. Cela vous permet de définir des comportements de compilation personnalisés et d'étendre la compatibilité à un nouveau système source. Il peut s'agir de systèmes de gestion de bases de données tels que PostgreSQL, MySQL, etc., qui répliquent leurs données brutes dans BigQuery.

Ce guide vous présente un exemple complet de création d'un module de socle de données pour un système de gestion des tickets du service client dont les données (tables customers, tickets, ticketlogitem) ont été répliquées d'une base de données PostgreSQL dans un ensemble de données BigQuery brut nommé ticketing_data_raw.

Lorsque vous créez un module de socle de données personnalisé, nous vous recommandons d'utiliser un espace de noms personnalisé dédié pour améliorer la gestion du cycle de vie en séparant les extensions et les personnalisations des artefacts Cortex Framework.

Présentation de l'exemple de scénario

Dans ce tutoriel, nous allons :

  1. créer un espace de noms personnalisé dédié ticketing pour isoler les actifs du socle de données ;
  2. définir un nouveau module de socle de données de chemin ticketing.ticketing.foundations.ticketing_system ;
  3. configurer les paramètres de table (table_settings.default.yaml) pour les tables customers, tickets et ticketlogitem ;
  4. créer des annotations de métadonnées au niveau des colonnes et des champs pour chaque table ;
  5. enregistrer la source de données brutes (ticketing_data_raw), l'ensemble de données conforme cible (data_foundation_ticketing) et le nouveau module de socle dans config/config.yaml.

Structure des dossiers et des fichiers du module

Tous les fichiers physiques de votre nouveau module de socle de données se trouvent dans votre espace de noms personnalisé sous src/data_modules/. Le tableau et l'arborescence de répertoires suivants indiquent où placer chaque fichier :

config/
└── config.yaml                                           # Global configuration & module registration
src/data_modules/ticketing/ticketing/foundations/ticketing_system/
├── manifest.yaml                                         # Declares module category, type, and builder
├── table_settings.default.yaml                           # Table materialization, bigQueryLabels, dataformTags, and layouts
├── builder.py                                            # (Optional) Custom Dataform generator class for this module
└── annotations/                                          # Field and column-level schema descriptions
    ├── customers.yaml
    ├── tickets.yaml
    └── ticketlogitem.yaml

Important : Avant de commencer, assurez-vous que les tables sources que vous prévoyez de traiter existent dans l'ensemble de données de la couche brute.

Chemin d'accès au fichier ou au répertoire Objectif et description
config/config.yaml Enregistre l'espace de noms ticketing, la source de données brutes PostgreSQL, l'ensemble de données BigQuery cible et l'instance du module de socle.
src/data_modules/ticketing/ticketing/foundations/ticketing_system/manifest.yaml Déclare les métadonnées du module, le nom à afficher, la catégorie (par exemple, foundation), le type de module (par exemple, generic) et la classe de compilateur de générateur utilisée lors de la compilation.
src/data_modules/ticketing/ticketing/foundations/ticketing_system/table_settings.default.yaml Configure les tables sources de ticketing_data_raw qui doivent être conformes, ainsi que les dataformTags d'optimisation BigQuery, les bigQueryLabels, les détails de partition et les détails de cluster.
src/data_modules/ticketing/ticketing/foundations/ticketing_system/annotations/*.yaml Contient des métadonnées YAML enrichies décrivant les définitions de table et de champ. Elles sont automatiquement fusionnées dans les définitions Dataform compilées afin que les descriptions soient conservées dans les métadonnées de la table BigQuery.
src/data_modules/ticketing/ticketing/foundations/ticketing_system/builder.py Facultatif. Si votre base de données source nécessite un nettoyage de données personnalisé ou des transformations SQL spécifiques à un dialecte lors de la compilation, vous pouvez définir une classe de compilateur au niveau du module ici.

Étape 1 : Enregistrez l'espace de noms, la source de données et la cible dans config.yaml

Avant de créer les fichiers physiques, ouvrez votre fichier de configuration de déploiement (config/config.yaml) et déclarez l'espace de noms personnalisé, l'ensemble de données source PostgreSQL brut et l'ensemble de données de destination dans lequel les tables conformes seront créées :

data:
  namespaces:
    - name: cortex
      path: ../src/data_modules/cortex
    - name: ticketing                              # <-- Name of custom namespace
      path: ../src/data_modules/ticketing          # <-- Points to subdirectory under 'src/data_modules/'

  datasets:
    - id: ticketing_data_raw                       # <-- Unique source ID
      projectId: "source_project_id"
      datasetId: ticketing_data_raw                # <-- Raw dataset containing PostgreSQL replication tables
    - id: data_foundation_ticketing                # <-- Unique target ID
      projectId: "target_project_id"
      datasetId: data_foundation_ticketing         # <-- Target dataset for conformed foundation tables

Étape 2 : Enregistrez le module de socle de données dans config.yaml

Dans la section data.modules.foundations de config/config.yaml, enregistrez la nouvelle instance du module de socle de données, en liant la source de données (ticketing_data_raw) à la cible de données (data_foundation_ticketing) :

data:
  modules:
    foundations:
      - moduleId: ticketing_foundation
        modulePath: ticketing.ticketing.foundations.ticketing_system   # Format: {namespace}.{systemtype}.{module_type:foundations}.{subsystemtype}
        dataSourceId: ticketing_data_raw
        dataTargetId: data_foundation_ticketing
        # Custom table settings file relative to 'config/' directory
        # Recommended path: '{namespace_dir}/{system_type}/foundations/{system_sub_type}/table_settings.yaml'
        # If omitted, defaults to "../src/data_modules/ticketing/ticketing/foundations/ticketing_system/table_settings.default.yaml"
        tableSettings: "ticketing/ticketing/foundations/ticketing_system/table_settings.yaml"

Étape 3 : Créez le fichier manifeste du module

Créez le fichier manifeste déclarant les métadonnées de votre module : src/data_modules/ticketing/ticketing/foundations/ticketing_system/manifest.yaml.

displayName: Ticketing System Data Foundation
description: Conformed foundation tables for PostgreSQL raw ticketing database.
category: foundation
type: generic
builder: ticketing_foundation

Étape 4 : Créez le fichier de paramètres de table (table_settings.default.yaml)

Créez le fichier de configuration de table par défaut src/data_modules/ticketing/ticketing/foundations/ticketing_system/table_settings.default.yaml. Ce fichier définit comment les tables PostgreSQL répliquées (customers, tickets, ticketlogitem) sont matérialisées, partitionnées et mises en cluster dans BigQuery :

common:
  - source:
      tableName: customers
    target:
      bigQueryLabels:
        - key: data_class
          value: master
      dataformTags: [ticketing, foundation, masterdata]
      clusterDetails:
        columns: [customer_id]

  - source:
      tableName: tickets
    target:
      bigQueryLabels:
        - key: data_class
          value: transactional
      dataformTags: [ticketing, foundation, transactional]
      partitionDetails:
        column: created_at
        partitionType: time
        timeGrain: day
      clusterDetails:
        columns: [ticket_id, customer_id]

  - source:
      tableName: ticketlogitem
    target:
      bigQueryLabels:
        - key: data_class
          value: transactional
      dataformTags: [ticketing, foundation, transactional]
      partitionDetails:
        column: log_timestamp
        partitionType: time
        timeGrain: day
      clusterDetails:
        columns: [ticket_id, log_id]

Étape 5 : Créez des annotations de métadonnées au niveau des champs

Pour vous assurer que vos tables conformes contiennent une documentation claire dans BigQuery, créez un fichier YAML d'annotation pour chaque table dans src/data_modules/ticketing/ticketing/foundations/ticketing_system/annotations/. Le nom de fichier doit correspondre exactement au nom de la table source.

annotations/customers.yaml

description: "Customer master data conformed from PostgreSQL raw ticketing database."
fields:
  - name: "customer_id"
    description: "Unique customer identifier, PK"
  - name: "email"
    description: "Primary email address associated with the customer"
  - name: "full_name"
    description: "Customer full name or account contact name"
  - name: "created_at"
    description: "Timestamp when the customer record was originally created in PostgreSQL"

annotations/tickets.yaml

description: "Customer service tickets conformed from PostgreSQL raw ticketing database."
fields:
  - name: "ticket_id"
    description: "Unique ticket identifier, PK"
  - name: "customer_id"
    description: "Foreign key referencing customers.customer_id"
  - name: "subject"
    description: "Summary or subject line of the customer inquiry"
  - name: "status"
    description: "Current ticket lifecycle status (e.g., OPEN, IN_PROGRESS, RESOLVED, CLOSED)"
  - name: "priority"
    description: "Priority severity level (e.g., LOW, MEDIUM, HIGH, URGENT)"
  - name: "created_at"
    description: "Timestamp when the ticket was created"
  - name: "updated_at"
    description: "Timestamp when the ticket was last modified"

annotations/ticketlogitem.yaml

description: "Audit log history and activity events for customer service tickets."
fields:
  - name: "log_id"
    description: "Unique log event identifier, PK"
  - name: "ticket_id"
    description: "Foreign key referencing tickets.ticket_id"
  - name: "action"
    description: "Action or event performed on the ticket"
  - name: "description"
    description: "Notes and description on performed events on the ticket"
  - name: "performed_by"
    description: "User, agent, or automated system that performed the action"
  - name: "log_timestamp"
    description: "Exact timestamp when the activity log event occurred"

Étape 6 : (Facultatif) Définissez un compilateur de socle personnalisé

Si votre socle de données PostgreSQL nécessite une logique lors de la compilation (telle que la conversion automatique de types de données, la conversion d'horodatages ou des règles de nettoyage des données dans toutes les tables), vous pouvez définir un compilateur personnalisé limité à ce module.

Créez src/data_modules/ticketing/ticketing/foundations/ticketing_system/builder.py :

import logging
import pathlib
import yaml
from common.builders.base import FoundationBuilder, Source
from common.registry import builder_registry
from common.schemas import config_schema, manifest_schema

logger = logging.getLogger(__name__)

@builder_registry.register("ticketing_foundation")
class TicketingFoundationBuilder(FoundationBuilder[config_schema.BaseModuleConfig]):
    """Custom Dataform generator for PostgreSQL ticketing data foundation."""

    def build(
        self,
        *,
        module_id: str,
        module_config: config_schema.BaseModuleConfig,
        global_config: config_schema.GlobalConfig,
        manifest: manifest_schema.ManifestConfig,
        base_dir: pathlib.Path,
        annotations_dir: pathlib.Path,
        output_dir: pathlib.Path,
        module_dir_name: str,
        sources_registry: set[Source],
        table_settings_file: pathlib.Path | None = None,
        required_tables: set[str] | None = None,
    ) -> None:
        logger.info("Building ticketing data foundation for module: %s", module_id)
        
        # 1. Load table settings
        if not table_settings_file or not table_settings_file.exists():
            logger.warning("No valid table settings found for %s", module_id)
            return

        with open(table_settings_file, encoding="utf-8") as f:
            settings = yaml.safe_load(f) or {}

        tables = settings.get("common", [])
        source_config = global_config.get_data_source(module_config.data_source_id)
        target_dataset = global_config.get_data_target(module_config.data_target_id)

        # 2. Generate Dataform .sqlx files for each table
        for table_item in tables:
            source_table = table_item["source"]["tableName"]
            if required_tables and source_table not in required_tables and not table_item.get("deployAlways"):
                continue

            # Register source table for centralized source generation
            sources_registry.add(Source(source_config.project_id, source_config.dataset_id, source_table))

            # Retrieve labels if configured
            bigquery_config = {}
            if "bigQueryLabels" in table_item["target"]:
                labels_dict = {label["key"]: label["value"] for label in table_item["target"]["bigQueryLabels"]}
                bigquery_config["labels"] = labels_dict

            dataform_tags = table_item["target"].get("dataformTags", ["ticketing", "foundation"])

            sqlx_content = f"""config {{
  type: "table",
  schema: "{target_dataset.dataset_id}",
  name: "{source_table}",
  tags: {dataform_tags}"""
            
            if bigquery_config:
                sqlx_content += f",\n  bigquery: {bigquery_config}"
                
            sqlx_content += f"""
}}

SELECT *
FROM `${{source_config.project_id}}.${{source_config.dataset_id}}.{source_table}`
"""
            out_file = output_dir / f"{source_table}.sqlx"
            out_file.write_text(sqlx_content, encoding="utf-8")
            logger.info("Generated %s", out_file)

Validation du nouveau module de socle

Pour valider et déployer le module de socle de données que vous venez de créer :

  1. Exécutez le script de compilation et de déploiement de Cortex Framework : bash uv run cortex-build-and-deploy --config "config/config.yaml"
  2. Vérifiez que la compilation Dataform a réussi sans erreur et que des scripts .sqlx ont été générés pour customers, tickets et ticketlogitem.
  3. Suivez les étapes de post-déploiement pour exécuter les actions de votre pipeline Dataform et vérifier les enregistrements conformes dans votre ensemble de données data_foundation_ticketing dans BigQuery.

Pour vérifier que le module de socle de données personnalisé est compilé et déployé correctement, consultez la section Validation de la page Extensibilité des produits de données.