পাঠ ০৮ · ৫৮-এর মধ্যে · মডিউল ২
Home / Courses / Full-Stack Web Frameworks / API গেটওয়ে ও BFF প্যাটার্ন

API গেটওয়ে ও ব্যাক-এন্ড-ফর-ফ্রন্ট-এন্ড (BFF) প্যাটার্ন

API Gateway and Backend-for-Frontend (BFF) pattern
৯ মিনিট পড়া শুরু · Beginner Python কোডসহ সম্পূর্ণ বাংলায়

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

  • API গেটওয়ে প্যাটার্ন কী ও কেন মাইক্রোসার্ভিস আর্কিটেকচারে দরকারি
  • BFF প্যাটার্ন কীভাবে গেটওয়ের ধারণাকে ক্লায়েন্ট-নির্দিষ্ট রেসপন্স শেপিং পর্যন্ত বিস্তৃত করে
  • কেন একই ডেটা মোবাইল ও ওয়েব ক্লায়েন্টের জন্য ভিন্নভাবে ফেরত পাঠানো লাভজনক
  • Python দিয়ে একটি সত্যিকারের কার্যকর গেটওয়ে যা একাধিক ব্যাকএন্ড সার্ভিসে রাউট করে ও ক্লায়েন্ট টাইপ অনুযায়ী রেসপন্স রিশেপ করে

১ · একাধিক মাইক্রোসার্ভিসের সামনে একটি একক এন্ট্রি পয়েন্ট

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

মোবাইল ক্লায়েন্ট ওয়েব ক্লায়েন্ট API গেটওয়ে রাউটিং + রেসপন্স শেপিং ইউজার সার্ভিস অর্ডার সার্ভিস রেকমেন্ডেশন সার্ভিস
গেটওয়ে ক্লায়েন্ট টাইপ শনাক্ত করে, প্রয়োজনীয় ব্যাকএন্ড সার্ভিসগুলো কল করে ফলাফল একত্র করে, ও প্রতিটি ক্লায়েন্ট টাইপের উপযোগী আকারে রেসপন্স ফেরত পাঠায়।

২ · BFF — ক্লায়েন্ট-নির্দিষ্ট রেসপন্স শেপিং

সাধারণ API গেটওয়ে শুধু রাউট করে, কিন্তু BFF (Backend-for-Frontend) আরেক ধাপ এগিয়ে — একই আন্ডারলাইং ডেটা মোবাইল ও ওয়েব ক্লায়েন্টের জন্য ভিন্ন আকারে সাজিয়ে দেয়। মোবাইল ডিভাইসে ব্যান্ডউইথ ও স্ক্রিন স্পেস সীমিত থাকে, তাই মোবাইলকে সাধারণত ছোট, সংক্ষিপ্ত রেসপন্স দেওয়া হয়; ওয়েব ক্লায়েন্ট বড় স্ক্রিনে বেশি ডেটা একসাথে দেখাতে পারে, তাই তাকে পূর্ণাঙ্গ, বিস্তারিত রেসপন্স দেওয়া হয়।

Python
# ---------- ব্যাকএন্ড সার্ভিস সিমুলেশন -- একাধিক আলাদা মাইক্রোসার্ভিস ----------
def user_service(user_id):
    return {
        "id": user_id,
        "name": "রিমা আক্তার",
        "email": "rima@example.com",
        "bio": "সফটওয়্যার ইঞ্জিনিয়ার, ঢাকা।",
        "joined": "2021-03-14",
    }

def orders_service(user_id):
    return [
        {"order_id": 101, "item": "কীবোর্ড", "price": 1200, "status": "ডেলিভারড"},
        {"order_id": 102, "item": "মাউস", "price": 450, "status": "প্রসেসিং"},
    ]

def recommendations_service(user_id):
    return ["মনিটর স্ট্যান্ড", "USB হাব", "ল্যাপটপ স্লিভ"]


