পাঠ ৫১ · ৫১-এর মধ্যে · মডিউল ১২
Home / Courses / System Design / চূড়ান্ত প্রকল্প

চূড়ান্ত প্রকল্প — সম্পূর্ণ সিস্টেম ডিজাইন ইন্টারভিউ সিমুলেশন

Capstone — a full mock system design interview
১৮ মিনিট পড়া উচ্চ · Advanced ক্যাপস্টোন প্রজেক্ট Python কোডসহ সম্পূর্ণ বাংলায়

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

  • একটি নতুন সিস্টেমকে কীভাবে প্রান্ত থেকে প্রান্ত ডিজাইন করতে হয়, কোর্সের বিভিন্ন মডিউলের কৌশল একত্র করে
  • কেন ক্যাশিং (M5, রিড-সাইড অপ্টিমাইজেশন) ও রেট লিমিটিং (M9, রাইট-সাইড সুরক্ষা) — দুটোই আলাদা প্রয়োজনে দরকার
  • আইডেম্পোটেন্সি (L38) ও রেট লিমিটিং (L35) একসাথে একটি ফাংশনে কীভাবে কম্পোজ করা যায়
  • একটি সম্পূর্ণ System Design ইন্টারভিউ কীভাবে সংগঠিত হয় — রিকোয়ারমেন্ট থেকে ট্রেড-অফ আলোচনা পর্যন্ত

১ · প্রকল্পের প্রেক্ষাপট

কল্পনা করুন একটি ইন্টারভিউয়ার আপনাকে বলছেন: "একটি নোটিফিকেশন সিস্টেম ডিজাইন করুন যা ইউজারদের লাইক, কমেন্ট ও নতুন ফলোয়ার সম্পর্কে প্রায়-রিয়েল-টাইমে জানায় — পুশ ও ইন-অ্যাপ দুই চ্যানেলেই।" ফাংশনাল রিকোয়ারমেন্ট সরল মনে হলেও, নন-ফাংশনাল দিকটা কঠিন — বিশাল স্কেল, কোনো নোটিফিকেশন হারানো যাবে না, এবং একজন ইউজারকে একই ইভেন্টের জন্য বারবার স্প্যাম করা যাবে না। নিচে আমরা এই সিস্টেমটি ধাপে ধাপে ডিজাইন করব, প্রতিটি সিদ্ধান্তে এই কোর্সের নির্দিষ্ট মডিউল ও লেসন উল্লেখ করে — ঠিক যেমন একটি বাস্তব ইন্টারভিউয়ে করা হয়।

ফাংশনাল
লাইক/কমেন্ট/ফলো ইভেন্টে ইউজারকে পুশ ও ইন-অ্যাপ নোটিফিকেশন পাঠানো।
নন-ফাংশনাল
বিশাল স্কেল, কোনো নোটিফিকেশন হারানো যাবে না (at-least-once delivery), ডুপ্লিকেট/স্প্যাম প্রতিরোধ।

২ · ধাপ ১ (M1, L01-02) — রিকোয়ারমেন্ট ও ক্যাপাসিটি অনুমান

L01-এর ফ্রেমওয়ার্ক অনুযায়ী ফাংশনাল ও নন-ফাংশনাল রিকোয়ারমেন্ট আলাদা করা হয়েছে উপরে। L02-এর পদ্ধতিতে ক্যাপাসিটি অনুমান করা যাক — ধরি প্রতিদিন ২০ কোটি (২০,০০,০০,০০০) ইভেন্ট (লাইক+কমেন্ট+ফলো একত্রে) তৈরি হয়, প্রতিটি ইভেন্ট গড়ে ১.৫ জন ইউজারকে নোটিফাই করে (যেমন একটি কমেন্টে একাধিক থ্রেড-পার্টিসিপেন্ট নোটিফাই হতে পারে)।

Python
events_per_day = 200_000_000
notifications_per_event = 1.5
seconds_per_day = 24 * 60 * 60

total_notifications_per_day = events_per_day * notifications_per_event
avg_notification_qps = total_notifications_per_day / seconds_per_day

peak_factor = 3   # L01-এর পিক-ফ্যাক্টর ধারণা
peak_notification_qps = avg_notification_qps * peak_factor

