UNPKG

kafka-pub-sub

Version:

Enterprise-grade Kafka publish/subscribe library for Node.js — producer pool, batch sending, DLQ, SSL/SASL, multi-broker, and real-world industry examples.

140 lines (125 loc) 4.22 kB
/** * E-Commerce Order Pipeline — Producer * * Simulates a checkout service that fires events as a customer places an order. * * Event flow: * [Checkout] → order.placed * → inventory.reserve (after stock is confirmed) * → payment.charge (after reservation) * → fulfillment.ship (after payment clears) * * In a real system each of these would be produced by separate microservices. * Here they are shown in sequence to illustrate the saga pattern. * * Run: * cp ../../.env.example ../../.env # set KAFKA_BROKER_URLS etc. * node examples/ecommerce/producer.js */ require('dotenv').config({ path: require('path').resolve(__dirname, '../../.env') }); const { ProduceEvent } = require('../../index'); const TOPICS = { ORDER_PLACED: 'order.placed', INVENTORY_RESERVE: 'inventory.reserve', PAYMENT_CHARGE: 'payment.charge', FULFILLMENT_SHIP: 'fulfillment.ship', }; const generateOrderId = () => `ORD-${Date.now()}`; const generateCorrelationId = () => `CORR-${Math.random().toString(36).slice(2, 10).toUpperCase()}`; /** * Simulates a complete order saga: place → reserve → pay → ship. */ async function runOrderSaga() { const orderId = generateOrderId(); const correlationId = generateCorrelationId(); const traceHeaders = { 'correlation-id': correlationId, 'x-source': 'checkout-service', 'x-tenant': 'acme-corp', }; // eslint-disable-next-line no-console const log = (...args) => console.log(`[${new Date().toISOString()}]`, ...args); log(`Starting order saga for ${orderId} (correlation: ${correlationId})`); // 1. Order placed await ProduceEvent( TOPICS.ORDER_PLACED, 'ORDER_PLACED', { orderId, customerId: 'CUST-42', items: [ { sku: 'SKU-WIDGET-100', qty: 2, unitPrice: 29.99 }, { sku: 'SKU-GADGET-200', qty: 1, unitPrice: 89.99 }, ], currency: 'USD', total: 149.97, shippingAddress: { line1: '123 Main St', city: 'San Francisco', state: 'CA', zip: '94102', country: 'US', }, }, traceHeaders, { correlationId } ); log(`✓ Produced: ORDER_PLACED → ${TOPICS.ORDER_PLACED}`); // 2. Inventory reservation request await ProduceEvent( TOPICS.INVENTORY_RESERVE, 'INVENTORY_RESERVE', { orderId, reservations: [ { sku: 'SKU-WIDGET-100', qty: 2, warehouseId: 'WH-WEST-01' }, { sku: 'SKU-GADGET-200', qty: 1, warehouseId: 'WH-EAST-03' }, ], expiresAt: new Date(Date.now() + 15 * 60 * 1000).toISOString(), // 15 min hold }, { ...traceHeaders, 'x-source': 'inventory-service' }, { correlationId, partitionKey: orderId } ); log(`✓ Produced: INVENTORY_RESERVE → ${TOPICS.INVENTORY_RESERVE}`); // 3. Payment charge request await ProduceEvent( TOPICS.PAYMENT_CHARGE, 'PAYMENT_CHARGE', { orderId, customerId: 'CUST-42', paymentMethodId: 'pm_3Nbz7g2eZvKYlo2C1wWR45Sz', // Stripe token amount: 14997, // in cents currency: 'usd', metadata: { orderId, correlationId }, }, { ...traceHeaders, 'x-source': 'payment-service' }, { correlationId, partitionKey: 'CUST-42' } // partition by customer for ordering ); log(`✓ Produced: PAYMENT_CHARGE → ${TOPICS.PAYMENT_CHARGE}`); // 4. Fulfillment / shipment trigger await ProduceEvent( TOPICS.FULFILLMENT_SHIP, 'FULFILLMENT_SHIP', { orderId, fulfillmentItems: [ { sku: 'SKU-WIDGET-100', qty: 2, warehouseId: 'WH-WEST-01' }, { sku: 'SKU-GADGET-200', qty: 1, warehouseId: 'WH-EAST-03' }, ], shippingCarrier: 'UPS', shippingService: 'GROUND', estimatedDelivery: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(), }, { ...traceHeaders, 'x-source': 'fulfillment-service' }, { correlationId } ); log(`✓ Produced: FULFILLMENT_SHIP → ${TOPICS.FULFILLMENT_SHIP}`); log(`Order saga complete for ${orderId}`); await ProduceEvent.disconnect(); } runOrderSaga().catch((err) => { // eslint-disable-next-line no-console console.error('Order saga failed:', err); process.exit(1); });