Creación de módulos de base de datos
Si bien Cortex Framework proporciona módulos de base de datos listos para usar para sistemas ERP empresariales, como SAP (cortex.sap), también puedes crear módulos de base de datos personalizados nuevos dentro de un espacio de nombres personalizado. Esto te permite definir comportamientos de compilación personalizados y extender la compatibilidad a un nuevo sistema de origen. Estos pueden incluir sistemas de administración de bases de datos como PostgreSQL, MySQL, etcétera, que replican sus datos sin procesar en BigQuery.
En esta guía, se explica un ejemplo de extremo a extremo para crear un nuevo módulo de base de datos para un sistema de emisión de tickets de atención al cliente cuyos datos (tablas customers, tickets, ticketlogitem) se replicaron desde una base de datos de PostgreSQL en un conjunto de datos sin procesar de BigQuery llamado ticketing_data_raw.
Cuando crees un módulo de base de datos personalizado, te recomendamos que uses un espacio de nombres personalizado dedicado para mejorar la administración del ciclo de vida separando las extensiones y personalizaciones de los artefactos de Cortex Framework.
Descripción general de la situación de ejemplo
En este instructivo, haremos lo siguiente:
- Crear un espacio de nombres personalizado dedicado
ticketingpara aislar los recursos de la base de datos - Definir un nuevo módulo de base de datos de la ruta de acceso
ticketing.ticketing.foundations.ticketing_system - Configurar los parámetros de configuración de la tabla (
table_settings.default.yaml) para las tablascustomers,ticketsyticketlogitem - Crear anotaciones de metadatos a nivel de columna y campo para cada tabla
- Registrar la fuente de datos sin procesar (
ticketing_data_raw), el conjunto de datos con formato de destino (data_foundation_ticketing) y el nuevo módulo de base enconfig/config.yaml
Estructura de carpetas y archivos del módulo
Todos los archivos físicos de tu nuevo módulo de base de datos residen dentro de tu espacio de nombres personalizado en src/data_modules/. En la siguiente tabla y árbol de directorios, se indica dónde colocar cada archivo:
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
Importante: Antes de comenzar, asegúrate de que las tablas de origen que planeas procesar existan en el conjunto de datos de la capa sin procesar.
| Ruta de acceso del archivo o directorio | Propósito y descripción |
|---|---|
config/config.yaml |
Registra el espacio de nombres ticketing, la fuente de datos sin procesar de PostgreSQL, el conjunto de datos de BigQuery de destino y la instancia del módulo de base. |
src/data_modules/ticketing/ticketing/foundations/ticketing_system/manifest.yaml |
Declara los metadatos del módulo, el nombre visible, la categoría (p.ej., foundation), el tipo de módulo (p.ej., generic) y la clase de compilador del generador que se usa durante la compilación. |
src/data_modules/ticketing/ticketing/foundations/ticketing_system/table_settings.default.yaml |
Configura qué tablas de origen de ticketing_data_raw deben tener formato, junto con los datos de optimización de BigQuery dataformTags, bigQueryLabels, detalles de partición y detalles de clúster. |
src/data_modules/ticketing/ticketing/foundations/ticketing_system/annotations/*.yaml |
Contiene metadatos YAML enriquecidos que describen las definiciones de tabla y campo. Estos se combinan automáticamente en las definiciones de Dataform compiladas, por lo que las descripciones persisten en los metadatos de la tabla de BigQuery. |
src/data_modules/ticketing/ticketing/foundations/ticketing_system/builder.py |
Opcional. Si tu base de datos de origen requiere limpieza de datos personalizada o transformaciones de SQL específicas del dialecto durante la compilación, puedes definir una clase de compilador a nivel del módulo aquí. |
Paso 1: Registra el espacio de nombres, la fuente de datos y el destino en config.yaml
Antes de crear los archivos físicos, abre el archivo de configuración de implementación (config/config.yaml) y declara el espacio de nombres personalizado, el conjunto de datos de origen sin procesar de PostgreSQL y el conjunto de datos de destino en el que se crearán las tablas con formato:
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
Paso 2: Registra el módulo de base de datos en config.yaml
En la sección data.modules.foundations de config/config.yaml, registra la nueva instancia del módulo de base de datos, vinculando la fuente de datos (ticketing_data_raw) al destino de datos (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"
Paso 3: Crea el archivo de manifiesto del módulo
Crea el archivo de manifiesto que declara los metadatos del módulo: 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
Paso 4: Crea el archivo de configuración de la tabla (table_settings.default.yaml)
Crea el archivo de configuración de tabla predeterminado src/data_modules/ticketing/ticketing/foundations/ticketing_system/table_settings.default.yaml. Este archivo define cómo se materializan, particionan y agrupan en clústeres las tablas de PostgreSQL replicadas (customers, tickets, ticketlogitem) dentro de 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]
Paso 5: Crea anotaciones de metadatos a nivel de campo
Para asegurarte de que tus tablas con formato contengan documentación clara en BigQuery, crea un archivo YAML de anotación para cada tabla dentro de src/data_modules/ticketing/ticketing/foundations/ticketing_system/annotations/. El nombre de archivo debe coincidir exactamente con el nombre de la tabla de origen.
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"
Paso 6: (Opcional) Define un compilador de base personalizado
Si tu base de datos de PostgreSQL requiere lógica durante la compilación (como la conversión automática de tipos de datos, la conversión de marcas de tiempo o las reglas de limpieza de datos en todas las tablas), puedes definir un compilador personalizado con alcance para este módulo.
Crea 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)
Verificación del nuevo módulo de base
Para verificar y, luego, implementar el módulo de base de datos recién creado, haz lo siguiente:
- Ejecuta la secuencia de comandos de compilación e implementación de Cortex Framework:
bash uv run cortex-build-and-deploy --config "config/config.yaml" - Verifica que la compilación de Dataform se haya realizado correctamente sin errores y que se hayan generado secuencias de comandos
.sqlxparacustomers,ticketsyticketlogitem. - Sigue los pasos posteriores a la implementación para ejecutar las acciones de la canalización de Dataform y verificar los registros con formato dentro de tu conjunto de datos
data_foundation_ticketingen BigQuery.
Para verificar que el módulo de base de datos personalizado se compile y se implemente correctamente, consulta la sección Verificación en la página Extensibilidad del producto de datos.
- Paso anterior: Configura un espacio de nombres personalizado
- Paso siguiente: Crea un módulo de producto de datos
- Volver a la descripción general