# ---------- API গেটওয়ে (BFF) -- রাউট করে, একত্র করে, ক্লায়েন্ট-নির্দিষ্ট শেপে সাজায় ----------
def api_gateway(path, params, client_type="web"):
    if path == "/dashboard":
        # একাধিক ব্যাকএন্ড সার্ভিস থেকে ডেটা একত্র করা (aggregation) -- এটাই গেটওয়ের মূল কাজ
        user = user_service(params["user_id"])
        orders = orders_service(params["user_id"])
        recommendations = recommendations_service(params["user_id"])

        if client_type == "mobile":
            # BFF: মোবাইল ক্লায়েন্টের জন্য ছোট, ছাঁটাই করা রেসপন্স -- ব্যান্ডউইথ বাঁচাতে
            return {
                "name": user["name"],
                "order_count": len(orders),
                "latest_order_status": orders[0]["status"] if orders else None,
            }
        else:
            # BFF: ওয়েব ক্লায়েন্টের জন্য পূর্ণাঙ্গ, বিস্তারিত রেসপন্স
            return {
                "user": user,
                "orders": orders,
                "recommendations": recommendations,
            }

    elif path == "/orders":
        return {"orders": orders_service(params["user_id"])}

    else:
        return {"error": "404 Not Found"}


print("== একই /dashboard রিকোয়েস্ট, দুই ভিন্ন ক্লায়েন্ট টাইপে ==")

web_response = api_gateway("/dashboard", {"user_id": 7}, client_type="web")
print("ওয়েব ক্লায়েন্ট রেসপন্স:")
print(web_response)
print(f"ওয়েব রেসপন্সে মোট টপ-লেভেল কী: {len(web_response)}")

mobile_response = api_gateway("/dashboard", {"user_id": 7}, client_type="mobile")
print("\nমোবাইল ক্লায়েন্ট রেসপন্স:")
print(mobile_response)
print(f"মোবাইল রেসপন্সে মোট টপ-লেভেল কী: {len(mobile_response)}")

print("\n== একই গেটওয়ে দিয়ে ভিন্ন পাথ রাউট হচ্ছে ==")
print(api_gateway("/orders", {"user_id": 7}))
print(api_gateway("/unknown-path", {}))

    
লক্ষ্য করুন web_response-এ পুরো user ডিকশনারি (৫টি ফিল্ড), সব orders, ও recommendations — তিনটি সার্ভিসের সম্পূর্ণ ডেটাই থাকে। কিন্তু mobile_response-এ শুধু name, একটি গণনা করা order_count (len(orders) দিয়ে হিসাব করা), ও সর্বশেষ অর্ডারের স্ট্যাটাস — অনেক কম ডেটা, কিন্তু একই অন্তর্নিহিত তথ্যের উপর ভিত্তি করে। দুটোই একই তিনটি ব্যাকএন্ড সার্ভিস কল করে, শুধু গেটওয়ের ভেতরে client_type চেক করে ভিন্নভাবে রেসপন্স সাজানো হয় — ব্যাকএন্ড সার্ভিসগুলোর কোনো পরিবর্তন লাগে না। /unknown-path কোনো if/elif শাখায় না পড়ায় সরাসরি "404 Not Found" ফেরত দেয় — ঠিক L01-এর Router-এর মতোই।
মূল কথা · Key takeaway

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

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

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

প্র ০১ উপরের কোডে mobile_response-এর order_count মান 2 কেন, অথচ কোথাও সরাসরি 2 লেখা হয়নি?

কারণ order_count-এর মান len(orders) দিয়ে গণনা করা হয়েছে, যেখানে orders হলো orders_service(params["user_id"])-এর ফেরত দেওয়া লিস্ট, যাতে দুটো অর্ডার ডিকশনারি আছে (id 101 ও 102)। তাই len(orders) সরাসরি গণনা করে 2 ফেরত দেয় — এটি একটি হার্ডকোড করা মান নয়, বরং প্রকৃত ডেটা থেকে গণনা করা ফলাফল।

প্র ০২ যদি গেটওয়ে না থাকত এবং মোবাইল অ্যাপকে সরাসরি তিনটি ব্যাকএন্ড সার্ভিস আলাদাভাবে কল করতে হতো, তাহলে কী কী সমস্যা হতো?

