MVC আর্কিটেকচার প্যাটার্ন
এই পাঠে যা শিখবেন
- MVC-এর তিনটি অংশের প্রতিটির সঠিক দায়িত্ব ও সীমারেখা
- একটি রিকোয়েস্ট MVC-তে ঠিক কোন ক্রমে প্রবাহিত হয় (Controller → Model → View → রেসপন্স)
- কেন এই আলাদাকরণ (separation of concerns) কোড মেইনটেইন করা সহজ করে
- Python দিয়ে একটি সম্পূর্ণ, সত্যিকারের কার্যকর
TodoMVC ত্রয়ী — Model, View, Controller তিনটিই আসল ক্লাস হিসেবে
১ · MVC-এর তিনটি অংশ
MVCModel-View-Controllerএকটি আর্কিটেকচার প্যাটার্ন যা অ্যাপ্লিকেশনকে ডেটা, প্রেজেন্টেশন, ও অর্কেস্ট্রেশন — তিনটি আলাদা দায়িত্বে ভাগ করে। হলো ওয়েব অ্যাপ্লিকেশনের সবচেয়ে পুরনো ও ব্যাপকভাবে ব্যবহৃত আর্কিটেকচার প্যাটার্নগুলোর একটি। এটি একটি অ্যাপ্লিকেশনের কোডকে তিনটি স্পষ্ট দায়িত্বে ভাগ করে, যাতে প্রতিটি অংশ শুধু নিজের কাজেই মনোযোগ দেয়।
ডেটা ও বিজনেস লজিক ধারণ করে — কীভাবে ডেটা তৈরি, পড়া, পরিবর্তন হয়। View বা Controller সম্পর্কে কিছুই জানে না।
ডেটা কীভাবে ব্যবহারকারীর কাছে উপস্থাপন করা হবে তা ঠিক করে। শুধু দেওয়া ডেটা রেন্ডার করে — নিজে ডেটা আনে না বা বদলায় না।
রিকোয়েস্ট গ্রহণ করে, প্রয়োজনমতো Model-কে কল করে, ফলাফল View-কে দিয়ে রেন্ডার করায়, ও চূড়ান্ত রেসপন্স ফেরত দেয়।
L01-এ দেখা Router ক্লাসটি একটি রিকোয়েস্টের পাথকে একটি হ্যান্ডলার ফাংশনে ম্যাপ করেছিল। MVC-তে
সেই হ্যান্ডলার ফাংশনটিই হলো Controller-এর একটি মেথড — Router শুধু ঠিক করে কোন Controller অ্যাকশন কল হবে,
Controller নিজে Model ও View সমন্বয় করার কাজটি করে।
২ · সম্পূর্ণ MVC ত্রয়ী — একটি Todo রিসোর্স
নিচের কোড সেলে একটি সম্পূর্ণ, সত্যিকারের কার্যকর MVC ত্রয়ী তৈরি করা হয়েছে Todo রিসোর্সের জন্য —
TodoModel ডেটা ধারণ করে, TodoView সেই ডেটা টেক্সট আকারে ফরম্যাট করে, এবং
TodoController রিকোয়েস্ট গ্রহণ করে দুটোকে সমন্বয় করে। ছয়টি ভিন্ন রিকোয়েস্ট পাঠিয়ে পুরো প্রবাহ
ট্রেস করা হয়েছে।
# ---------- MODEL -- শুধু ডেটা ও ডেটা-সম্পর্কিত লজিক ----------
class TodoModel:
def __init__(self):
self._todos = []
self._next_id = 1
def add(self, title):
todo = {"id": self._next_id, "title": title, "done": False}
self._todos.append(todo)
self._next_id += 1
return todo
def toggle_done(self, todo_id):
for todo in self._todos:
if todo["id"] == todo_id:
todo["done"] = not todo["done"]
return todo
return None
def all(self):
return list(self._todos)
# ---------- VIEW -- শুধু প্রেজেন্টেশন, নিজে ডেটা আনে না ----------
class TodoView:
@staticmethod
def render_list(todos):
if not todos:
return "(কোনো টুডু নেই)"
lines = []
for t in todos:
mark = "[x]" if t["done"] else "[ ]"
lines.append(f"{mark} #{t['id']} {t['title']}")
return "\n".join(lines)
@staticmethod
def render_single(todo):
if todo is None:
return "404 Not Found -- এই টুডু নেই"
mark = "সম্পন্ন" if todo["done"] else "বাকি আছে"
return f"টুডু #{todo['id']}: {todo['title']} -- {mark}"
# ---------- CONTROLLER -- রিকোয়েস্ট গ্রহণ ও Model/View সমন্বয় ----------
class TodoController:
def __init__(self, model, view):
self.model = model
self.view = view
def handle_request(self, action, params=None):
params = params or {}
if action == "add_todo":
todo = self.model.add(params["title"])
return self.view.render_single(todo)
elif action == "toggle_todo":
todo = self.model.toggle_done(params["id"])
return self.view.render_single(todo)
elif action == "list_todos":
todos = self.model.all()
return self.view.render_list(todos)
else:
return "404 Not Found"
model = TodoModel()
view = TodoView()
controller = TodoController(model, view)
print("== রিকোয়েস্ট ১: add_todo ==")
print(controller.handle_request("add_todo", {"title": "দুধ কেনা"}))
print("\n== রিকোয়েস্ট ২: add_todo ==")
print(controller.handle_request("add_todo", {"title": "রিপোর্ট জমা দেওয়া"}))
print("\n== রিকোয়েস্ট ৩: list_todos ==")
print(controller.handle_request("list_todos"))
print("\n== রিকোয়েস্ট ৪: toggle_todo id=1 ==")
print(controller.handle_request("toggle_todo", {"id": 1}))
print("\n== রিকোয়েস্ট ৫: list_todos (আবার) ==")
print(controller.handle_request("list_todos"))
print("\n== রিকোয়েস্ট ৬: অজানা অ্যাকশন ==")
print(controller.handle_request("delete_everything"))
TodoModel ক্লাসের ভেতর কোথাও TodoView-এর কোনো উল্লেখ নেই, এবং
TodoView-এর মেথডগুলো শুধু যা প্যারামিটার হিসেবে দেওয়া হয় তাই রেন্ডার করে — নিজে
model._todos স্পর্শ করে না। রিকোয়েস্ট ৪-এ toggle_todo কল করার পর রিকোয়েস্ট ৫-এর
list_todos-এ #1 টুডুর মার্ক [ ] থেকে [x]-এ বদলে যায় — কারণ
Controller প্রতিবারই Model থেকে সর্বশেষ ডেটা তাজা করে আনে।
৩ · কেন এই আলাদাকরণ গুরুত্বপূর্ণ
MVC-তে প্রতিটি অংশ স্বাধীনভাবে বদলানো যায় — Controller ও Model অপরিবর্তিত রেখে TodoView-এর
আউটপুট ফরম্যাট (যেমন প্লেইন টেক্সটের বদলে HTML বা JSON) বদলে ফেলা যায়, কারণ View শুধু ডেটা গ্রহণ করে ও
ফরম্যাট করে — কীভাবে সেই ডেটা তৈরি হলো তা নিয়ে মাথা ঘামায় না। একইভাবে Model-এর ভেতরের স্টোরেজ (এখানে একটি
পাইথন লিস্ট, বাস্তবে একটি ডেটাবেস টেবিল হতে পারে) বদলে ফেললেও Controller বা View-এর কোনো কোড বদলাতে হয় না,
যতক্ষণ Model-এর পাবলিক মেথডগুলোর (add, toggle_done, all) স্বাক্ষর
একই থাকে।
MVC একটি অ্যাপ্লিকেশনকে তিনটি স্পষ্ট দায়িত্বে ভাগ করে — Model (ডেটা), View (প্রেজেন্টেশন), Controller (অর্কেস্ট্রেশন)। একটি রিকোয়েস্ট সবসময় Controller দিয়ে শুরু হয়, যেটি Model থেকে ডেটা এনে View-কে দিয়ে রেন্ডার করায় ও চূড়ান্ত রেসপন্স ফেরত দেয়। এই আলাদাকরণের ফলে প্রতিটি অংশ স্বাধীনভাবে পরীক্ষা ও পরিবর্তন করা যায় — পরের পাঠে (L06) আমরা দেখব MVVM ও MVP কীভাবে এই একই সমস্যার ভিন্ন সমাধান দেয়।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
উপরের কোডে TodoModel কেন কখনো সরাসরি প্রিন্ট বা কোনো টেক্সট ফরম্যাট করে না?
কারণ প্রেজেন্টেশন (কীভাবে দেখানো হবে) Model-এর দায়িত্ব নয় — সেটা View-এর কাজ। Model যদি নিজেই টেক্সট ফরম্যাট করত, তাহলে ভবিষ্যতে একই ডেটা JSON বা HTML আকারে দেখাতে চাইলে Model-এর কোড বদলাতে হতো, যা আলাদাকরণের মূল লক্ষ্যকে ভেঙে দেয়। Model শুধু কাঁচা ডেটা (dict) ফেরত দেয়, View সিদ্ধান্ত নেয় সেটা কীভাবে উপস্থাপিত হবে।
প্র ০২
TodoController.handle_request-এ কেন Model বা View-এর ভেতরের কোনো বিস্তারিত (যেমন
_todos লিস্ট) সরাসরি স্পর্শ করা হয় না?
Controller শুধু Model ও View-এর পাবলিক মেথড কল করে (model.add(...),
view.render_list(...)) — তাদের ভেতরের ডেটা স্ট্রাকচার সম্পর্কে কিছু জানে না বা জানার
প্রয়োজন নেই। এভাবে Model-এর ভেতরের স্টোরেজ (লিস্ট থেকে ডেটাবেস টেবিলে) বদলে ফেললেও Controller-এর কোনো
কোড বদলাতে হয় না, যতক্ষণ মেথডের স্বাক্ষর একই থাকে।
প্র ০৩
রিকোয়েস্ট ৬-এ "delete_everything" অ্যাকশন কেন "404 Not Found" ফেরত দেয়,
অথচ Model বা View-এ কোনো এরর হ্যান্ডলিং কোড নেই?
কারণ "delete_everything" নামে কোনো if/elif শাখা handle_request-এ
নেই, তাই এক্সিকিউশন সরাসরি শেষের else-এ পড়ে, যা "404 Not Found" ফেরত দেয় —
এই ক্ষেত্রে Model বা View-কে একদমই কল করা হয় না। এটাই দেখায় Controller-ই একমাত্র জায়গা যেখানে "কোন
রিকোয়েস্ট বৈধ" এই সিদ্ধান্ত নেওয়া হয়।
অনুশীলন
-
চিন্তা করুন: যদি
TodoView.render_list-এর ভেতর সরাসরিmodel.all()কল করা হতো (Controller-এর মাধ্যমে ডেটা না পাঠিয়ে), তাহলে MVC-এর কোন নিয়মটি ভাঙা হতো, এবং এর ফলে কী সমস্যা হতে পারে?এতে View সরাসরি Model-এর উপর নির্ভরশীল হয়ে যেত, যা "View কখনো নিজে ডেটা আনে না" নিয়মটি ভাঙে। ফলে View-কে আলাদাভাবে টেস্ট করা কঠিন হয়ে যেত (সবসময় একটি আসল Model দরকার হতো), এবং একই View বিভিন্ন উৎসের ডেটা দিয়ে (যেমন টেস্টের জন্য ভুয়া ডেটা) ব্যবহার করা যেত না — Controller-ই একমাত্র জায়গা হওয়া উচিত যেখানে Model ও View একসাথে সংযুক্ত হয়।
-
পরীক্ষা করুন: উপরের কোড সেলে
TodoController-এ একটি নতুন অ্যাকশন"delete_todo"যোগ করুন যাself.model._todosথেকে নির্দিষ্টid-র টুডু সরিয়ে দেয় (Model-এ একটি নতুনdeleteমেথড লিখে), তারপরlist_todosআবার কল করে দেখুন টুডুটি সত্যিই বাদ পড়েছে কি না।TodoModel-এdef delete(self, todo_id): self._todos = [t for t in self._todos if t["id"] != todo_id]যোগ করে,TodoController-এelif action == "delete_todo": self.model.delete(params["id"]); return self.view.render_list(self.model.all())যোগ করলেdelete_todoকলের পর পরবর্তীlist_todos-এর আউটপুটে সেই টুডুটি আর দেখাবে না — এটাই দেখায় নতুন ফিচার যোগ করতে শুধু Model ও Controller-এ পরিবর্তন লাগে, View অপরিবর্তিত থাকে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরবর্তী পাঠ L06 MVVM ও MVP প্যাটার্ন — MVC-এর দুটি বিকল্প ও তাদের ভিন্ন সমাধান দেখুন।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ আর্কিটেকচার প্যাটার্ন, ফ্রন্ট-এন্ড/ব্যাক-এন্ড ফ্রেমওয়ার্ক ফান্ডামেন্টাল, স্টেট ম্যানেজমেন্ট, REST API, ORM, অথেন্টিকেশন, রেন্ডারিং স্ট্র্যাটেজি ও ডিপ্লয়মেন্ট।
- Python Programming কোর্স সহোদর কোর্স উপরের কোড সেলে ব্যবহৃত ক্লাস, ডিকশনারি ও লিস্ট কমপ্রিহেনশনের ভাষাগত ভিত্তি সেই কোর্সে শেখানো হয়েছে।
- সব 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 — সব এক জায়গায়।