পাঠ ২৮ · ৫৮-এর মধ্যে · মডিউল ৬
Home / Courses / Software Engineering Principles & Git / সফটওয়্যার আর্কিটেকচার

MVC ও MVVM প্যাটার্ন

MVC & MVVM patterns
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • MVC-এর তিনটি অংশ — Model, View, Controller — প্রতিটির স্পষ্ট, আলাদা দায়িত্ব
  • একটি ইউজার ইন্টারঅ্যাকশন MVC-র মধ্য দিয়ে ঠিক কোন ক্রমে প্রবাহিত হয়
  • MVVM কীভাবে Controller-কে ViewModel দিয়ে প্রতিস্থাপন করে, এবং মূল পার্থক্যটি কী
  • একটি বাস্তব Python MVC Todo অ্যাপ বানিয়ে সম্পূর্ণ ডেটা ফ্লো হাতে-কলমে দেখা

১ · MVC — তিনটি স্পষ্ট দায়িত্ব

MVCModel-View-Controllerএকটি আর্কিটেকচারাল প্যাটার্ন যা ইন্টারঅ্যাক্টিভ অ্যাপ্লিকেশনকে তিনটি ভাগে ভাগ করে — Model (ডেটা), View (প্রদর্শন), Controller (ইনপুট হ্যান্ডলিং)। একটি genuinely ব্যাপকভাবে ব্যবহৃত, বাস্তব আর্কিটেকচারাল প্যাটার্ন — ইন্টারঅ্যাক্টিভ অ্যাপ্লিকেশনগুলোকে তিনটি স্পষ্ট অংশে ভাগ করে (L14-এর separation-of-concerns থিমের সরাসরি প্রয়োগ):

Model
অ্যাপ্লিকেশনের ডেটা ও বিজনেস লজিক ধরে রাখে — কোনো UI-সংক্রান্ত ধারণা ছাড়াই, সম্পূর্ণ স্বাধীন।
View
Model-এর ডেটা ইউজারকে দেখায়/রেন্ডার করে — যতটা সম্ভব "dumb"/প্যাসিভ রাখা হয়, নিজে কোনো সিদ্ধান্ত নেয় না।
Controller
ইউজার ইনপুট হ্যান্ডল করে, সেই অনুযায়ী Model আপডেট করে, এবং View-কে নির্বাচন/আপডেট করে — অন্য দুইয়ের মধ্যে coordinator।
ইউজার View Controller Model Model বদলালে Observer-এর মাধ্যমে View স্বয়ংক্রিয়ভাবে আপডেট হয়
ইউজার → View → Controller → Model, তারপর Model-এর পরিবর্তন Observer প্যাটার্নের (M5/L23) মাধ্যমে সরাসরি View-কে জানানো হয় — Controller প্রতিবার সরাসরি View push করে না।

স্বাভাবিক ডেটা ফ্লো এভাবে: ইউজার View-এর সাথে ইন্টারঅ্যাক্ট করে → View সেই ইনপুট Controller-এ পাঠায় → Controller Model আপডেট করে → Model-এর পরিবর্তন View-কে আপডেট হতে ট্রিগার করে। শেষ ধাপটি লক্ষ্য করার মতো — অনেক বাস্তব MVC বাস্তবায়নে View সরাসরি Model-কে observe করে (M5/L23-এর Observer প্যাটার্নের সরাসরি পুনর্ব্যবহার), Controller নিজে সরাসরি View-তে push করে না।

genuine সুবিধা

এই তিনটি দায়িত্ব আলাদা করার ফল — একই Model একাধিক ভিন্ন View-তে দেখানো যায় (যেমন একটি ওয়েব View আর একটি মোবাইল View), বিজনেস লজিক কোথাও ডুপ্লিকেট না করেই — এটি L15-এর DRY নীতির সরাসরি প্রয়োগ, কিন্তু এখন আর্কিটেকচার লেভেলে।

২ · MVVM — Controller-এর বদলে ViewModel

MVVMModel-View-ViewModelMVC-এর একটি রূপভেদ, যেখানে Controller-এর বদলে একটি ViewModel থাকে যা Model-এর ডেটা View-ফ্রেন্ডলি ফরম্যাটে এক্সপোজ করে, আর View সরাসরি তার সাথে বাইন্ড করে। MVC-এর একটি real, বহুল-ব্যবহৃত রূপভেদ — বিশেষত আধুনিক UI ফ্রেমওয়ার্কে। কাঠামো প্রায় একই থাকে, কিন্তু Controller-এর জায়গায় একটি ViewModel বসে — এটি Model-এর ডেটাকে View-ফ্রেন্ডলি, প্রি-ফরম্যাটেড রূপে এক্সপোজ করে এবং View-নির্দিষ্ট প্রেজেন্টেশন লজিক সামলায়, আর View সরাসরি ViewModel-এর এক্সপোজ করা প্রপার্টিগুলোর সাথে বাইন্ড করে (একটি ডেটা-বাইন্ডিং মেকানিজম, শুধু নামমাত্র উল্লেখ) — Controller ম্যানুয়ালি প্রতিটি আপডেট push করার বদলে।