print(f"মোট নোটিফিকেশন/দিন: {total_notifications_per_day:,.0f}")
print(f"গড় নোটিফিকেশন QPS: {avg_notification_qps:,.1f}")
print(f"পিক নোটিফিকেশন QPS (~{peak_factor}x): {peak_notification_qps:,.1f}")

    
পিক QPS (~হাজার হাজার/সেকেন্ড) থেকেই স্পষ্ট — এটি একক সার্ভারের কাজ নয়। এই একটি সংখ্যাই বাকি সব ডিজাইন সিদ্ধান্ত (কতগুলো সার্ভার, শার্ডেড না মনোলিথিক ডেটাবেস) নিয়ন্ত্রণ করবে, ঠিক L01-এর মূল শিক্ষা অনুযায়ী।

৩ · ধাপ ২ (M2, L06/L08) — ক্লায়েন্ট API ও ডেলিভারি চ্যানেল

ইউজার তার নোটিফিকেশন লিস্ট আনতে একটি সাধারণ REST এন্ডপয়েন্ট (L06) ব্যবহার করে — GET /notifications। কিন্তু নতুন নোটিফিকেশন রিয়েল-টাইমে ইউজারকে জানাতে (যাতে বারবার পোল করতে না হয়) একটি WebSocketWebSocketক্লায়েন্ট-সার্ভারের মধ্যে একটি পার্সিস্টেন্ট, ফুল-ডুপ্লেক্স কানেকশন — সার্ভার থেকে ক্লায়েন্টে পুশ করার জন্য আদর্শ, L08 দেখুন। (L08) কানেকশন ব্যবহার করা হয় ইউজার অ্যাপ চালু থাকা অবস্থায় — অ্যাপ বন্ধ থাকলে অপারেটিং-সিস্টেম-লেভেল পুশ নোটিফিকেশন সার্ভিস (APNs/FCM) ব্যবহার হয়।

৪ · ধাপ ৩ (M3, L10-12) — লোড ব্যালেন্সিং ও স্টেটলেস ডেলিভারি সার্ভার

নোটিফিকেশন-ডেলিভারি সার্ভারগুলো সম্পূর্ণ স্টেটলেস (L12) রাখা হয় — কোন ইউজার কোন সার্ভারে কানেক্টেড তার তথ্য একটি শেয়ারড স্টোরে (Redis-জাতীয়) থাকে, সার্ভারে নয়। একটি লোড ব্যালেন্সার (L11) ইনকামিং রিকোয়েস্ট বিতরণ করে, এবং হরাইজন্টাল স্কেলিং (L10) দিয়ে ট্রাফিক বাড়লে নতুন সার্ভার যোগ করা যায় — কোনো সার্ভারের ক্যাপাসিটি সিলিং ছাড়াই।

৫ · ধাপ ৪ (M4, L14/L17) — স্টোরেজ ডিজাইন

নোটিফিকেশন রেকর্ড (append-heavy, "সাম্প্রতিক নোটিফিকেশন আনো" অ্যাক্সেস প্যাটার্ন) একটি NoSQL স্টোর-এ (L14) সংরক্ষিত হয়, user_id দিয়ে শার্ডেড (L17) — যাতে "এই ইউজারের নোটিফিকেশন" কোয়েরি সবসময় একটি একক শার্ডে সম্পন্ন হয়, ক্রস-শার্ড জয়েন ছাড়াই।

৬ · ধাপ ৫ (M5, L19) — আনরিড কাউন্ট ক্যাশিং

"আপনার কয়টি না-পড়া নোটিফিকেশন আছে" — এই প্রশ্নটি অ্যাপ খোলার সাথে সাথে প্রতিটি ইউজার বারবার জিজ্ঞেস করে, তাই এটি একটি ক্লাসিক উচ্চ-রিড, কম-রাইট প্যাটার্ন। আনরিড কাউন্ট প্রতিবার পুরো নোটিফিকেশন টেবিল স্ক্যান করে গোনার বদলে একটি ক্যাশে (L19-এর cache-aside প্যাটার্ন) সংরক্ষিত থাকে, প্রতিটি নতুন নোটিফিকেশনে ইনক্রিমেন্ট হয়।

৭ · ধাপ ৬ (M6, L22-23) — ইভেন্ট-ড্রিভেন জেনারেশন

