Recorrido pedagógico · Paso 2 de 13

Cómo modelamos los datos

De las entidades operativas de Betterez a un modelo dimensional confiable. Por qué separamos hechos de dimensiones, qué representa el grano de cada tabla y qué decisiones de diseño sostienen los tableros.

Schema public (OLTP) Schema bi_rebuild (BI) Modelo estrella Grano explícito por hecho
Cómo leer esta presentación. Es la pieza conceptual del recorrido: sigue a Plataforma de datos (la visión) y precede a De un evento a un KPI (el recorrido aplicado a un caso real). Acá explicamos el qué y el por qué del modelo; allá, el cómo paso a paso.

Sección 02

El problema: eventos operativos ≠ preguntas de negocio

Betterez no nos entrega "ventas por agencia": nos entrega un flujo de eventos sueltos. Modelar es tender el puente entre ese flujo y las preguntas que hace la dirección.

Lo que llega (operativo)

Un webhook por cada hecho del mundo real: se vendió un pasaje, se canceló un boleto, se cerró un manifiesto. Cada evento es un JSON crudo, con su forma y su momento, optimizado para registrar transacciones, no para sumarlas.

Lo que se pregunta (negocio)

"¿Cuánto vendimos en mayo por agencia y moneda?", "¿qué ocupación tuvo el servicio Mendoza–Buenos Aires?", "¿cuántos boletos se cancelaron parcialmente?". Preguntas agregadas, comparables y estables en el tiempo.

Entre esos dos mundos hay una distancia real. Resolverla con consultas ad-hoc sobre las tablas operativas es frágil: cada reporte reinterpreta las reglas y dos personas obtienen dos números. Modelar es escribir esas reglas una sola vez, en un lugar curado, auditable y con nombres que no cambian.

Tres tensiones que el modelo resuelve

Fidelidad vs. usabilidad

La copia OLTP es fiel pero cruda. La capa BI es usable pero derivada. Necesitamos las dos, no una.

Estado vivo vs. histórico

Un boleto cambia de estado; un reporte de mayo no debería cambiar en julio. El modelo fija fotos consistentes.

Detalle vs. agregado

Auditar exige el detalle; decidir exige el agregado. El grano de cada tabla define a quién sirve.

Sección 03

Las cuatro capas, como decisiones de modelado

Cada capa es una representación distinta del mismo hecho. No son copias redundantes: cada una existe porque responde una pregunta que las otras no pueden.

Betterezfuente operativa
RawGCS + raw_events
OLTPschema public
BIschema bi_rebuild
MicroStrategytableros
CapaPregunta que respondeForma del datoQuién la usa
Raw¿Qué nos dijo Betterez, exactamente?JSON inmutable, tal cual llegóIngeniería, auditoría, reproceso
OLTP public¿Cuál es el estado actual de cada boleto?Tablas normalizadas, copia fielReconciliación, backend, ETL
BI bi_rebuild¿Cuánto, por agencia, ruta, estado?Modelo estrella: hechos + dimensionesMicroStrategy, finanzas, gerencia
MSTR¿Cómo lo veo y lo decido?Métricas, atributos, filtrosNegocio, dirección
Foco de esta pieza: las dos capas centrales de modelado — public (entidades operativas) y bi_rebuild (modelo dimensional). El raw-first y el flujo de ingesta se detallan en De un evento a un KPI y en Fiabilidad de captura.

Sección 04

Capa OLTP: las entidades del negocio

El schema public es la copia fiel de lo que Betterez nos entrega, normalizada en tablas. Cada una tiene un propósito claro y una clave de negocio.

TablaRepresentaClave de negocio
raw_eventsCada evento crudo recibido (auditoría + idempotencia)idempotency_key (hash SHA-256)
salesEntrada contable: venta, reembolso o ítemtransaction_id
ticketsCada boleto dentro de una transacciónticket_number
paymentsMedios de pago de una transacciónsale_id + tipo
refund_detailsDetalle de cada reembolsorefund_id
sold_item_detailsÍtems adicionales (seguros, impuestos)sold_item_number
manifest_entriesCada pasajero en un manifiestoticket_number + manifest_id
manifest_legsCada parada de un servicio (carga)manifest_id + leg_ord

Tres claves de cruce

Todo el sistema se entreteje con transaction_id, ticket_number y manifest_id. Memorizarlas es entender el modelo.

Normalización fiel

No "mejoramos" el dato acá: lo guardamos como vino. Las reglas de negocio viven aguas abajo, en BI.

Trazabilidad

