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

আর্কিটেকচারাল স্টাইল — লেয়ার্ড ও ক্লায়েন্ট-সার্ভার

Architectural styles — layered & client-server
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • M4-M5-এর ক্লাস-লেভেল ডিজাইন ও M6-এর সিস্টেম-লেভেল আর্কিটেকচারের মধ্যে স্কোপের পার্থক্য
  • লেয়ার্ড আর্কিটেকচারের strict dependency নিয়ম ও এর honest trade-off
  • ক্লায়েন্ট-সার্ভার স্টাইলের request-response ইন্টারঅ্যাকশন প্যাটার্ন
  • দুটো স্টাইলই ছোট, বাস্তব Python সিমুলেশন দিয়ে

১ · সফটওয়্যার আর্কিটেকচার — জুম আউট করা লেন্স

সফটওয়্যার আর্কিটেকচারSoftware Architectureএকটি সম্পূর্ণ সিস্টেমের উচ্চ-স্তরের স্ট্রাকচারাল সংগঠন — MAJOR কম্পোনেন্টগুলো কীভাবে সম্পর্কিত, individual ক্লাস কীভাবে ডিজাইন করা তা নয়। M4-M5-এ আমরা দেখেছি individual ক্লাসগুলো কীভাবে ভালোভাবে ডিজাইন করা হয় (SOLID, ডিজাইন প্যাটার্ন) — সফটওয়্যার আর্কিটেকচার সেই লেন্স থেকে "জুম আউট" করে দেখে পুরো সিস্টেমের MAJOR কম্পোনেন্টগুলো কীভাবে সম্পর্কিত, ব্যক্তিগত ক্লাসের ভেতরের ডিজাইন নয়। এই মডিউল vocabulary/style-এর লেভেলে থাকবে — স্কেলেবিলিটি ও ডিস্ট্রিবিউটেড-সিস্টেমস-এর গভীর আলোচনার জন্য System Design কোর্স দেখুন, যেখানে এই স্টাইলগুলো বাস্তবে কীভাবে হাজারো/লক্ষো ইউজারের স্কেলে কাজ করে তা বিস্তারিত কভার করা হয়েছে।

২ · লেয়ার্ড আর্কিটেকচার

লেয়ার্ড আর্কিটেকচারLayered Architectureসিস্টেমকে অনুভূমিক লেয়ারে সংগঠিত করে (যেমন presentation, business-logic, data-access) — প্রতিটি লেয়ার শুধু তার ঠিক নিচের লেয়ারের উপর নির্ভর করে। সিস্টেমকে অনুভূমিক লেয়ারে (যেমন presentation layer, business-logic layer, data-access layer) সংগঠিত করে, যেখানে প্রতিটি লেয়ার শুধু তার ঠিক নিচের লেয়ারের উপর নির্ভর করে — সরাসরি M4/L18-এর DIPDependency Inversion Principleপ্রায়ই প্রতিটি লেয়ার নিচের লেয়ারের একটি ABSTRACTION-এর উপর নির্ভর করে, কংক্রিট বাস্তবায়নের উপর নয় — একটি স্ট্রাকচারাল ইকো। -এর একটি স্ট্রাকচারাল প্রতিধ্বনি। genuinely ব্যাপকভাবে ব্যবহৃত, সহজে বোধগম্য — কিন্তু একটি honest দুর্বলতাও আছে: একটি সাধারণ অপারেশনের জন্যও প্রায়ই রিকোয়েস্টকে প্রতিটি লেয়ার দিয়ে পাস করতে হয়, যা কিছুটা ওভারহেড যোগ করে।

PresentationLayer রিকোয়েস্ট ↓ BusinessLogicLayer রিকোয়েস্ট ↓ DataAccessLayer রেসপন্স ↑
রিকোয়েস্ট উপর থেকে নিচে নামে, রেসপন্স নিচ থেকে উপরে ফেরে — প্রতিটি লেয়ার শুধু তার ঠিক নিচেরটাকে রেফারেন্স রাখে, উপরেরটাকে কখনো নয়।
Python
# লেয়ার্ড আর্কিটেকচার সিমুলেশন -- প্রতিটি লেয়ার শুধু তার ঠিক নিচের লেয়ারকে
# চেনে, উপরেরটাকে না -- একটি রিকোয়েস্ট উপর থেকে নিচে নামে, রেসপন্স নিচ থেকে
# উপরে ফেরে।

class DataAccessLayer:
    def fetch_record(self, record_id):
        print(f"  [DataAccessLayer] record #{record_id} ডাটাবেস থেকে fetch করা হলো")
        return {"id": record_id, "name": "নাসরিন", "balance": 5000}