যখন কেউ একটি পোস্ট লাইক করে, সেই সার্ভিসটি সরাসরি নোটিফিকেশন সার্ভিসকে কল করে না — বরং একটি "PostLiked" ইভেন্ট পাবলিশ করে একটি pub/sub সিস্টেমে (L22)। নোটিফিকেশন সার্ভিস সেই ইভেন্টের একজন সাবস্ক্রাইবার — এটি স্বাধীনভাবে ইভেন্ট শুনে নোটিফিকেশন তৈরি করে (L23-এর ইভেন্ট-ড্রিভেন আর্কিটেকচার)। এই ডিকাপলিং মানে লাইক-সার্ভিস নোটিফিকেশন-সার্ভিসের অস্তিত্ব সম্পর্কে কিছুই জানে না — ভবিষ্যতে নতুন সাবস্ক্রাইবার (যেমন অ্যানালিটিক্স) যোগ করা সহজ।

৮ · ধাপ ৭ (M7, L25-27) — আর্কিটেকচার প্যাটার্ন

নোটিফিকেশন একটি আলাদা, স্বাধীনভাবে-স্কেলযোগ্য মাইক্রোসার্ভিস (L25) — মূল অ্যাপ থেকে আলাদা ডিপ্লয় হয়, একটি API গেটওয়ের (L26) পেছনে বসে যা রাউটিং ও অথেন্টিকেশন হ্যান্ডল করে। এই সার্ভিস যখন এক্সটার্নাল পুশ-নোটিফিকেশন প্রোভাইডারকে (APNs/FCM) কল করে, একটি সার্কিট ব্রেকার (L27) সেই কলকে সুরক্ষা দেয় — প্রোভাইডার ধীর/ডাউন হলে ব্রেকার দ্রুত ফেইল করে, পুরো নোটিফিকেশন সার্ভিসকে আটকে না রেখে।

৯ · ধাপ ৮ (M9, L35/L38) — আইডেম্পোটেন্সি ও রেট লিমিটিং

দুটি নন-ফাংশনাল রিকোয়ারমেন্ট এখানে সরাসরি কোডে প্রয়োগ করা দরকার। প্রথমত — নেটওয়ার্ক রিট্রাই (L27) একই ইভেন্টের জন্য send_notification() দুইবার কল করতে পারে; একটি আইডেম্পোটেন্সি-কী (L38) এই ডুপ্লিকেট সেন্ড আটকায়। দ্বিতীয়ত — একটি অত্যন্ত সক্রিয় থ্রেড (যেমন একটি ভাইরাল পোস্টে শত শত কমেন্ট) একজন ইউজারকে স্প্যাম করতে পারে; একটি পার-ইউজার রেট লিমিট (L35) এটি আটকায়। নিচের কোড দুটোই একসাথে বাস্তবায়ন করে।

Python
RATE_LIMIT_PER_WINDOW = 3   # প্রতি সিমুলেটেড উইন্ডোতে ইউজারপ্রতি সর্বোচ্চ ৩টি নোটিফিকেশন

processed_keys = {}          # idempotency_key -> ফলাফল (L38)
user_send_count = {}         # user_id -> এই উইন্ডোতে পাঠানো নোটিফিকেশন সংখ্যা (L35)

def send_notification(user_id, event_id, idempotency_key):
    # ধাপ ১ — আইডেম্পোটেন্সি চেক (L38): আগে প্রসেস হয়ে থাকলে পুনরায় না পাঠিয়ে ক্যাশড ফলাফল ফেরত দাও
    if idempotency_key in processed_keys:
        return "deduped", processed_keys[idempotency_key]

    # ধাপ ২ — রেট লিমিট চেক (L35): এই ইউজার এই উইন্ডোতে সীমা ছাড়িয়ে গেলে ব্লক করো
    count_so_far = user_send_count.get(user_id, 0)
    if count_so_far >= RATE_LIMIT_PER_WINDOW:
        return "rate_limited", None

    # ধাপ ৩ — প্রকৃত সেন্ড (শুধু এখানেই, একবারই ঘটে)
    user_send_count[user_id] = count_so_far + 1
    result = f"notification[{event_id}] sent to {user_id}"
    processed_keys[idempotency_key] = result
    return "accepted", result

# একটি সিমুলেটেড কল-সিকোয়েন্স — একটি রিপিটেড আইডেম্পোটেন্সি-কী + একটি রেট-লিমিট-ছাড়ানো বার্স্ট
calls = [
    ("u1", "like_1",    "key1"),
    ("u1", "like_1",    "key1"),   # <- একই কী পুনরায় (যেমন একটি রিট্রাই)
    ("u1", "comment_1", "key2"),
    ("u1", "follow_1",  "key3"),
    ("u1", "like_2",    "key4"),   # <- u1-এর জন্য ৪র্থ প্রচেষ্টা, সীমা ৩ ছাড়িয়ে যায়
    ("u1", "like_3",    "key5"),   # <- এখনও রেট-লিমিটেড
    ("u1", "like_4",    "key6"),   # <- এখনও রেট-লিমিটেড
    ("u2", "like_5",    "key7"),   # <- ভিন্ন ইউজার, তার নিজস্ব তাজা কাউন্টার
]

