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:
- Sheet de ventas internas
- API de Stripe (mock — pagos recibidos)
- 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