class BusinessLogicLayer:
    def __init__(self, data_layer):
        self.data_layer = data_layer  # শুধু ঠিক নিচের লেয়ারকে রেফারেন্স করে

    def get_account_summary(self, record_id):
        print("  [BusinessLogicLayer] অ্যাকাউন্ট সামারি তৈরি হচ্ছে...")
        record = self.data_layer.fetch_record(record_id)
        record["status"] = "সক্রিয়" if record["balance"] > 0 else "নিষ্ক্রিয়"
        return record

class PresentationLayer:
    def __init__(self, business_layer):
        self.business_layer = business_layer  # শুধু ঠিক নিচের লেয়ারকে রেফারেন্স করে

    def render_account_page(self, record_id):
        print(f"[PresentationLayer] রিকোয়েস্ট এলো: account #{record_id}")
        data = self.business_layer.get_account_summary(record_id)
        print(f"[PresentationLayer] ইউজারকে দেখানো হলো: {data['name']} "
              f"— ব্যালেন্স {data['balance']} — {data['status']}")

data_layer = DataAccessLayer()
business_layer = BusinessLogicLayer(data_layer)
presentation_layer = PresentationLayer(business_layer)

presentation_layer.render_account_page(7)

    

৩ · ক্লায়েন্ট-সার্ভার আর্কিটেকচার

ক্লায়েন্ট-সার্ভার আর্কিটেকচারClient-Server Architectureসিস্টেমকে service-requesting client ও service-providing server-এ ভাগ করে, একটি নির্দিষ্ট প্রোটোকল/ইন্টারফেসের মাধ্যমে যোগাযোগ করে। সিস্টেমকে service-requesting client ও service-providing server-এ ভাগ করে, যারা একটি ডিফাইনড প্রোটোকল/ইন্টারফেসের মাধ্যমে যোগাযোগ করে — সবচেয়ে পরিচিত everyday উদাহরণ: ওয়েব ব্রাউজার (client) ও ওয়েব সার্ভার। এই লেসনে আমরা in-process সিমুলেশন দেখবো (দুটোই একই Python প্রসেসে) — বাস্তবে এরা নেটওয়ার্কে আলাদা মেশিনে থাকে, আসল নেটওয়ার্ক-প্রোটোকল মেকানিক্সের জন্য Computer Networks কোর্স দেখুন।

Python
# ক্লায়েন্ট-সার্ভার আর্কিটেকচার -- ইন-প্রসেস সিমুলেশন (বাস্তবে এরা নেটওয়ার্কে
# আলাদা মেশিনে থাকে -- আসল নেটওয়ার্ক-প্রোটোকল মেকানিক্স computer-networks
# কোর্সে)।

class Server:
    def __init__(self):
        self._catalog = {"101": "ওয়্যারলেস মাউস", "102": "মেকানিক্যাল কিবোর্ড"}

    def handle_request(self, request):
        product_id = request.get("product_id")
        if product_id in self._catalog:
            return {"status": 200, "product": self._catalog[product_id]}
        return {"status": 404, "product": None}

class Client:
    def __init__(self, server):
        self.server = server

    def request_product(self, product_id):
        request = {"product_id": product_id}
        print(f"[Client] রিকোয়েস্ট পাঠানো হলো: product_id={product_id}")
        response = self.server.handle_request(request)
        print(f"[Client] সার্ভার থেকে রেসপন্স: {response}")
        return response

server = Server()
client = Client(server)

client.request_product("101")
client.request_product("999")

    
লক্ষ্য করুন "101" রিকোয়েস্টে সার্ভার status 200 সহ প্রোডাক্টের নাম ফেরত দেয়, কিন্তু "999" (যা catalog-এ নেই) status 404 ফেরত দেয়, প্রোডাক্ট None সহ — ঠিক বাস্তব HTTP status কোডের (200 OK, 404 Not Found) মতোই একটি structured request-response প্যাটার্ন।
মূল কথা · Key takeaway

লেয়ার্ড ও ক্লায়েন্ট-সার্ভার — দুটো ভিন্ন প্রশ্নের উত্তর দেয়: লেয়ার্ড বলে দেয় একটি সিস্টেমের ভেতরের কোড কীভাবে সংগঠিত হবে (উল্লম্ব দিকে responsibility ভাগ), ক্লায়েন্ট-সার্ভার বলে দেয় সিস্টেমের বিভিন্ন অংশ নেটওয়ার্কে কীভাবে ছড়িয়ে থাকবে (কে রিকোয়েস্ট করে, কে সার্ভ করে) — বাস্তবে এই দুটো প্রায়ই একসাথে ব্যবহৃত হয় (যেমন একটি সার্ভারের ভেতরেই লেয়ার্ড আর্কিটেকচার থাকতে পারে)।

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

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

প্র ০১ লেয়ার্ড আর্কিটেকচারে যদি PresentationLayer সরাসরি DataAccessLayer-এর মেথড কল করতো (মাঝের BusinessLogicLayer স্কিপ করে), এটি কেন লেয়ার্ড আর্কিটেকচারের নিয়ম লঙ্ঘন করবে?