report = {"accepted": 0, "deduped": 0, "rate_limited": 0}

for user_id, event_id, idem_key in calls:
    status, result = send_notification(user_id, event_id, idem_key)
    report[status] += 1
    print(f"send_notification({user_id}, {event_id}, {idem_key}) -> {status} | {result}")

print("\n=== চূড়ান্ত রিপোর্ট ===")
print(f"accepted (গৃহীত)         : {report['accepted']}")
print(f"deduped (ডিডুপ্লিকেটেড)  : {report['deduped']}")
print(f"rate_limited (সীমা ছাড়ানো): {report['rate_limited']}")
print(f"মোট কল                    : {sum(report.values())}")

    
কোডটি চালিয়ে দেখুন — key1-এর দ্বিতীয় কলটি "deduped" হয় (আসল সেন্ড কাউন্টার শুধু একবারই বাড়ে), আর u1-এর জন্য ৩টি সফল সেন্ডের (key1, key2, key3) পর key4/key5/key6 সবগুলো "rate_limited" হয় — অথচ u2-এর key7 স্বাভাবিকভাবে "accepted" হয়, কারণ প্রতিটি ইউজারের কাউন্টার স্বাধীন। চূড়ান্ত রিপোর্টে accepted=৪, deduped=১, rate_limited=৩, মোট=৮ — ইনপুট সিকোয়েন্সের সাথে হুবহু মেলে।

১০ · ধাপ ৯ (M10, L39/L41-42) — সিকিউরিটি ও অবজারভেবিলিটি

প্রতিটি নোটিফিকেশন-রিকোয়েস্টে ইউজারের আইডেন্টিটি টোকেন যাচাই করা হয় (L39-এর অথেন্টিকেশন), যাতে কেউ অন্য ইউজারের নোটিফিকেশন লিস্ট বা আনরিড-কাউন্ট দেখতে না পারে। ইভেন্ট থেকে ডেলিভারি পর্যন্ত পুরো পথ একটি একক ট্রেস-আইডি দিয়ে ট্র্যাক করা হয় (L41-এর ডিস্ট্রিবিউটেড ট্রেসিং) — "একটি নোটিফিকেশন পৌঁছাতে কেন দেরি হলো" ডিবাগ করতে অপরিহার্য। দলটি একটি SLO (L42) সেট করে — যেমন "৯৯.৯% নোটিফিকেশন ৫ সেকেন্ডের মধ্যে ডেলিভার হবে" — এবং এই SLO-ই মনিটরিং ড্যাশবোর্ড ও অ্যালার্টের ভিত্তি।

M8 (ডিস্ট্রিবিউটেড সিস্টেমস থিওরি) কেন এখানে সরাসরি ব্যবহৃত হয়নি

লক্ষ্য করুন — ভেক্টর ক্লক, কোরাম রিড/রাইট বা CRDT (M8) এই ডিজাইনে সরাসরি প্রয়োজন হয়নি, কারণ এই সিস্টেমে একাধিক নোড একই ডেটার উপর কনফ্লিক্টিং রাইট করছে না — প্রতিটি নোটিফিকেশন একবারই তৈরি হয় (আইডেম্পোটেন্সি দিয়ে সুরক্ষিত) এবং শার্ডিং সরল (user_id-ভিত্তিক, কোনো মাল্টি-মাস্টার কনফ্লিক্ট নেই)। এটি একটি গুরুত্বপূর্ণ ইন্টারভিউ দক্ষতা — কোন মডিউলের কৌশল প্রাসঙ্গিক তা বাছাই করা, প্রতিটি কৌশল গায়ের জোরে ব্যবহার করা নয়।