মূল পার্থক্যটি নির্দিষ্ট করে বলা যাক: MVVM-এর View সাধারণত তার ViewModel-এর সাথে আরও ডিক্লারেটিভভাবে সংযুক্ত থাকে (স্বয়ংক্রিয় বাইন্ডিং), MVC-র তুলনায় — MVC-তে Controller বেশিরভাগ ক্ষেত্রে ম্যানুয়ালি View আপডেট মধ্যস্থতা করে।

৩ · একটি বাস্তব MVC — Todo অ্যাপ্লিকেশন

নিচের কোড সেলে একটি সম্পূর্ণ, ordinary Python OOP MVC ট্রায়ো বানানো হয়েছে — এটি কোনো সিমুলেশন নয়, বাস্তব, সম্পূর্ণ কার্যকরী কোড: একটি TodoModel (ডেটা + Observer সাপোর্ট), একটি TodoView (print-ভিত্তিক রেন্ডারার, Model-এ সাবস্ক্রাইবড), আর একটি TodoController (Model আপডেট করার মেথড এক্সপোজ করে)।

Python
class TodoModel:
    def __init__(self):
        self.items = []       # প্রতিটি item: {"text": ..., "done": False}
        self.observers = []   # M5/L23-এর Observer প্যাটার্নের সরাসরি পুনর্ব্যবহার

    def subscribe(self, observer):
        self.observers.append(observer)

    def _notify(self):
        for obs in self.observers:
            obs.render(self.items)

    def add_item(self, text):
        self.items.append({"text": text, "done": False})
        self._notify()   # Model বদলাতেই স্বয়ংক্রিয়ভাবে View-কে জানানো হচ্ছে

    def complete_item(self, index):
        self.items[index]["done"] = True
        self._notify()


class TodoView:
    def render(self, items):
        print("---- Todo List (View) ----")
        for i, item in enumerate(items):
            mark = "x" if item["done"] else " "
            print(f"[{mark}] {i}: {item['text']}")
        print()


class TodoController:
    def __init__(self, model):
        self.model = model

    def add_todo(self, text):
        self.model.add_item(text)

    def complete_todo(self, index):
        self.model.complete_item(index)


model = TodoModel()
view = TodoView()
model.subscribe(view)          # View, Model-কে observe করছে
controller = TodoController(model)

controller.add_todo("Git শেখা")
controller.add_todo("MVC পাঠ শেষ করা")
controller.complete_todo(0)

    
লক্ষ্য করুন TodoController-এর কোনো মেথডেই print() কল নেই — Controller শুধু Model আপডেট করে, বাকিটা _notify()-এর মাধ্যমে স্বয়ংক্রিয়ভাবে ঘটে। প্রতিটি add_todo/complete_todo কলের পর View স্বয়ংক্রিয়ভাবে পুরো লিস্টের সর্বশেষ অবস্থা প্রিন্ট করে — এটিই MVC-র "Model বদলালে View নিজে থেকে আপডেট হয়" নীতির concrete প্রমাণ।
মূল কথা · Key takeaway

MVC তিনটি স্পষ্ট দায়িত্বে ভাগ করে — Model (ডেটা), View (প্রদর্শন), Controller (ইনপুট) — আর View-কে Model-এর Observer বানিয়ে দেয়, যাতে ম্যানুয়াল push ছাড়াই আপডেট প্রচারিত হয়। MVVM একই কাঠামো, কিন্তু Controller-এর জায়গায় একটি ViewModel বসিয়ে View-ViewModel সংযোগকে আরও ডিক্লারেটিভ বানায়। দুটোই একই মূল লক্ষ্য অর্জন করে — বিজনেস লজিক থেকে প্রদর্শন-লজিক আলাদা রাখা, যাতে একই Model একাধিক View-তে পুনর্ব্যবহারযোগ্য থাকে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ উপরের কোড সেলে TodoView.render() মেথডটি সরাসরি কোথাও কল করা হয়নি, তবুও এটি তিনবার চলে। কেন?

