REST আর্কিটেকচারাল প্রিন্সিপল
এই পাঠে যা শিখবেন
- REST-এর চারটি মূল নীতি — স্টেটলেসনেস, ইউনিফর্ম ইন্টারফেস, রিসোর্স-ভিত্তিক URL, ক্যাশেবিলিটি
- কেন স্টেটলেস সার্ভার একাধিক ইনস্ট্যান্স জুড়ে একইভাবে কাজ করে, স্টেটফুল সার্ভার কেন করে না
- রিসোর্স-ভিত্তিক URL ডিজাইনের মূল ধারণা (বিস্তারিত M6-এ)
- Python দিয়ে একটি সত্যিকারের স্টেটফুল বনাম স্টেটলেস সার্ভার তুলনা, একাধিক "সার্ভার ইনস্ট্যান্স" জুড়ে পরীক্ষা করে
১ · স্টেটলেসনেস — প্রতিটি রিকোয়েস্ট সম্পূর্ণ
REST-এর সবচেয়ে গুরুত্বপূর্ণ নীতি হলো স্টেটলেসনেসStatelessnessসার্ভার ক্লায়েন্টের আগের কোনো রিকোয়েস্টের তথ্য "মনে" রাখে না — প্রতিটি রিকোয়েস্টেই তার প্রক্রিয়া করার জন্য প্রয়োজনীয় সব তথ্য থাকতে হবে। — সার্ভার ক্লায়েন্টের কোনো "সেশন" নিজের মেমরিতে রাখে না। প্রতিটি রিকোয়েস্টে অথেন্টিকেশন টোকেন, প্রয়োজনীয় প্যারামিটার — সবকিছু থাকতে হবে, যাতে যেকোনো সার্ভার ইনস্ট্যান্স সেই রিকোয়েস্ট প্রসেস করতে পারে, আগের কোনো রিকোয়েস্ট কোন ইনস্ট্যান্স হ্যান্ডল করেছিল তা বিবেচনা না করেই।
২ · ইউনিফর্ম ইন্টারফেস, রিসোর্স-ভিত্তিক URL, ক্যাশেবিলিটি
সব রিসোর্সের সাথে মিথস্ক্রিয়া একইরকম, প্রমিত নিয়মে হয় — একই HTTP ভার্ব (GET/POST/PUT/DELETE) একই অর্থ বহন করে যেকোনো রিসোর্সের ক্ষেত্রে (বিস্তারিত L24)।
URL গুলো ক্রিয়া (verb) নয়, বিশেষ্য (noun/resource) বর্ণনা করে --
/users/42, /getUser?id=42 নয় (বিস্তারিত L23)।একটি রেসপন্স স্পষ্টভাবে জানায় সেটি ক্যাশ করা যাবে কি না, কতক্ষণের জন্য -- যাতে ক্লায়েন্ট বা মাঝের প্রক্সি বারবার একই রিকোয়েস্ট সার্ভারে না পাঠিয়ে সরাসরি ক্যাশ থেকে উত্তর দিতে পারে।
৩ · স্টেটফুল বনাম স্টেটলেস — একটি কার্যকর তুলনা
নিচের কোড সেলে দুটো পদ্ধতি তুলনা করা হয়েছে। প্রথমে একটি স্টেটফুল সার্ভার — যেখানে
login() কল করার পর সেই তথ্য সার্ভারের নিজের মেমরিতে (self.session) জমা থাকে।
দুটো আলাদা সার্ভার ইনস্ট্যান্স (server_a, server_b) কল্পনা করুন — বাস্তবে একটি
লোড-ব্যালান্সারের পেছনে দুটো আলাদা প্রসেস। তারপর একটি স্টেটলেস ফাংশন — যেখানে প্রতিটি কলে
প্রয়োজনীয় সব তথ্য (অথ টোকেন) সরাসরি পাঠানো হয়, কোনো সার্ভার নিজে কিছু মনে রাখে না।
# ---------- স্টেটফুল পদ্ধতি -- সার্ভার নিজেই সেশন ডেটা মনে রাখে ----------
class StatefulServer:
def __init__(self, name):
self.name = name
self.session = {} # সার্ভারের নিজস্ব মেমরিতে জমা থাকা "hidden" স্টেট
def login(self, username):
self.session["user"] = username
return f"{self.name}: লগইন সফল, {username}"
def get_profile(self):
user = self.session.get("user")
if user is None:
return f"{self.name}: এরর -- এই সার্ভারে কোনো ইউজার লগইন করা নেই"
return f"{self.name}: প্রোফাইল দেখাচ্ছে {user}-এর"
# দুটো আলাদা সার্ভার ইনস্ট্যান্স -- লোড-ব্যালান্সারের পেছনে দুটো আলাদা প্রসেস কল্পনা করুন
server_a = StatefulServer("সার্ভার-A")
server_b = StatefulServer("সার্ভার-B")
print("== স্টেটফুল পদ্ধতি ==")
print(server_a.login("রিমা")) # রিকোয়েস্ট ১ -- সার্ভার-A হ্যান্ডল করল, লগইন
print(server_a.get_profile()) # রিকোয়েস্ট ২ -- আবার সার্ভার-A, ঠিকঠাক কাজ করে
print(server_b.get_profile()) # রিকোয়েস্ট ৩ -- কিন্তু এবার সার্ভার-B হ্যান্ডল করল -- ব্যর্থ!
# ---------- স্টেটলেস পদ্ধতি -- প্রতিটি কলেই সম্পূর্ণ কনটেক্সট পাঠানো হয় ----------
def stateless_get_profile(server_name, auth_token, users_by_token):
user = users_by_token.get(auth_token)
if user is None:
return f"{server_name}: এরর -- টোকেন সঠিক নয়"
return f"{server_name}: প্রোফাইল দেখাচ্ছে {user}-এর"
# টোকেন থেকে ইউজারের ম্যাপিং একটি শেয়ার্ড ডেটাবেসে থাকে, কোনো একক সার্ভারের মেমরিতে নয়
users_by_token = {"tok-remA-123": "রিমা"}
print("\n== স্টেটলেস পদ্ধতি ==")
print(stateless_get_profile("সার্ভার-A", "tok-remA-123", users_by_token)) # সার্ভার-A হ্যান্ডল করল
print(stateless_get_profile("সার্ভার-B", "tok-remA-123", users_by_token)) # সার্ভার-B হ্যান্ডল করল -- একই ফলাফল!
print(stateless_get_profile("সার্ভার-C", "tok-remA-123", users_by_token)) # সার্ভার-C হ্যান্ডল করল -- এখনও একই ফলাফল!
server_b.get_profile() ব্যর্থ হয় — কারণ login()
কল হয়েছিল server_a-তে, আর সেই তথ্য শুধু server_a.session-এই আছে, server_b
তা জানে না। স্টেটলেস পদ্ধতিতে users_by_token ম্যাপিং কোনো একক সার্ভারের মেমরিতে না রেখে
প্রতিটি কলে সরাসরি পাঠানো হচ্ছে (বাস্তবে এটি একটি শেয়ার্ড ডেটাবেস বা JWT টোকেনের ভেতরেই এনকোড করা থাকে, দেখুন
L34) — তাই সার্ভার-A, B, C যেকোনোটিই একই সঠিক ফলাফল দেয়। এটাই দেখায় কেন স্টেটলেস ডিজাইন হরাইজন্টাল স্কেলিং
সহজ করে — নতুন সার্ভার ইনস্ট্যান্স যোগ করলেই কাজ চলতে থাকে, কোনো "স্টিকি সেশন" সমস্যা ছাড়াই।
REST-এর মূল নীতিগুলো — স্টেটলেসনেস, ইউনিফর্ম ইন্টারফেস, রিসোর্স-ভিত্তিক URL, ক্যাশেবিলিটি — একসাথে একটি API-কে সহজে বোঝা, টেস্ট করা, ও স্কেল করার যোগ্য করে তোলে। স্টেটলেসনেস বিশেষভাবে গুরুত্বপূর্ণ কারণ এটিই নিশ্চিত করে যেকোনো সার্ভার ইনস্ট্যান্স যেকোনো রিকোয়েস্ট হ্যান্ডল করতে পারে — এটি সরাসরি লোড-ব্যালান্সিং ও হাই-অ্যাভেইলেবিলিটির ভিত্তি তৈরি করে। পরের পাঠে (L08) আমরা দেখব একাধিক ব্যাকএন্ড সার্ভিসের সামনে একটি গেটওয়ে কীভাবে বসে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
কোড সেলে server_b.get_profile() এরর দেয় কেন, ঠিক কোন লাইনের কারণে?
কারণ server_b একটি সম্পূর্ণ আলাদা StatefulServer ইনস্ট্যান্স, যার নিজস্ব
খালি self.session = {} আছে। login() শুধু server_a.session-এ
ডেটা লিখেছিল, তাই server_b.get_profile()-এ self.session.get("user")
None ফেরত দেয়, এবং কোড সেই None চেক করে এরর মেসেজ ফেরত দেয়।
প্র ০২
স্টেটলেস পদ্ধতিতে users_by_token ডিকশনারিটি কোথায় "থাকা উচিত" বাস্তব সিস্টেমে, যদি
এটি কোনো একক সার্ভারের মেমরিতে না থাকে?
বাস্তবে এটি হয় একটি শেয়ার্ড ডেটাবেস/ক্যাশে (যেমন Redis) থাকতে পারে যা সব সার্ভার ইনস্ট্যান্স একসাথে অ্যাক্সেস করতে পারে, অথবা আরও "খাঁটি" স্টেটলেস পদ্ধতিতে এই তথ্য একদমই আলাদা সংরক্ষণ না করে সরাসরি টোকেনের ভেতরেই ক্রিপ্টোগ্রাফিকভাবে সাইন করে এনকোড করা থাকে (JWT, দেখুন L34) — তখন সার্ভারকে টোকেন যাচাই করতে কোনো বাহ্যিক স্টোরেজও লাগে না।
প্র ০৩ ইউনিফর্ম ইন্টারফেস নীতিটি কীভাবে API শেখার খরচ কমায়?
যদি প্রতিটি রিসোর্সের জন্য আলাদা নিয়মে ভার্ব ও URL ডিজাইন করা হতো (কোথাও GET /fetchUser,
কোথাও POST /user/get), তাহলে প্রতিটি নতুন এন্ডপয়েন্ট শেখার জন্য আলাদা ডকুমেন্টেশন পড়তে
হতো। ইউনিফর্ম ইন্টারফেসে একবার শিখলে (GET মানে পড়া, POST মানে তৈরি করা, ইত্যাদি) — সেই একই নিয়ম
প্রতিটি রিসোর্সে প্রযোজ্য হয়, তাই নতুন এন্ডপয়েন্ট আন্দাজ করা যায়, ডকুমেন্টেশন কম পড়তে হয়।
অনুশীলন
-
চিন্তা করুন: একটি শপিং কার্ট ফিচার প্রায়ই স্টেটফুলভাবে ডিজাইন করার প্রলোভন তৈরি করে
("সার্ভার মনে রাখুক ইউজার কী কী কার্টে রেখেছে")। REST-এর স্টেটলেসনেস নীতি মেনে এটি কীভাবে ডিজাইন করা
যেতে পারে?
কার্ট ডেটা সার্ভারের কোনো একক ইনস্ট্যান্সের মেমরিতে না রেখে একটি শেয়ার্ড ডেটাবেসে (ইউজার আইডি দিয়ে) সংরক্ষণ করা যায় — প্রতিটি রিকোয়েস্টে ইউজারের অথ টোকেন পাঠানো হয়, সার্ভার সেই টোকেন দিয়ে ডেটাবেস থেকে কার্ট লোড করে, কোনো ইনস্ট্যান্স-নির্দিষ্ট মেমরি স্টেট ছাড়াই। এভাবে যেকোনো সার্ভার ইনস্ট্যান্স যেকোনো রিকোয়েস্ট হ্যান্ডল করতে পারে, ঠিক যেমন উপরের কোড সেলের স্টেটলেস
get_profile-এ দেখানো হয়েছে। -
পরীক্ষা করুন: উপরের কোড সেলে
users_by_token-এ আরেকটি টোকেন"tok-karX-456": "করিম"যোগ করুন, তারপরstateless_get_profile-কে একটি ভুল টোকেন (যেমন"tok-fake-999") দিয়ে কল করে দেখুন এরর মেসেজটি সঠিকভাবে আসে কি না।নতুন টোকেন যোগ করার পর
stateless_get_profile("সার্ভার-A", "tok-karX-456", users_by_token)কল করলে সঠিকভাবে "প্রোফাইল দেখাচ্ছে করিম-এর" ফেরত দেবে। আর"tok-fake-999"দিয়ে কল করলেusers_by_token.get("tok-fake-999")Noneফেরত দেয়, তাই কোড "এরর -- টোকেন সঠিক নয়" প্রিন্ট করবে — কোন সার্ভার-নাম দিয়ে কল করা হয়েছে তা এই ফলাফলে কোনো প্রভাব ফেলে না, যা স্টেটলেসনেসের মূল বৈশিষ্ট্য।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরবর্তী পাঠ L08 API গেটওয়ে ও ব্যাক-এন্ড-ফর-ফ্রন্ট-এন্ড (BFF) প্যাটার্ন দেখুন।
- আগের পাঠ L06 MVVM ও MVP প্যাটার্ন — এই পাঠের আগের ধাপ।
- System Design কোর্স সহোদর কোর্স স্টেটলেসনেস কীভাবে বৃহত্তর স্কেলে লোড-ব্যালান্সিং ও হাই-অ্যাভেইলেবিলিটি সক্ষম করে, তার বিস্তারিত সেই কোর্সে আছে।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics ও Full-Stack Web Frameworks — সব এক জায়গায়।