লাইক/কমেন্ট/ফলো Source Service Pub/Sub (M6, L22) "PostLiked" event নোটিফিকেশন মাইক্রোসার্ভিস M7, L25 — idempotent (M9) API গেটওয়ে (M7) Gateway (L26) শার্ডেড NoSQL + ক্যাশ Storage (M4) + Cache (M5) সার্কিট ব্রেকার (M7) Circuit Breaker (L27) পুশ প্রোভাইডার (APNs/FCM)
ইভেন্ট থেকে ডেলিভারি পর্যন্ত সম্পূর্ণ পথ — প্রতিটি বক্স কোর্সের একটি নির্দিষ্ট মডিউল।

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

প্রতিটি প্রশ্ন একাধিক মডিউল জুড়ে ভাবতে হবে — নিজে কিছুক্ষণ ভাবুন, তারপর "→ উত্তর" চাপুন।

প্র ০১ এই সিস্টেমে ক্যাশিং (M5, আনরিড কাউন্ট) এবং রেট লিমিটিং (M9, স্প্যাম প্রতিরোধ) — দুটোই তো "অনেক বেশি রিকোয়েস্ট" সামলানোর কথা বলে। তাহলে দুটো আলাদা মেকানিজম কেন দরকার?

এই দুটো সম্পূর্ণ ভিন্ন অক্ষে কাজ করে। ক্যাশিং (M5) রিড দিকের সমস্যা সমাধান করে — একই ডেটা (আনরিড কাউন্ট) বারবার গণনা/ফেচ করার খরচ কমায়, ডেটা নিজে বদলায় না বরং তার হিসাবের প্রক্রিয়া দ্রুত করে। রেট লিমিটিং (M9) রাইট/ট্রিগার দিকের সমস্যা সমাধান করে — একজন ক্লায়েন্ট বা ইভেন্ট-সোর্স কতগুলো নতুন কাজ (নোটিফিকেশন তৈরি) ট্রিগার করতে পারে তার একটি সীমা বেঁধে দেয়, যাতে একটি সোর্স পুরো সিস্টেম বা একজন ইউজারকে অভিভূত করতে না পারে। একটি ডেটা পুনরায়-পড়ার খরচ কমায়, অন্যটি নতুন কাজ তৈরির হার সীমিত করে — উভয়ই দরকার কারণ তারা ভিন্ন সমস্যার সমাধান করে।

প্র ০২ যদি আইডেম্পোটেন্সি-কী চেক (L38) সরিয়ে ফেলা হয়, শুধু রেট লিমিট (L35) রেখে দেওয়া হয় — এই সিস্টেমে ঠিক কী ধরনের বাগ দেখা দিতে পারে?

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

প্র ০৩ এই কোর্সের ৫১টি পাঠের মধ্যে, এই ক্যাপস্টোনে যে ৯টি মডিউল উল্লেখ করা হলো, তার বাইরে M8 (ডিস্ট্রিবিউটেড সিস্টেমস থিওরি) ব্যবহার হয়নি। এটি কি এই ডিজাইনের একটি দুর্বলতা?

না — এটি একটি ভালো ডিজাইন সিদ্ধান্তের ফল, দুর্বলতা নয়। M8-এর কৌশল (ভেক্টর ক্লক, কোরাম, CRDT) তখনই দরকার হয় যখন একাধিক নোড একই ডেটার উপর কনকারেন্ট, কনফ্লিক্টিং রাইট করে এবং কোনো কেন্দ্রীয় সমন্বয়কারী নেই। এই নোটিফিকেশন সিস্টেমে প্রতিটি নোটিফিকেশন একবারই তৈরি হয় (আইডেম্পোটেন্সি দিয়ে সুরক্ষিত) এবং শার্ডিং key-ভিত্তিক সরল — তাই কোনো মাল্টি-মাস্টার কনফ্লিক্ট নেই। একজন ভালো ইঞ্জিনিয়ার প্রতিটি টুল ব্যবহার করেন না শুধু "কোর্সে শিখেছি" বলে — বরং সমস্যার প্রকৃতি অনুযায়ী সঠিক টুল বেছে নেন। এটাই System Design-এর মূল দক্ষতা।