মোবাইল অ্যাপকে তিনটি আলাদা নেটওয়ার্ক কল করতে হতো (প্রতিটির নিজস্ব লেটেন্সি ও ব্যর্থতার সম্ভাবনা সহ), প্রতিটি সার্ভিসের ঠিকানা ও অথেন্টিকেশন লজিক আলাদাভাবে জানতে হতো, এবং সম্পূর্ণ ডেটা (ওয়েবের জন্য ডিজাইন করা) ডাউনলোড করে তারপর ক্লায়েন্ট-সাইডে ছেঁটে ফেলতে হতো — ব্যান্ডউইথ অপচয় হতো। BFF এই সবকিছু একটি একক কলে সমাধান করে, রেসপন্সও ইতিমধ্যে সঠিক আকারে থাকে।

প্র ০৩ api_gateway("/orders", {"user_id": 7}) কল করার সময় কেন client_type প্যারামিটার দেওয়া হয়নি, এবং তাতে কোনো সমস্যা হয় কি?

client_type-এর ডিফল্ট মান "web" হিসেবে ফাংশন সিগনেচারে সংজ্ঞায়িত করা আছে (client_type="web"), তাই না দিলেও ফাংশন কাজ করে। তবে সমস্যা হয় না কারণ /orders শাখায় client_type আদৌ ব্যবহার করা হয়নি — শুধু /dashboard শাখায় BFF-স্টাইল রিশেপিং প্রযোজ্য, অন্য পাথগুলো সব ক্লায়েন্টের জন্য একই রেসপন্স দেয়।

অনুশীলন

  1. চিন্তা করুন: BFF প্যাটার্নে সাধারণত মোবাইলের জন্য একটি আলাদা BFF সার্ভিস ও ওয়েবের জন্য আরেকটি আলাদা BFF সার্ভিস রাখা হয় (একটি ফাংশনের ভেতরে if/else দিয়ে নয়, উপরের কোড সেলে যেমন সরলীকৃত আকারে দেখানো হয়েছে)। এটি আলাদা সার্ভিস হিসেবে রাখার সুবিধা কী হতে পারে?

    আলাদা সার্ভিস হিসেবে রাখলে মোবাইল টিম ও ওয়েব টিম স্বাধীনভাবে নিজেদের BFF ডেপ্লয় ও স্কেল করতে পারে, একে অপরের রিলিজ সাইকেলে বাধা না দিয়ে। এছাড়া মোবাইল BFF-এ যদি কোনো বাগ থাকে, তা ওয়েব BFF-কে প্রভাবিত করবে না, কারণ তারা সম্পূর্ণ আলাদা প্রসেস — উপরের একক-ফাংশন সংস্করণে একটি বাগ উভয় ক্লায়েন্ট টাইপকেই প্রভাবিত করতে পারত।

  2. পরীক্ষা করুন: উপরের কোড সেলে api_gateway-তে একটি তৃতীয় শাখা যোগ করুন — path == "/profile" হলে শুধু user_service কল করে সরাসরি সেই ডিকশনারি ফেরত দেবে, তারপর api_gateway("/profile", {"user_id": 7}) কল করে দেখুন সঠিক ইউজার ডেটা ফেরত আসে কি না।

    elif path == "/profile": return user_service(params["user_id"]) যোগ করার পর api_gateway("/profile", {"user_id": 7}) কল করলে {"id": 7, "name": "রিমা আক্তার", "email": "rima@example.com", "bio": ..., "joined": "2021-03-14"} ডিকশনারিটি সরাসরি ফেরত আসবে — এটি দেখায় গেটওয়েতে নতুন রাউট যোগ করা সহজ, ঠিক যেমন L01-এর Router-এ নতুন রুট যোগ করা হয়েছিল।

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

  • পরবর্তী পাঠ L09 কম্পোনেন্ট-ভিত্তিক UI — মূল ধারণা — M3 মডিউলের প্রথম পাঠ শুরু করুন।
  • আগের পাঠ L07 REST আর্কিটেকচারাল প্রিন্সিপল — এই পাঠের আগের ধাপ।
  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ M2 (ওয়েব আর্কিটেকচার প্যাটার্ন) সম্পূর্ণ হলো — বাকি মডিউলগুলো ফ্রন্ট-এন্ড, স্টেট ম্যানেজমেন্ট, ব্যাক-এন্ড, ORM, অথেন্টিকেশন, রেন্ডারিং ও ডিপ্লয়মেন্ট কভার করবে।
  • সব 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 — সব এক জায়গায়।
আগের পাঠ
REST আর্কিটেকচারাল প্রিন্সিপল