ধরুন আপনি একটি ই-কমার্স ওয়েবসাইট বানাচ্ছেন। একজন ক্রেতা অর্ডার করল, আপনার সিস্টেম সেই অর্ডার প্রসেস করল এবং তারপর তাকে একটি ইনভয়েস ইমেইল পাঠাতে হবে। কিন্তু চিন্তা করুন — যদি প্রতিটি অর্ডারের সাথে সাথেই ইনভয়েস জেনারেট করে ইমেইল পাঠাতে যান, তাহলে পুরো প্রসেস স্লো হয়ে যেতে পারে। কেন স্লো হয়ে যাবে?
নিচের ডায়াগ্রামটি একটু খেয়াল করুন 👇
আমার একটি কাল্পনিক ই-কমার্স ব্যাকেন্ড অ্যাপ্লিকেশনে একটি এন্ডপয়েন্ট রয়েছে:
POST /api/create-order
এই API হিট করলেই নিচের কাজগুলো একসাথে হয়:
- ডাটাবেজে অর্ডার ও ইনভয়েস ইনসার্ট করা হয়।
- ইনভয়েস তথ্য নিয়ে একটি PDF তৈরি করা হয় — যা বেশ ব্যয়বহুল (CPU ও RAM heavy) কাজ।
- সেই PDF কে S3 স্টোরেজে আপলোড করা হয়।
- এরপর PDF ফাইলটি অ্যাটাচমেন্ট হিসেবে কাস্টমারকে ইমেইলে পাঠানো হয়।
এই পুরো কাজগুলো করতে আমাদের সময় লেগে যায় প্রায় 13.5 সেকেন্ড। মানে, একজন ইউজার যখন অর্ডার করে, তাকে ১৩ সেকেন্ড অপেক্ষা করতে হয় — যা অনেক বিরক্তিকর হতে পারে।
এই সমস্যা সমাধানে কী করা যায়?
এই ধরনের senerio তে আমরা যেকোন set of tasks কে critial এবং non-critical দুইটি ভাগে ভাগ করতে পারি। সাধারণত যেই কাজ গুলি আমাদের সাথে সাথে করতে হবে সেগুলাকে critical task বলি আরা। তেমনি ভাবে যেগুলি সাথে সাথে করার প্রয়োজন নেই যেমন আমার কাল্পনিক ইকমার্স এপ্লিকেশনের ক্ষেত্রে কাস্টমার যদি ২০/২৫ সেকেন্ড এমনকি কোন কাস্টমার যদি কয়েক মিনিট পরেও মেইল পায় সে কিছু মনে করবে না, এই ধরনের কাজ গুলিকে বলে non-critial task।
সুতরাং বুঝতেই পারছেন আমরা যদি এই অর্ডার প্রসেসিং উদাহরণে ক্রিটিক্যাল টাস্ক - ডাটাবেজে order entry করা এটি করে দিয়ে বাকি কাজ গুলা background এ করতে পারতাম, তাহলে কাস্টোমার কে মাত্র ৫০০ মিলিসেকেন্ড এর মধ্যে রেসপন্স দেয়া সম্ভব হত। যা হত খুবই ভাল একটি ইউজার এক্সপেরিয়েন্স।
🚀 সমাধান: Event-Driven প্যাটার্ন
এখানেই Event-Driven Architecture আমাদের বাঁচায়। এটি এমন এক design pattern, যা মূলত বলে — “আমার মূল কাজ শেষ, এখন বাকিগুলোকে ইভেন্ট দিয়ে অন্য কেউ হ্যান্ডেল করুক।”
এই প্যাটার্ন এখনকার অনেক স্কেলেবল অ্যাপ্লিকেশনেই ব্যবহৃত হয়, বিশেষ করে microservices বা asynchronous-heavy application গুলোতে। এবং আজকের এই লেখায় আমরা Node.js এর EventEmitter ব্যবহার করে দেখবো কীভাবে সহজেই এমন ব্যাকগ্রাউন্ড প্রসেস তৈরি করা যায়।
Event Driven Pattern কী?
Event Driven Pattern এমন এক ডিজাইন প্যাটার্ন, যেখানে আমরা কোন একটা কাজ হয়ে যাওয়ার পর — সেটার উপর ভিত্তি করে আরেকটা কাজ trigger করে দিই, যেন সেটা পরে বা আলাদা ভাবে সম্পন্ন হয়।
এটা অনেকটা এরকম ভাবুন —
ধরুন, আপনি একজন হোটেল রিসেপশনিস্ট। কেউ রুম বুক করল, আপনি সেই রুমটা confirm করে দিলেন — এখন তার লাগেজ রুমে পৌঁছে দেওয়ার জন্য bellboy কে ডাক দিলেন। আপনি নিজে লাগেজ টানতে যাননি, শুধু signal (event) দিয়েছেন, বাকিটা অন্য কেউ হ্যান্ডেল করেছে।
Node.js এ আমরা ঠিক এই কাজটাই করতে পারি EventEmitter দিয়ে। কোনো event ঘটে গেলে (যেমন order processed হয়ে গেছে), তখন আমরা বলি — "এই ইভেন্টের জন্য কে কে রেসপন্স করবে?" — আর সেগুলোকেই আমরা listener হিসেবে যুক্ত করি।
নিচের দুইটা senerio দেখুন 👇
🛎️ ১. Without Event (Everything done manually, tightly coupled)
ধরুন, আপনি একজন হোটেল রিসেপশনিস্ট। একজন অতিথি রুম বুক করলেন। আপনি রুম কনফার্ম করার পর, নিজেই বেরিয়ে পড়লেন তার লাগেজ নিতে। নিজেই লাগেজ টেনে নিয়ে গেলেন তার রুমে, দরজায় কড়া নেড়ে দিয়ে এলেন।
পরিণতি কী হলো?
- আপনি মূল দায়িত্ব (রুম বুকিং) ছেড়ে বাকি কাজ করতে গেলেন।
- আপনি নতুন কেউ আসলে তাদেরকে সময় দিতে পারবেন না।
- আপনার উপর অনেক লোড, সব কাজ আপনাকেই করতে হচ্ছে।
- নতুন কাস্টমার যখন বুকিং করতে আসবে তাকে অপেক্ষা করতে হবে আগের কাসস্টমারের প্রসেস শেষ হওয়ার আগ পর্যন্ত 😕।
🔁 ২. With Event (Event Driven Pattern)
এবার চলুন আরেকটি উদাহরণ দেখি, এখানে আপনি কোন কাস্টমার হোটেল রুম বুকিং করতে আসলে আপনি নিজে সব কিছু করেন না। কোন কাস্টমারের জন্যে আপনি রুম বুকিংয়ের পর শুধু bellboy কে জানালেন:
"Room 302 booked. Please take the luggage." Bellboy নিজেই চলে গেলো, লাগেজ নিলো, রুমে পৌঁছে দিলো।
এবার কী হলো?
- আপনি রিসেপশনে থাকলেন, আপনার মূল কাজেই ফোকাস করলেন। নতুন কোন কাস্টমার আসলতে তাকে সার্ভিস দেয়া।
- লাগেজের দায়িত্ব অন্য কেউ efficiently পালন করলো।
- পুরো সিস্টেমটা modular — একেকজনের একেক কাজ, dependency কম।
কী বুঝলেন?
Event-driven প্যাটার্ন ঠিক এমনই। আপনি আপনার কাজ শেষ করে একটি signal দিয়ে দেন — তারপর যার যা দায়িত্ব, সে সেই event ধরেই কাজ করে। এতে করে আপনার কোডbase clean থাকে, এবং পুরো অ্যাপ্লিকেশনটি scalable ও efficient হয়।
শুরুতেই একটি ছোট্ট উদাহরণ: Node.js এ EventEmitter module কিভাবে কাজ করে?
Node.js এর EventEmitter একটা ছোট signal system এর মতো কাজ করে। আপনি যেখানে ইচ্ছা একটি event trigger (emit) করতে পারেন, আর অন্য জায়গায় সেই event এর জন্য listener বসাতে পারেন।
// 1️⃣ EventEmitter import করি
import EventEmitter from 'node:events';
// 2️⃣ একটি ইন্সট্যান্স তৈরি করি
const emitter = new EventEmitter();
// 3️⃣ একটি ইভেন্টের জন্য listener সেট করি
emitter.on('greet', (name) => {
console.log(`Hello, ${name}!`);
});
// 4️⃣ এবার ইভেন্ট emit করি
emitter.emit('greet', 'Rayhan');
👉 আউটপুট:
Hello, Rayhan!
🔍 এখানে কী হলো?
- আমরা greet নামের একটি ইভেন্ট তৈরি করলাম
- যখনই এই ইভেন্ট emit হবে, তখন listener ফাংশনটি চলে যাবে
- emit('greet', 'Rayhan') কল করলে message print হলো
উপরের লিখা থেকে আমরা EventDriven patten বুঝলাম, critical/non-crital pattern কী বুঝলাম। এবার চলুন সেই কাল্পনিক ecommerce order processing api কে কীভাবে event-driven pattern এ critical এবং non-critical কাজ গুলি ভাগ করে order-processing আরও অনেক ফাস্ট করা যায় দেখি।
আমাদের কাজের ফ্লো হবে নিচের মত 👇
Node.js দিয়ে Event Driven অর্ডার প্রসেসিং
চলুন এবার দেখি কীভাবে এই সমস্যার সমাধান Node.js দিয়ে করা যায়।
আমরা EventEmitter ক্লাসকে extend করে OrderService নামের একটি ক্লাস তৈরি করেছি, যেটি ইভেন্ট-ভিত্তিক অর্ডার প্রসেস করার জন্য কাজ করে। নিচে তিনটি ফাইলে পুরো প্রসেসটি দেখানো হয়েছে:
1️⃣ Order Service (event emit করে)
// orderService.ts
import { EventEmitter } from 'node:events';
class OrderService extends EventEmitter {
processOrder(order: { id: string; customerEmail: string }) {
console.log(`✅ অর্ডার প্রসেস করা হলো: ${order.id}`);
// অর্ডার প্রসেস করার পরে ইভেন্ট ট্রিগার করা হচ্ছে
this.emit('orderProcessed', order);
}
}
export default new OrderService();
এখানে processOrder() মেথডটি কাস্টমার অর্ডার প্রসেস করে এবং একটি orderProcessed ইভেন্ট পাঠিয়ে দেয়, যা অন্য কোথাও লিসেন করা হবে।
2️⃣ Invoice Worker (ইভেন্ট ধরে ইনভয়েস তৈরি করে)
// invoiceWorker.ts
function handleInvoice(order: { id: string; customerEmail: string }) {
console.log(`📄 ইনভয়েস PDF তৈরি হচ্ছে...`);
setTimeout(() => {
console.log(`⬆️ ইনভয়েস S3 তে আপলোড সম্পন্ন`);
console.log(`📧 ${order.customerEmail} কে ইনভয়েস মেইল পাঠানো হলো`);
}, 2000); // simulate time delay
}
export default handleInvoice;
এই worker ফাংশনটি orderProcessed ইভেন্ট পেলে ব্যাকগ্রাউন্ডে ইনভয়েস তৈরি, আপলোড এবং ইমেইল পাঠানোর কাজ করে।
3️⃣ Integration (সবকিছু একসাথে সংযুক্ত করা)
// index.ts
import orderService from './orderService';
import handleInvoice from './invoiceWorker';
// ইভেন্টের জন্য লিসেনার রেজিস্টার করা হচ্ছে
orderService.on('orderProcessed', handleInvoice);
// এখন একটি অর্ডার প্রসেস করার সিমুলেশন
orderService.processOrder({
id: 'ORD12345',
customerEmail: 'user@example.com',
});
⚡ বাস্তব API রেসপন্স টাইম কমানোর উদাহরণ
// order.controller.ts
saveToDB(order);
eventEmitter.emit('orderProcessed', order);
res.send({ success: true });
এখানে saveToDB() দিয়ে critical কাজটি শেষ করে ফেলছি এবং emit() করে rest কাজগুলো (PDF, Email) পিছনে পাঠিয়ে দিচ্ছি।
- 🕐 রেসপন্স টাইম: ~500ms
- 🎉 ইউজার খুশি, কারণ দ্রুত রেসপন্স পাচ্ছে
- 🔁 বাকি কাজ হচ্ছে ব্যাকগ্রাউন্ডে
🧠 উপসংহার
আপনি যদি প্রতিটা কাজ একসাথে করেন, সিস্টেম ধীরে চলে। অথচ আপনি যদি event-driven ভাবনায় critical task (অর্ডার সেভ) এবং non-critical task (invoice, email) আলাদা করে ফেলেন — তাহলে পুরো অ্যাপ্লিকেশন:
- ✅ পারফরম্যান্সে ফাস্ট হবে
- 🧩 মডুলার ও loosely coupled থাকবে
- 📈 ভবিষ্যতে সহজেই scale করা যাবে
Node.js-এর EventEmitter দিয়ে আমরা এই কাজটা খুব সহজেই করতে পারি।
📘 কী শিখলাম?
- কিভাবে অর্ডার প্রসেসিং-এ সময় কমানো যায়
- Critical vs Non-Critical Task আলাদা করার গুরুত্ব
- Event Driven Pattern কী এবং কেন দরকার
- Node.js দিয়ে event emit ও handle করার সহজ উদাহরণ
আপনার অ্যাপ্লিকেশনে এমন কোন কাজ আছে যেটা সাথে সাথে না করলেও চলে? ভাবুন তো, আপনি কি সেই কাজগুলো event-driven করে আরও দ্রুত এবং স্কেলেবল করে তুলতে পারেন? TechDiary-তে মন্তব্য করে জানিয়ে দিন আপনার অভিজ্ঞতা! ❤️