কারণ লেয়ার্ড আর্কিটেকচারের নিয়ম হলো প্রতিটি লেয়ার শুধু তার ঠিক নিচের লেয়ারের উপর নির্ভর করবে — একটি লেয়ার "স্কিপ" করা মানে PresentationLayer এখন BusinessLogicLayer-এর কোনো business rule (যেমন balance-ভিত্তিক status নির্ধারণ) ছাড়াই সরাসরি raw ডেটার সাথে কাজ করছে — এটি লেয়ারগুলোর দায়িত্ব বিভাজন ভেঙে দেয় এবং DataAccessLayer-এর ইন্টারনাল স্ট্রাকচারের সাথে PresentationLayer-কে সরাসরি couple করে ফেলে।

প্র ০২ ক্লায়েন্ট-সার্ভার কোড সেলে Client কীভাবে জানে Server-এর ইন্টারনাল _catalog ডিকশনারিতে ঠিক কী আছে?

Client এটি জানেই না — এবং জানার দরকারও নেই। Client শুধু একটি request পাঠায় ও একটি response পায়, handle_request()-এর মধ্য দিয়ে — _catalog-এর ইন্টারনাল গঠন সম্পূর্ণ Server-এর ভেতরে encapsulated। এটিই client-server স্টাইলের একটি মূল সুবিধা: server তার ইন্টারনাল ইমপ্লিমেন্টেশন (যেমন catalog একটি ডিকশনারি থেকে একটি ডাটাবেসে বদলানো) বদলাতে পারে Client-এর কোনো কোড না বদলিয়েই, যতক্ষণ request/response ফরম্যাট একই থাকে।

প্র ০৩ লেয়ার্ড আর্কিটেকচারের "প্রতিটি রিকোয়েস্ট প্রতিটি লেয়ার দিয়ে পাস করতে হয়" দুর্বলতা কোন ধরনের সিস্টেমে সবচেয়ে বেশি সমস্যা তৈরি করতে পারে?

যেসব সিস্টেমে খুব উচ্চ-ফ্রিকোয়েন্সিতে, লো-লেটেন্সি রিকোয়ারমেন্ট সহ অনেক সাধারণ অপারেশন হয় (যেমন রিয়েল-টাইম ট্রেডিং সিস্টেম বা হাই-থ্রুপুট API) — সেখানে প্রতিটি লেয়ার পার হওয়ার সামান্য ওভারহেডও মোট লেটেন্সিতে যোগ হয়ে যায়। বিপরীতে, কম-ফ্রিকোয়েন্সি বা কম-সময়-সংবেদনশীল অপারেশনে এই ওভারহেড সাধারণত নগণ্য — এটিই কারণ লেয়ার্ড আর্কিটেকচার এখনো ব্যাপকভাবে ব্যবহৃত, নির্দিষ্ট high-performance পরিস্থিতিতে সতর্কতার সাথে প্রয়োগ করা প্রয়োজন।

অনুশীলন

  1. চিন্তা করুন: আপনার প্রিয় কোনো ওয়েবসাইট/অ্যাপের কথা চিন্তা করুন — এতে সম্ভাব্য কোন কোন লেয়ার (presentation, business-logic, data-access) থাকতে পারে তার একটি তালিকা করুন।

    উদাহরণস্বরূপ একটি ফুড-ডেলিভারি অ্যাপে: presentation layer = মোবাইল অ্যাপের UI (মেনু দেখানো, অর্ডার বাটন); business-logic layer = দাম হিসাব, ডেলিভারি-চার্জ নির্ধারণ, রেস্টুরেন্ট availability চেক; data-access layer = রেস্টুরেন্ট/মেনু/অর্ডার ডেটা ডাটাবেস থেকে fetch/সেভ করা। প্রতিটি লেয়ার আলাদা, এবং presentation layer কখনো সরাসরি ডাটাবেস কোয়েরি করে না — সবসময় business-logic layer-এর মধ্য দিয়ে যায়।

  2. পরীক্ষা করুন: ক্লায়েন্ট-সার্ভার কোড সেলে Server-এর _catalog-এ একটি নতুন প্রোডাক্ট ("103": "USB হাব") যোগ করুন এবং client.request_product("103") কল করে যাচাই করুন সঠিক রেসপন্স আসছে কিনা।

    self._catalog = {"101": "...", "102": "...", "103": "USB হাব"} — তারপর client.request_product("103") কল করলে {"status": 200, "product": "USB হাব"} রেসপন্স আসবে, ঠিক অন্য দুটো প্রোডাক্টের মতোই — Client ক্লাসের কোনো কোড বদলাতে হয়নি, শুধু Server-এর ইন্টারনাল ডেটা আপডেট করা হয়েছে।

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

আগের পাঠ
বিহেভিয়ারাল প্যাটার্ন — Command ও Iterator