অনুশীলন

  1. পরিবর্তন করুন: কোড সেলে RATE_LIMIT_PER_WINDOW-কে ৩ থেকে ৫-এ বদলান এবং আবার চালান। চূড়ান্ত রিপোর্টে accepted/rate_limited সংখ্যা কীভাবে বদলাবে?

    সীমা ৫ হলে u1-এর জন্য key1, key2, key3, key4, key5 — সবগুলোই "accepted" হবে (key1-এর দ্বিতীয় কল এখনও "deduped" থাকবে, কারণ সেটা আইডেম্পোটেন্সি চেকের ফল, রেট লিমিটের নয়), এবং শুধু key6 "rate_limited" হবে (u1-এর ৬ষ্ঠ নতুন-কী প্রচেষ্টা)। চূড়ান্ত রিপোর্ট হবে accepted=৫, deduped=১, rate_limited=১ — মোট এখনও ৮, শুধু বণ্টন বদলেছে।

  2. ব্যাখ্যা করুন (কোড ছাড়া): এই কোর্সের শুরুতে (L01) বলা হয়েছিল System Design মানে ট্রেড-অফ বোঝা, কোনো একক "সঠিক উত্তর" খোঁজা নয়। এই ক্যাপস্টোনে ঠিক কোন কোন সিদ্ধান্তে ট্রেড-অফ ছিল?

    বেশ কয়েকটি — (১) WebSocket বনাম পোলিং (L08): রিয়েল-টাইম বনাম সরলতা। (২) NoSQL বনাম SQL স্টোরেজ (L14): স্কেল-বান্ধব স্কিমা নমনীয়তা বনাম স্ট্রং কনসিস্টেন্সি/জয়েন সুবিধা। (৩) ক্যাশড আনরিড কাউন্ট (L19): দ্রুত রিড বনাম সাময়িক স্টেলনেস ঝুঁকি। (৪) সার্কিট ব্রেকার থ্রেশহোল্ড (L27): দ্রুত-ফেইল বনাম সাময়িক সমস্যায় অতি-দ্রুত সার্ভিস বন্ধ করে দেওয়া। প্রতিটি সিদ্ধান্তই নির্দিষ্ট নন-ফাংশনাল রিকোয়ারমেন্টের ভিত্তিতে নেওয়া হয়েছে — এটাই System Design-এর মূল অনুশীলন, L01-এ যা বলা হয়েছিল ঠিক তাই।

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

অভিনন্দন — আপনি কোর্সটি সম্পূর্ণ করেছেন

L01-এ আমরা বলেছিলাম System Design মানে DSA ও DBMS-এর উপর তৈরি একটি নতুন স্তর — একটি ফাংশনের গতি নয়, একটি একক ডেটাবেসের অভ্যন্তরীণ কাজ নয়, বরং অনেকগুলো সার্ভার, ডেটাবেস, ক্যাশ ও সার্ভিস একত্র করে এমন একটি সিস্টেম বানানো যা লক্ষ লক্ষ ইউজারকে নির্ভরযোগ্যভাবে সার্ভ করতে পারে। এই ৫১টি পাঠে আপনি নেটওয়ার্কিং ফান্ডামেন্টালস (M2) থেকে শুরু করে স্কেলেবিলিটি (M3), ডেটাবেস অ্যাট স্কেল (M4), ক্যাশিং (M5), অ্যাসিনক্রোনাস সিস্টেম (M6), আর্কিটেকচার প্যাটার্ন (M7), ডিস্ট্রিবিউটেড সিস্টেমস থিওরি (M8), রিলায়েবিলিটি প্যাটার্ন (M9), এবং সিকিউরিটি ও অবজারভেবিলিটি (M10) পেরিয়ে — আটটি সম্পূর্ণ কেস স্টাডি (M11) এবং এই ক্যাপস্টোনে (M12) সবকিছু একত্রিত করে একটি বাস্তব সিস্টেম ডিজাইন করেছেন। এখন থেকে যখন আপনি কোনো অ্যাপ ব্যবহার করবেন — একটি মেসেজ পাঠাবেন, একটি ফিড স্ক্রল করবেন, একটি পেমেন্ট করবেন — তার পেছনের আর্কিটেকচারাল সিদ্ধান্তগুলো আর অচেনা লাগবে না। এটি একটি সমাপ্তি নয়, একটি ভিত্তি — DSA (অ্যালগরিদম), DBMS (একক ডেটাবেস) ও System Design (পুরো সিস্টেম) — এই তিনটি কোর্স একসাথে আপনাকে "কোড লেখা" থেকে "প্রোডাকশন-গ্রেড সিস্টেম ডিজাইন করা" পর্যন্ত পূর্ণ পথ দেখিয়েছে। অভিনন্দন, এবং শুভকামনা আপনার পরবর্তী ধাপের জন্য।

পূর্ববর্তী পাঠ
কেস স্টাডি: ডিস্ট্রিবিউটেড ফাইল স্টোরেজ ডিজাইন করা