Módulo 4: Merge Avanzado y Fan-Out / Fan-In

Mini-Proyecto: 3 Fuentes Paralelas a Reporte Único

Descripción de la cápsula

Cierre del módulo. Vas a construir un workflow que consulta 3 fuentes en paralelo (Sheet + API mock + otro API mock), las combina según necesidad, y genera un reporte único. Aplica todos los patrones del módulo en un caso de negocio realista.

El caso: dashboard diario que consulta:

  1. Sheet de ventas internas
  2. API de Stripe (mock — pagos recibidos)
  3. API de Shopify (mock — orders del día)

Output: reporte unificado a Slack con stats combinadas.


Lo que vas a aprender

  • Integrar fan-out + Wait + Merge en un workflow productivo
  • Manejar timing distintos entre fuentes
  • Resolver inconsistencias entre datasets
  • Generar reporte combinado legible

El workflow

Estructura

[Schedule diario 9am]
   │
   ├─→ [Sheet: ventas internas]
   ├─→ [HTTP: Stripe API mock]
   └─→ [HTTP: Shopify API mock]
        │
        ▼
   [Merge: Wait]
        │
   [Set: combinar y calcular stats]
        │
   [Slack: reporte unificado]

Paso 1: Preparar las fuentes

1.1. Sheet "internal_sales"

Columnas: date, product, amount, seller

Llena con ~10 filas del día actual ({{ $now.toFormat('yyyy-MM-dd') }}).

1.2. API mock "Stripe"

Usa Beeceptor o similar para crear endpoint mock que devuelva:

{
  "payments": [
    { "id": "ch_1", "amount": 5000, "status": "succeeded", "currency": "usd" },
    { "id": "ch_2", "amount": 3000, "status": "succeeded", "currency": "usd" },
    { "id": "ch_3", "amount": 1500, "status": "failed", "currency": "usd" }
  ]
}

1.3. API mock "Shopify"

Otro endpoint mock:

{
  "orders": [
    { "id": 1001, "total": 75.50, "status": "fulfilled" },
    { "id": 1002, "total": 125.00, "status": "fulfilled" },
    { "id": 1003, "total": 50.00, "status": "pending" }
  ]
}

Paso 2: Construir el workflow

2.1. Workflow nuevo

[PROY-G2M04] Dashboard diario

2.2. Schedule Trigger

Configurado para 9am diario (cron: 0 9 * * *).

Para development, agrega Manual Trigger en paralelo.

2.3. 3 ramas paralelas

Cada rama lee de su fuente:

Rama 1: Sheet

  • Google Sheets node
  • Sheet: internal_sales
  • Filter por fecha = hoy (opcional, si tu Sheet tiene datos viejos)

Rama 2: Stripe mock

  • HTTP Request
  • GET tu mock URL
  • Configurar Continue on Fail (por si el mock está caído)

Rama 3: Shopify mock

  • HTTP Request
  • GET tu mock URL
  • Continue on Fail

2.4. Aggregate dentro de cada rama (opcional)

Para limpiar, usa Aggregate al final de cada rama:

[Sheet] → [Aggregate: all into 'sales_items']
[Stripe HTTP] → [Aggregate: response.payments into 'payments_items']
[Shopify HTTP] → [Aggregate: response.orders into 'orders_items']

Cada rama termina con 1 item con array.

2.5. Merge Wait

Configura Merge con mode Wait. Conecta las 3 ramas como inputs.

2.6. Set: combinar stats

Después del Merge:

// Field: total_internal_sales
{{ $('Aggregate Sheet').first().json.sales_items.length }}

// Field: total_sales_amount
{{ $('Aggregate Sheet').first().json.sales_items.reduce((a, b) => a + Number(b.amount), 0) }}

// Field: total_stripe_payments
{{ $('Aggregate Stripe').first().json.payments_items.filter(p => p.status === 'succeeded').length }}

// Field: total_stripe_amount
{{ $('Aggregate Stripe').first().json.payments_items.filter(p => p.status === 'succeeded').reduce((a, b) => a + b.amount, 0) / 100 }}

// Field: total_orders_shopify
{{ $('Aggregate Shopify').first().json.orders_items.length }}

// Field: total_shopify_amount
{{ $('Aggregate Shopify').first().json.orders_items.reduce((a, b) => a + Number(b.total), 0) }}

// Field: grand_total
{{ $json.total_sales_amount + $json.total_stripe_amount + $json.total_shopify_amount }}

Nota: $('NodeName') accede a outputs de nodos específicos por nombre. Útil después de Merge Wait.

2.7. Slack: reporte

📊 *Dashboard Diario - {{ $now.toFormat('dd/MM/yyyy') }}*

*Ventas Internas:*
• Operaciones: {{ $json.total_internal_sales }}
• Total: ${{ $json.total_sales_amount.toFixed(2) }}

*Stripe (Pagos online):*
• Exitosos: {{ $json.total_stripe_payments }}
• Total: ${{ $json.total_stripe_amount.toFixed(2) }}

*Shopify:*
• Orders: {{ $json.total_orders_shopify }}
• Total: ${{ $json.total_shopify_amount.toFixed(2) }}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━
💰 *GRAN TOTAL: ${{ $json.grand_total.toFixed(2) }}*

Paso 3: Test

3.1. Ejecutar manualmente

Click Execute Workflow.

3.2. Verificar

  • Las 3 ramas se ejecutaron en paralelo (deberías ver timing similar)
  • Merge Wait esperó a las 3
  • Slack recibe el reporte unificado con stats

3.3. Timing

Si las 3 APIs respondieran cada una en 2-5s:

  • Secuencial: 6-15s
  • Paralelo: ~5s (la más lenta)

Mide ambos para confirmar el speedup.


Paso 4: Variantes

Variante 1: Manejar una fuente caída

Si Stripe está caído (mock returns 500), el reporte debería seguir con las otras 2 fuentes + mensaje "Stripe no disponible".

Implementación:

  • Continue On Fail en HTTP Stripe
  • IF después: si error, set defaults a 0 + flag stripe_available: false
  • En el mensaje: agregar "⚠️ Stripe no disponible hoy" si flag false

Variante 2: Detectar discrepancias

Si Sheet "internal_sales" debería matchear con Stripe (mismas operaciones), agregar Combine para detectar diferencias:

[Sheet] ──┐
          ├─→ [Combine Keep Non-Matches by order_id]
[Stripe] ─┘
   │
[IF: hay discrepancias?]
└─→ [Slack: "Hay N ventas en Sheet sin matching pago en Stripe"]

Resumen y siguiente paso

Lo que construiste:

  • Workflow real con 3 fuentes paralelas + sincronización
  • Aplicación de fan-out + Merge Wait + agregación
  • Reporte combinado profesional

Lo que sabes:

  • Paralelismo real para multi-API
  • Sincronización con Wait
  • Estructuras mantenibles con 3+ fuentes

Antes de pasar a M05:

  • Workflow funciona y produce reporte
  • 3 APIs procesadas en paralelo (timing demostrado)
  • Manejas error de una fuente caída

Lo que sigue (Módulo 5 — Scheduling y Tiempo):

Profundiza el Schedule Trigger que viste en M01. Cron expressions avanzadas, timezone management para LATAM, scheduling complejo (mes/trimestre/año fiscal), recurring jobs con dependencias temporales.


Creado: Mayo 11, 2026 Versión: 1.0