Cada fila apunta a su raw_event_id: de cualquier número se puede volver al JSON original.

Sección 05

Cómo se relacionan las entidades

Una sola transacción explota en varias filas relacionadas. Entender estas cardinalidades evita los dos errores clásicos: contar de menos y contar de más.

raw_events evento crudo sales venta / refund / ítem 1:N tickets payments refund_details 1:1 con fila R- sold_item_details 1:1 con fila PI-

Una transacción es muchas filas

La venta principal vive en sales (sale_type='sale'). Sus boletos van a tickets (1:N — ida y vuelta son dos). Sus pagos a payments.

Refunds e ítems son filas nuevas

Un reembolso no edita la venta: crea una fila R- en sales con montos negativos. Un seguro crea una fila PI-. Esto preserva la historia, pero obliga a filtrar bien al sumar.

Sección 06

Decisiones de diseño en la capa operativa

Cuatro elecciones que parecen técnicas pero son de negocio: determinan qué se puede auditar y qué se puede confiar.

1. Raw-first, siempre

Antes de extraer cualquier tabla, el JSON completo se guarda en Google Cloud Storage y en raw_events. Si mañana cambia una regla, reprocesamos desde el original: el dato histórico nunca se pierde.

2. Idempotencia por hash

Mismo payload → mismo idempotency_key (SHA-256). Los reintentos de Betterez se deduplican solos: un evento nunca se cuenta dos veces.

3. Refunds como filas negativas

Cada reembolso es una fila R- aparte (is_refund=true, montos negativos, original_transaction_id apunta a la venta). La venta original queda intacta y el neto se obtiene sumando.

4. Ítems vendidos como filas PI-

Seguros, menores e impuestos viven como filas PI- en sales, con su detalle en sold_item_details y un puente a su boleto. Permite medirlos sin contaminar el conteo de pasajes.

Consecuencia para quien consulta: public.sales mezcla ventas, reembolsos e ítems. Sumar total a ciegas da un número sin sentido. Por eso la capa BI separa estos conceptos en hechos distintos — la próxima sección.

Sección 07

Del OLTP al modelo estrella

El schema bi_rebuild reorganiza las entidades operativas en dos tipos de tabla: hechos (lo que se mide) y dimensiones (cómo se corta lo medido).

Hechos — los verbos

Filas numéricas, una por evento de negocio. Cada fila tiene medidas (montos, conteos) y claves hacia las dimensiones. Crecen sin parar: un hecho por boleto, por venta, por parada.

Dimensiones — los sustantivos

Catálogos estables que dan contexto: agencias, rutas, fechas, servicios. Responden el "por": por agencia, por ruta, por mes. Pocas filas, nombres canónicos.

fact_ticket grano: 1 boleto dim_date dim_agency dim_route dim_schedule dim_manifest dim_channel

Un hecho en el centro, rodeado de las dimensiones que lo describen: la forma de "estrella" que da nombre al patrón.

Sección 08

Los hechos de bi_rebuild y su grano

El grano es la regla más importante de un hecho: define qué representa exactamente una fila. Confundirlo es la causa raíz de casi todo número mal sumado.

Grano · 1 boleto

fact_ticket

El hecho central de pasajes. Una fila por boleto no borrado. Trae estado (is_paid, is_cancelled, cancelación parcial), montos (ticket_amount, ticket_taxes), ruta, currency, manifest_id y flags de alcance (has_nacional…).

Grano · 1 boleto (wide)

fact_ticket_mstr

Mismo grano que fact_ticket pero "ancho" para MicroStrategy: 13 etiquetas has_*, tarifa/segmento de pasajero y ancillaries (seguro, menor) en columnas, sin tablas puente.

Grano · 1 venta

fact_sale

Una fila por transacción de sales. Medidas de cabecera: total_amount, num_tickets, pax_qty, gross_sale, net_sale, refounds_amount. Útil para ranking por agencia y neto comercial.

Grano · 1 ítem vendido

fact_sold_item

Una fila por ítem PI-. Clasifica seguro (VIAJE PROTEGIDO), menor (BOLETO SEGURO%), impuesto u otro. Alimenta v_insurance_kpi / v_minor_kpi.

Grano · 1 parada de servicio

fact_manifest_leg

Una fila por (manifest_id, leg_ord). Mide ocupación por tramo: pax_on, pax_off, load, load_factor. Para ocupación agregada se usa dim_manifest.

La regla de oro