কারণ view অবজেক্টটি model.subscribe(view)-এর মাধ্যমে Model-এর observer লিস্টে যোগ হয়েছে — প্রতিবার add_item বা complete_item কল হলে Model নিজে self._notify() কল করে, যা প্রতিটি সাবস্ক্রাইবড observer-এর (এখানে শুধু view) render() মেথড কল করে। Controller বা কোনো ব্যবহারকারী কোড কখনো সরাসরি view.render() কল করেনি — এটিই Observer-চালিত স্বয়ংক্রিয় আপডেট।

প্র ০২ MVVM-এ View আর ViewModel-এর মধ্যে সংযোগকে "বেশি ডিক্লারেটিভ" বলা হয় — এর মানে ঠিক কী, MVC-র Controller-ভিত্তিক সংযোগের তুলনায়?

MVC-তে Controller প্রতিটি ক্ষেত্রে ম্যানুয়ালি সিদ্ধান্ত নেয় ঠিক কখন ও কীভাবে View আপডেট হবে (উপরের কোড সেলে এটি Observer-এর মাধ্যমে হচ্ছে, কিন্তু অনেক সরল MVC বাস্তবায়নে Controller নিজেই সরাসরি View-তে push করে)। MVVM-এ View শুধু ঘোষণা করে "আমার এই অংশটি ViewModel-এর এই প্রপার্টির সাথে বাইন্ডেড" — ফ্রেমওয়ার্ক নিজে থেকেই সেই প্রপার্টি বদলালে View আপডেট করে, কোনো ম্যানুয়াল push কোড লেখা ছাড়াই।

প্র ০৩ যদি একই TodoModel-এ একটি দ্বিতীয় View (ধরুন একটি JSON-এক্সপোর্ট করা View) সাবস্ক্রাইব করানো হয়, তাহলে TodoController-এর কোডে কী পরিবর্তন লাগবে?

কিছুই না। TodoController শুধু self.model.add_item(...)/ self.model.complete_item(...) কল করে — Model-এ কতগুলো বা কী ধরনের View সাবস্ক্রাইবড আছে, সে বিষয়ে Controller-এর কোনো ধারণাই নেই। এটিই এই পাঠের মূল সুবিধার সরাসরি প্রমাণ — একাধিক View একই Model শেয়ার করতে পারে, Controller বা Model-এর কোডে কোনো পরিবর্তন ছাড়াই।

অনুশীলন

  1. চিন্তা করুন: একটি ব্লগ অ্যাপ্লিকেশনে MVC প্রয়োগ করলে Model, View, Controller-এ ঠিক কী কী থাকবে তার একটি সংক্ষিপ্ত তালিকা লিখুন (উদাহরণ: পোস্ট ডেটা, HTML রেন্ডারিং, "নতুন কমেন্ট" ফর্ম হ্যান্ডলিং)।

    সম্ভাব্য বিভাজন: Model — পোস্টের টেক্সট, লেখক, কমেন্ট লিস্ট, এবং পোস্ট সেভ/লোড/ভ্যালিডেট করার বিজনেস লজিক। View — পোস্টের HTML রেন্ডারিং, কমেন্ট ফর্মের প্রদর্শন। Controller — "নতুন কমেন্ট জমা দিন" ফর্ম সাবমিশন হ্যান্ডল করা, ইনপুট পড়ে Model-এর add_comment(...)-জাতীয় মেথড কল করা, এবং কোন View রেন্ডার হবে তা ঠিক করা।

  2. পরীক্ষা করুন: উপরের কোড সেলে TodoModel-এ একটি remove_item(index) মেথড যোগ করুন (যা _notify()-ও কল করে), এবং TodoController-এ একটি সংশ্লিষ্ট remove_todo(index) মেথড যোগ করে একটি আইটেম মুছে দেখুন — View স্বয়ংক্রিয়ভাবে আপডেটেড লিস্ট দেখাচ্ছে কিনা যাচাই করুন।

    remove_item মেথডটি self.items.pop(index) (বা অনুরূপ) করে তারপর self._notify() কল করলেই যথেষ্ট — যেহেতু view ইতিমধ্যে সাবস্ক্রাইবড, তাই এই নতুন মেথডটিও স্বয়ংক্রিয়ভাবে View আপডেট ট্রিগার করবে, বাড়তি কোনো কোড ছাড়াই — Observer-চালিত ডিজাইনের একটি সরাসরি সম্প্রসারণ, নতুন কোনো wiring ছাড়াই।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
ইভেন্ট-ড্রিভেন আর্কিটেকচার