আপনার application দিব্যি চলেছে, traffic বাড়ছে। এখন Perfectly সবকিছু চলছে। Business-ও grow করছে, profit-ও দিন দিন বাড়ছে। ভালোই। এভাবেই ২-৩ মাস ভালোই চললো। কিন্তু সমস্যা হলো database load নিয়ে। Database slow হয়ে গেলো। User-রা তাদের important information-গুলো পাচ্ছে না।
আগের মতোই, same way-তে application debugging শুরু করলেন। এখন কিন্তু আপনি একা না। তাই কয়েক ঘণ্টার ভেতরেই সব issue-গুলো পেয়ে গেলেন। সমস্যাগুলো ঠিক এরকম:
Feed application-এ post read-এর থেকে write বেশি হচ্ছে। Caching দিয়েও কাজ হচ্ছে না। কারণ frequently write হচ্ছে। আর cache regularly invalidate হচ্ছে। Read বেশি হওয়ার কারণে database-এর limit শেষ হয়ে যাচ্ছে। অনেক read হওয়ার কারণে অনেক user write করতে পারছে না।
Cache service এখানে অনেকটা useless হয়ে যাচ্ছে কারণ frequently cache update করতে হচ্ছে।
এখন problem তো পেয়ে গেছেন। আর আপনি ভালো করেই জানেন problem properly খুঁজে বের করা মানেই problem অর্ধেক solve হয়ে গেছে। এখন আপনার মাথায় একটা idea আসলো, read আর write-এর জন্য আলাদা database use করলে কেমন হয়। কিন্তু এতোদিনে বুঝে গেছেন, একটা simple জিনিসকে distribute করলে সেখানে limitless problem হাজির হয়। Distribute system is nightmare.
আপনার এই idea-টা আপনি আপনার team member-দের কাছে share করলেন, brain storming শুরু হলো। অনেকগুলো question এবং possible complexity-গুলো হাজির হলো। যেমন:
- ২টা database maintain কিভাবে করবো, manpower লাগবে, budget লাগবে।
- Write এবং read database-এর জন্য আলাদা আলাদা model, access modifier, response change হওয়ার possibility থাকতে পারে।
- Write-এর জন্য যে schema use করবো, তার সাথে read-এর জন্য আলাদা schema use করলে communication কিভাবে করবো।
- Data replicate কিভাবে করবো, read এবং write database-এর ভেতরে replication-টা কিভাবে হবে।
- কোনো একটা database fail হলে কি হবে। Backup কিভাবে নিবো। Failover-টা কিভাবে হবে।
এখন এই প্রত্যেকটা problem solve করার জন্য, আপনি যদি নিজে solution বের করতে চান, তাহলে সময় লাগবে, manpower লাগবে।
কিন্তু কথা হচ্ছে, আপনার এতো চিন্তা করা লাগবে না, এই problem-গুলো address করার জন্য একটা architecture already বিদ্যমান আছে।
CQRS (Command Query Responsibility Segregation) is the solution.
CQRS: Command Query Responsibility Segregation
CQRS-এর full form হচ্ছে Command Query Responsibility Segregation। নামটা শুনে কেমন একটা মনে হচ্ছে যে, কোনো responsibility আলাদা করা লাগবে। এখানে command-এর মানে write operations এবং query-এর মানে হচ্ছে read operation। CQRS pattern-এ read আর write operation-এর যে responsibility আছে সেটা segregate বা আলাদা করা।
নিচের code-গুলো দেখেন। দেখলে idea-টা পাবেন।
// সব কাজ একই model
const user = await userService.findById(id);
await userService.update(id, data);
CQRS-এ:
// Write side — Command
await commandBus.execute(new UpdateUserCommand(id, data));
// Read side — Query
const userView = await queryBus.execute(new GetUserQuery(id));
কেন আলাদা করব?
এখন আপনার arise হওয়া question-গুলোর solution CQRS-এ already বিদ্যমান।
- আপনি কিন্তু এখন আর একা নন। আপনার team আছে। Manpower আছে। এটাকে easily handle করা যাবে। আর profit-ও তো হচ্ছে। এর জন্য নতুন ১-২ জন hire করলে খুব বেশি problem হবে না। বা আপনার existing team member-দের এখানে assign করলেই হয়ে যাবে।
- এটা CQRS model-এর issue না। মূলত এটা আপনার development এবং UI-কে optimize করতে help করবে। কারণ write-এর operation থাকবে business এবং database perspective-এ আর read operation related কাজগুলো হবে UI optimization করার জন্য। সেখানে response model আলাদা রাখলে কোনো problem হবে না।
- এই Architecture কাজ করবে asynchronous model-এ। প্রথমে সবকিছুই write database-এ write হবে, then, eventually সেই data read DB-তে asynchronously replicate হবে।
- ২টা database-এর মধ্যে data synchronization-এর জন্য event use করা হয়। এটাকে আপনি event driven architecture-ও বলতে পারেন। আর event driven architecture সম্পর্কে তো অনেক আগে থেকেই জানেন।
- Write DB-টাই মূলত database-এর source of truth। Read DB, write থেকেই replication হয়। কোনো কারণে read DB fail করলে, write DB থেকে replicate করে নিলেই হয়ে যাবে।
এগুলো সবই CQRS-এর part।
Read write operation আলাদা কেন করবেন সেটা তো already বুঝে গেছেন।
Event Sourcing: History-র পুরো বই
Event সম্পর্কে মোটামুটি idea হয়ে যাওয়ার কথা। এখন বলবেন event আর event sourcing তো একই জিনিস। না। Event sourcing বোঝার আগে একটা গল্প বলি।
মনে করুন, আপনার application-এর জন্য একটা bank account খুললেন। খোলার পরে ৫০,০০০ টাকা deposit করলেন। এরপর ২০,০০০ টাকা withdraw করলেন। ৫,০০০ টাকা transfer করলেন। তারপরে আবার ১০,০০০ টাকা deposit করলেন।
এখন আপনার account-এ কত টাকা আছে? কিন্তু এখানে সমস্যা হচ্ছে আপনার কাছে এমন কোনো access নেই যার মাধ্যমে আপনি জানতে পারবেন যে আপনার current balance কত। এখানে আপনি একটু বুদ্ধি খাটিয়ে বের করে ফেলতে পারেন, আপনি যা করেছেন সেগুলো replay বা calculate করে। তাহলে final balance কত হবে: ৫০,০০০ - ২০,০০০ - ৫,০০০ + ১০,০০০ = ৩৫,০০০।
Event sourcing জিনিসটা অনেকটা এরকম। কোনো single value store না করে event-গুলো properly store করে, সেগুলো process করে current state কে বের করা হয়। এই যে কোনো single state use না করে event-গুলো store করে সেগুলো process করাই হচ্ছে event sourcing।
একটা example দিলে বুঝতে পারবেন। সাধারণত আমরা current state-কেই database-এ store করি এবং সেটাই প্রয়োজনে mutate করে use করি। For example:
user: { balance: 5000 }
এখানে বোঝাচ্ছে user-এর balance ৫০০০। এখন deposit বা withdraw করলে current state update হয়ে যাবে। Transfer করলেও এই same state mutate হবে।
কিন্তু event sourcing-এর ক্ষেত্রে কোনো fixed state থাকে না। Multiple event-গুলো store রাখা হয়। For example:
AccountOpened { balance: 10000 }
MoneyWithdrawn { amount: 2000 }
MoneyDeposited { amount: 500 }
MoneyWithdrawn { amount: 3500 }
এখানে আগের মতো কোনো fixed state কিন্তু নেই। এখানে সবগুলোই event এবং সেগুলো properly sequence way-তে store করা। এখন আমার balance জানতে হলে এগুলো replay করতে হবে। Replay করার পরে balance হবে: ১০,০০০ - ২,০০০ + ৫০০ - ৩,৫০০ = ৫,০০০। এই জিনিসটাই কিছু আগে কিন্তু একবার করেছেন।
Event Sourcing-এর জাদু
Event sourcing জিনিসটার concept-টা একদম simple। কিন্তু implementation একটু complex। কিন্তু যেখানে প্রতিটি event বা operation আপনার জন্য important, সেখানে শুধুমাত্র এই event sourcing-ই use করা হয়। For example:
Banking sector-এ একজন user-এর প্রত্যেকটা event store করে রাখা প্রয়োজন, কারণ যেকোনো সময়ে audit বা legal requirement-এর জন্য আপনাকে প্রত্যেকটা event-এর হিসাব দিতে হয়। আর টাকার হিসাব এদিক-ওদিক হলেই bank-এর liability তৈরি হয়ে যাবে। Bank-এর reputation নষ্ট হবে। এই জন্য এই জিনিসটা খুবই important। Similar way-তে medical related application-এর জন্যও same rule apply হতে পারে।
- Complete Audit Trail — কখন কী হয়েছে সব জানা যাবে। Financial system, healthcare-এ এটা must।
- Temporal Query — "৩ মাস আগে balance কত ছিল?" — সহজেই বের করা যাবে।
- Debug — Bug হলে event replay করে ঠিক কোথায় সমস্যা হয়েছে দেখা যাবে।
- Event Replay — নতুন feature-এর জন্য পুরনো data নতুন করে process করা যাবে।
কিন্তু এখানে একটা বড় question আসতে পারে, event-এর পরিমাণ যদি ১ লাখের বেশি হয়ে যায় তখন কী হবে? প্রতিবার balance জানার জন্য ১ লাখ event replay করতে হবে? বিষয়টা কী পরিমাণ resource consume করতে পারে সেটা বুঝতেই পারছেন।
Answer হচ্ছে এটা বারবার করার প্রয়োজন হবে না। এর জন্য Snapshots use করা হয়।
Snapshots
Snapshots concept-এর সাথে already পরিচিত হওয়ার কথা। সংক্ষেপে বললে এখানে কিছু checkpoints তৈরি করে রাখা হয়, যাতে easily সেগুলো use করা যায়।
ওপরে যে problem-টার কথা বললাম, ১ লাখ event হলে কী হবে। আসলে এই ক্ষেত্রে snapshot use করা হবে। প্রতি ১০০০ event-এর state-কে aggregate-এ save করে রাখা হবে snapshot হিসেবে। এভাবে চলতে থাকবে।
তখন ১ লাখ event-এর জন্য, last snapshot এবং নতুন events-গুলো replay করলেই হয়ে যাবে। Balance জেনে যাবো।
Projection: Read Model তৈরি করুন
এখানে যেহেতু কোনো single state না। আপনি চাইলেই এটাকে direct present করতে পারবেন না। এটাকে present করার জন্য কিছু কাজে লাগবে। সেই জিনিসটা হচ্ছে Projection (বা Projector Engine) । আর এই projection-এর মাধ্যমে read optimized view তৈরি করা হয়।
Projector ব্যাকগ্রাউন্ডে Event Store থেকে আসা নতুন ইভেন্টগুলো শোনে (Event Bus/Kafka-র মাধ্যমে) এবং সরাসরি Read Database-এ pre-computed বা সাজানো ডাটা আপডেট করে দেয়
For example:
// Projector যা Read Database আপডেট করে
eventBus.on("OrderPlaced", async (event) => {
await readDb.userDashboard.updateOne(
{ userId: event.userId },
{
$inc: {
totalOrders: 1,
totalSpent: event.amount,
},
$set: { lastOrderAt: event.timestamp },
},
{ upsert: true },
);
});
কখন ব্যবহার করবেন?
CQRS+Event Sourcing দরকার যখন:
- Audit log mandatory (banking, healthcare, legal)
- Complex reporting/analytics
- High read/write ratio difference
- Event replay দরকার হবে
দরকার নেই যখন:
- Simple CRUD application
- Small team, fast iteration
- History রাখার দরকার নেই
বটম লাইন
CQRS আর Event Sourcing powerful কিন্তু complexity আনে। Simple CRUD-এ এটা over-engineering। কিন্তু financial system, audit trail critical এমন system-এ — এটা ছাড়া চিন্তাই করা যায় না।
আপনি চাইলে শুধু cqrs pattern use করতে পারেন, যদি application-এর scale ছোট হয়। তবে complex এপ্লিকেশন এ দুই টা জিনিস একসাথেই বেশির ভাব ক্ষেত্রেই use হয়।
CQRS বা Event Sourcing কি আপনার কাজে লেগেছে? কমেন্টে শেয়ার করুন! 👇
Top comments (0)