Antes de sumar, preguntá: ¿qué es una fila acá? Pasajes → fact_ticket. Neto comercial → fact_sale. Seguros → fact_sold_item. Ocupación → fact_manifest_leg. Nunca mezclar granos en una misma medida.

Sección 09

Las dimensiones: el contexto estable

Las dimensiones son los catálogos por los que se cortan los hechos. Su valor está en ser canónicas: un solo nombre por agencia, una sola definición de ruta.

DimensiónDa contexto deDetalle de diseño
dim_dateTiempoAño, mes, semana, trimestre, día de semana
dim_agencyPunto de venta~297 agencias con agency_label canónico (catálogo Betterez)
dim_routeOrigen → destinoroute_id = origin_destination, distancia mediana
dim_schedule / dim_schedule_legItinerario / servicioDistancia en km por servicio y por tramo
dim_manifestServicio operadoOcupación pico, capacidad inferida, recursos (chofer, vehículo)
dim_channelCanal de ventaweb / callcenter / digital / physical
dim_manifest_tag + bridge_manifest_tagEtiquetas de servicio13 tags (nacional, internacional…) en relación N:M
dim_sold_item_productTipo de ítemCategoría: seguro / menor / impuesto / otro
Por qué importa el catálogo canónico: si Betterez renombra "Andesmar Centro" a "Andesmar Mendoza Centro", sin dim_agency el ranking se parte en dos. La dimensión absorbe el cambio y los tableros siguen sumando bien. Un job mensual la mantiene al día.

Sección 10

Granularidad y el riesgo de doble conteo

El error más caro en BI no es un dato faltante: es un dato contado dos veces. El modelo lo previene por diseño, pero hay que saber leerlo.

El riesgo

En public.sales conviven la venta padre, sus reembolsos R- y sus ítems PI-. Si un reporte suma todo junto, mezcla pasajes con seguros y resta reembolsos donde no debe.

La defensa: granos separados

BI parte cada concepto en su hecho. fact_ticket cuenta boletos; fact_sold_item cuenta ítems; fact_sale resume la transacción. Cada medida vive en su grano.

La vista de seguridad

v_fact_sale_parent_only expone solo las ventas padre, sin filas PI-% ni R-%. Es el atajo correcto cuando se quiere el total comercial sin doble conteo.

Tres preguntas, tres granos correctos

PreguntaHecho correctoPor qué
¿Cuántos pasajes pagados vendimos?fact_ticket · is_paidEl grano es el boleto, no la venta
¿Cuál fue el neto comercial por agencia?fact_sale / v_fact_sale_parent_onlyResumen por transacción, sin ítems
¿Cuántos seguros se vendieron?fact_sold_item · is_insuranceEl grano es el ítem; PI- solo no alcanza

Sección 11

Decisiones de diseño en la capa BI

Convenciones que conviene conocer para leer cualquier número de bi_rebuild sin sorpresas.

Singular, no plural

La capa activa usa nombres en singular: fact_ticket, dim_agency. El viejo schema bi (plural: fact_tickets) está congelado y no se usa. Si ves plural, es código viejo.

sale_date en hora Argentina

OLTP guarda sale_date en UTC. BI lo materializa como fecha ART (America/Argentina/Buenos_Aires). Por eso un corte mensual en BI coincide con el calendario operativo, no con UTC.

Multi-moneda sin conversión

Los montos conviven en ARS, CLP, BOB y USD con currency explícita. No se convierten entre sí. Para sumar coherente, siempre filtrar por una moneda.

Match ticket↔manifiesto por ticket_number

El vínculo oficial boleto–servicio es por ticket_number, con fallback por schedule + travel_date. schedule_id es atributo de servicio, nunca clave de unión de tickets.

Estas convenciones no son trivia técnica: son el contrato que permite que finanzas, operaciones y dirección lean el mismo tablero y obtengan el mismo número. Modelar bien es, sobre todo, acordar significados.

Sección 12 · Cierre

El modelo, en una frase

Guardamos el evento crudo, lo normalizamos fiel en OLTP y lo reorganizamos en hechos y dimensiones con grano explícito: así una pregunta de negocio tiene una sola respuesta confiable.

1

Fidelidad

Raw-first y OLTP preservan el original, auditable y reprocesable.

2

Estructura

Hechos y dimensiones con grano claro, sin doble conteo.

3

Significado

Convenciones compartidas: un número, una definición.

Seguí el recorrido: ahora que sabés cómo está modelado, mirá De un evento a un KPI → para ver el modelo en acción sobre un caso real, de la venta al número en el tablero.

Andesmar API · Modelo de datos · public + bi_rebuild · Junio 2026