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
JavaScript
/**
* 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);
});