Sitemap
Yeasaleh

Yeasaleh

System Design and DevOps

28 min readSep 28, 2026

--

Phase 1: System Design, Architecture Patterns & Developer-Focused DevOps

Question 1: Monolithic vs. Microservices Architecture: At what point do you decide to break a Monolith into Microservices?

Short Answer:

You do not start with Microservices; you start with a Modular Monolith. I decide to split into microservices only when the monolith faces strict organizational bottlenecks (teams stepping on each other’s code), independent scaling requirements (e.g., the PDF generator consumes 90% CPU while the core API idles), or disparate technology requirements (e.g., using Python for AI while the main app is Node.js).

Detailed Answer:

Microservices introduce immense operational complexity: distributed tracing, network latency, data consistency (CAP theorem), and complex CI/CD.

  1. The Monolith First Strategy: A well-structured monolith with distinct modules (Controller-Service-Repository) is highly scalable and easier to deploy.
  2. The Breaking Point: If your E-commerce app has a massive Black Friday sale, the Payment and Inventory services need horizontal scaling, but the User Profile service does not. In a monolith, you must scale the entire heavy codebase. By breaking it into microservices, you can deploy 50 instances of the Payment service and only 2 of the Profile service, optimizing cloud costs and resource allocation.
  3. Database per Service: The golden rule of microservices is that they must not share a database. If Service A needs Service B’s data, it must ask for it via an API or an Event Queue, never by querying Service B’s tables directly.

প্রশ্ন ১: Monolithic বনাম Microservices আর্কিটেকচার: ঠিক কোন পর্যায়ে আপনি একটি মনোলিথকে ভেঙে মাইক্রোসার্ভিস করার সিদ্ধান্ত নেন?

সংক্ষিপ্ত উত্তর:

আমরা কখনোই মাইক্রোসার্ভিস দিয়ে প্রজেক্ট শুরু করি না; আমরা মডুলার মনোলিথ দিয়ে শুরু করি। আমি মনোলিথ ভাঙার সিদ্ধান্ত নিই যখন বড় টিমের কোড মার্জ করতে সমস্যা হয়, স্বাধীন স্কেলিংয়ের প্রয়োজন পড়ে (যেমন- PDF জেনারেটর একাই ৯০% CPU খাচ্ছে, কিন্তু মূল API বসে আছে), অথবা ভিন্ন প্রযুক্তির প্রয়োজন হয় (যেমন- মূল অ্যাপ Node.js এ, কিন্তু AI এর কাজের জন্য Python লাগবে)।

বিস্তারিত উত্তর:

মাইক্রোসার্ভিস সিস্টেমে প্রচুর অপারেশনাল জটিলতা নিয়ে আসে: ডিস্ট্রিবিউটেড ট্রেসিং, নেটওয়ার্ক ল্যাটেন্সি, ডেটা কনসিস্টেন্সি (CAP Theorem) এবং জটিল CI/CD।

১. Monolith First Strategy: মডুলার আর্কিটেকচার (Controller-Service-Repository) মেনে তৈরি করা একটি মনোলিথ অনেক বেশি স্কেলেবল এবং ডিপ্লয় করা সহজ।

২. ভাঙার সময় (The Breaking Point): ই-কমার্সে ব্ল্যাক ফ্রাইডে সেলের সময় Payment এবং Inventory সার্ভিসের হরাইজন্টাল স্কেলিং প্রয়োজন হয়, কিন্তু User Profile সার্ভিসের হয় না। মনোলিথে পুরো ভারী কোডবেসটি স্কেল করতে হয়। কিন্তু মাইক্রোসার্ভিসে আপনি Payment সার্ভিসের ৫০টি কপি এবং Profile এর মাত্র ২টি কপি চালু করে ক্লাউডের খরচ বাঁচাতে পারেন।

৩. Database per Service: মাইক্রোসার্ভিসের গোল্ডেন রুল হলো, দুটি সার্ভিস কখনো একই ডেটাবেস শেয়ার করবে না। Service A-এর যদি Service B-এর ডেটা লাগে, তবে তাকে API বা Event Queue-এর মাধ্যমে ডেটা চাইতে হবে, সরাসরি ডেটাবেস থেকে নয়।

Question 2: What is the API Gateway Pattern, and why is it essential in a distributed system?

Short Answer:

An API Gateway is an architectural pattern that acts as the single entry point for all client requests into a microservices ecosystem. Instead of a mobile app or web client securely authenticating, routing, and aggregating data by talking directly to 15 different microservices, the client makes one call to the API Gateway. The Gateway handles the routing, rate limiting, SSL termination, and authentication globally.

Detailed Answer:

Architecturally, exposing internal microservices directly to the public internet is a massive security and maintenance risk.

  • Request Aggregation (BFF — Backend for Frontend): To render a dashboard, a client might need user data (Service A), order history (Service B), and shipping status (Service C). Instead of the client making 3 network round-trips over slow 4G networks, the client makes 1 call to the API Gateway. The Gateway concurrently fetches data from A, B, and C over the ultra-fast internal server network, combines it, and returns a single JSON payload.
  • Cross-Cutting Concerns: Security protocols like JWT verification, CORS headers, and IP Rate Limiting are implemented once at the Gateway layer (using tools like NGINX, Kong, or AWS API Gateway), rather than being duplicated inside every single microservice’s codebase.

প্রশ্ন ২: API Gateway Pattern কী, এবং ডিস্ট্রিবিউটেড সিস্টেমে এটি কেন অপরিহার্য?

সংক্ষিপ্ত উত্তর:

API Gateway হলো এমন একটি আর্কিটেকচারাল প্যাটার্ন যা মাইক্রোসার্ভিস ইকোসিস্টেমে ক্লায়েন্টের সমস্ত রিকোয়েস্টের জন্য একটিমাত্র প্রবেশদ্বার (single entry point) হিসেবে কাজ করে। ক্লায়েন্ট (মোবাইল বা ওয়েব অ্যাপ) নিজে থেকে ১৫টি আলাদা মাইক্রোসার্ভিসের সাথে কথা বলে ডেটা যোগাড় বা অথেনটিকেট করার বদলে, শুধু API Gateway-কে কল করে। Gateway নিজে থেকেই রাউটিং, রেট লিমিটিং, SSL টার্মিনেশন এবং গ্লোবাল অথেনটিকেশনের দায়িত্ব পালন করে।

বিস্তারিত উত্তর:

আর্কিটেকচারালি, ইন্টারনাল মাইক্রোসার্ভিসগুলোকে সরাসরি পাবলিক ইন্টারনেটে উন্মুক্ত করা সিকিউরিটি এবং মেইনটেন্যান্সের জন্য বিশাল ঝুঁকিপূর্ণ।

  • Request Aggregation (BFF — Backend for Frontend): একটি ড্যাশবোর্ড রেন্ডার করতে ক্লায়েন্টের হয়তো ইউজার ডেটা (Service A), অর্ডারের হিস্ট্রি (Service B) এবং শিপিং স্ট্যাটাস (Service C) দরকার। ধীরগতির থ্রি-জি/ফোর-জি নেটওয়ার্কে ক্লায়েন্ট ৩ বার রিকোয়েস্ট না পাঠিয়ে, API Gateway-কে ১ বার রিকোয়েস্ট পাঠায়। Gateway সার্ভারের ভেতরের অতি-দ্রুতগতির নেটওয়ার্ক ব্যবহার করে A, B, ও C থেকে ডেটা এনে একসাথে একটি JSON বানিয়ে ক্লায়েন্টকে ফেরত দেয়।
  • Cross-Cutting Concerns: JWT ভেরিফিকেশন, CORS হেডার বা IP Rate Limiting-এর মতো সিকিউরিটি রুলস প্রতিটি মাইক্রোসার্ভিসের কোডে বারবার না লিখে, সরাসরি Gateway লেভেলে (NGINX বা AWS API Gateway ব্যবহার করে) একবার ইমপ্লিমেন্ট করা হয়।

Question 3: How does the Circuit Breaker Pattern prevent cascading failures in Microservices?

Short Answer:

The Circuit Breaker pattern is a fault-tolerance mechanism that prevents an application from repeatedly trying to execute an operation that is highly likely to fail. If Service A calls Service B, and Service B is down, the circuit “trips” (opens) after a defined number of consecutive failures. Subsequent calls from Service A will instantly fail-fast (returning a fallback response) without waiting for a network timeout, preventing Service A’s threads and memory from being exhausted.

Detailed Answer:

In a distributed system, a slow service is often more dangerous than a dead service.

  • The Cascading Failure: If the Inventory service becomes unresponsive (e.g., taking 30 seconds to timeout), the Order service waiting for it will also block its own threads for 30 seconds. Quickly, the Order service runs out of memory and crashes. Then the Payment service crashes waiting for Order. The entire platform melts down.
  • The States:
  1. Closed (Normal): Requests flow freely.
  2. Open (Tripped): After X failures, the circuit opens. Requests instantly fail or return cached data.
  3. Half-Open: After a timeout period, the breaker allows a limited number of test requests through. If they succeed, it closes the circuit. If they fail, it opens again.

JavaScript

// English Code Example: Conceptual Circuit Breaker (Using 'opossum' library conceptually)
const CircuitBreaker = require('opossum');
const fetchInventory = async (productId) => {
return await axios.get(`http://inventory-service/api/${productId}`);
};
const breaker = new CircuitBreaker(fetchInventory, {
timeout: 3000, // If function takes longer than 3 seconds, trigger a failure
errorThresholdPercentage: 50, // When 50% of requests fail, open the circuit
resetTimeout: 10000 // After 10 seconds, try a half-open request
});
// Fallback gracefully instead of crashing
breaker.fallback(() => ({ status: 'delayed', message: 'Inventory currently unavailable' }));
breaker.fire('prod_123').then(console.log).catch(console.error);

প্রশ্ন ৩: মাইক্রোসার্ভিসে ক্যাসকেডিং ফেইলিউর (Cascading Failures) ঠেকাতে Circuit Breaker Pattern কীভাবে কাজ করে?

সংক্ষিপ্ত উত্তর:

Circuit Breaker প্যাটার্ন হলো একটি ফল্ট-টলারেন্স (fault-tolerance) মেকানিজম, যা কোনো সার্ভিস ডাউন থাকলে তাকে বারবার রিকোয়েস্ট পাঠিয়ে সিস্টেম ব্লক হওয়া থেকে আটকায়। Service A যদি Service B-কে কল করে এবং B ডাউন থাকে, তবে নির্দিষ্ট কয়েকবার ফেইল করার পর সার্কিটটি “ট্রিপ” (Open) করে যায়। এরপর Service A থেকে আসা রিকোয়েস্টগুলো নেটওয়ার্ক টাইমআউটের জন্য অপেক্ষা না করে সাথে সাথেই ফেইল-ফাস্ট (বা ফলব্যাক রেসপন্স) রিটার্ন করে। এতে Service A-এর মেমরি এবং থ্রেড বেঁচে যায়।

বিস্তারিত উত্তর:

ডিস্ট্রিবিউটেড সিস্টেমে একটি ডেড সার্ভিসের চেয়ে একটি স্লো সার্ভিস বেশি ভয়ংকর।

  • ক্যাসকেডিং ফেইলিউর: যদি Inventory সার্ভিস স্লো হয়ে যায় (টাইমআউট হতে ৩০ সেকেন্ড লাগে), তবে তার জন্য অপেক্ষায় থাকা Order সার্ভিসও তার থ্রেড ৩০ সেকেন্ড আটকে রাখবে। দ্রুতই Order সার্ভিসের মেমরি শেষ হয়ে সেটি ক্র্যাশ করবে। এরপর Payment সার্ভিস ক্র্যাশ করবে। পুরো প্ল্যাটফর্ম ধসে পড়বে।
  • সার্কিটের স্টেটগুলো:
  • ১. Closed (স্বাভাবিক): রিকোয়েস্ট নির্বিঘ্নে পাস হয়।
  • ২. Open (ট্রিপড): নির্দিষ্ট সংখ্যক ফেইল হলে সার্কিট ওপেন হয়। রিকোয়েস্ট সাথে সাথে ফেইল করে বা ক্যাশ ডেটা রিটার্ন করে।
  • ৩. Half-Open: কিছু সময় পর সার্কিট পরীক্ষামূলকভাবে কয়েকটি রিকোয়েস্ট পাস হতে দেয়। সফল হলে সার্কিট ক্লোজ করে দেয়, আর ফেইল করলে আবার ওপেন করে।

JavaScript

// বাংলা কোড এক্সাম্পল: Circuit Breaker লজিক
const CircuitBreaker = require('opossum');
const fetchInventory = async (productId) => {
return await axios.get(`http://inventory-service/api/${productId}`);
};
const breaker = new CircuitBreaker(fetchInventory, {
timeout: 3000, // ৩ সেকেন্ডের বেশি সময় লাগলে ফেইলিউর ধরা হবে
errorThresholdPercentage: 50, // ৫০% রিকোয়েস্ট ফেইল করলে সার্কিট ওপেন হবে
resetTimeout: 10000 // ১০ সেকেন্ড পর Half-Open চেক করবে
});
// ক্র্যাশ করার বদলে সুন্দরভাবে ফলব্যাক রেসপন্স দেওয়া
breaker.fallback(() => ({ status: 'delayed', message: 'ইনভেন্টরি সাময়িকভাবে বন্ধ আছে' }));
breaker.fire('prod_123').then(console.log).catch(console.error);

Question 4: What is Event-Driven Architecture (Pub/Sub Pattern), and why do we use Kafka or RabbitMQ instead of direct HTTP calls?

Short Answer:

Event-Driven Architecture decouples microservices by relying on asynchronous events rather than synchronous HTTP requests. A service (Publisher) emits an event to a Message Broker (like RabbitMQ or Kafka) without knowing who will consume it. Other services (Subscribers) listen for that event and react. This ensures that if a consuming service is temporarily down, the event is safely stored in the queue and processed when the service recovers, guaranteeing zero data loss.

Detailed Answer:

Synchronous HTTP calls create tight coupling and temporal dependency (both services must be alive at the exact same time).

  • The HTTP Problem: If the User service makes an HTTP POST to the Email service to send a welcome email, and the Email service is restarting, the HTTP call drops. The welcome email is lost forever unless you build complex retry logic.
  • The Message Broker Solution: Using RabbitMQ or Kafka, the User service simply publishes { event: "USER_REGISTERED", email: "x@x.com" } to an Exchange/Topic and immediately returns 200 OK to the frontend. The Email service pulls from that queue at its own pace. If the Email service crashes for an hour, the messages safely pile up in the queue. Upon reboot, it processes the backlog sequentially.

প্রশ্ন ৪: Event-Driven Architecture (Pub/Sub Pattern) কী, এবং সরাসরি HTTP কলের বদলে আমরা কেন Kafka বা RabbitMQ ব্যবহার করি?

সংক্ষিপ্ত উত্তর:

ইভেন্ট-ড্রিভেন আর্কিটেকচার সিনক্রোনাস HTTP রিকোয়েস্টের বদলে অ্যাসিনক্রোনাস ইভেন্টের ওপর নির্ভর করে মাইক্রোসার্ভিসগুলোকে ডিকাপল (আলাদা) করে। একটি সার্ভিস (Publisher) একটি মেসেজ ব্রোকারে (যেমন Kafka বা RabbitMQ) ইভেন্ট পাঠিয়ে দেয়, কে সেই মেসেজ রিসিভ করবে তা তার জানার প্রয়োজন নেই। অন্য সার্ভিসগুলো (Subscribers) সেই ইভেন্ট শুনে কাজ করে। কোনো রিসিভার সার্ভিস যদি সাময়িকভাবে ডাউনও থাকে, মেসেজটি নিরাপদে কিউ (queue)-তে জমা থাকে এবং সার্ভিস আপ হলে তা প্রসেস হয়, ফলে কোনো ডেটা হারায় না।

বিস্তারিত উত্তর:

সিনক্রোনাস HTTP কল টাইট কাপলিং (tight coupling) এবং টেম্পোরাল ডিপেন্ডেন্সি (দুটি সার্ভিসকেই একই সময়ে লাইভ থাকতে হবে) তৈরি করে।

  • HTTP এর সমস্যা: User সার্ভিস যদি ওয়েলকাম ইমেইল পাঠানোর জন্য Email সার্ভিসকে HTTP POST কল করে, এবং ইমেইল সার্ভিস ওই সময় রিস্টার্ট নিতে থাকে, তবে রিকোয়েস্টটি ড্রপ করবে। জটিল রিট্রাই (retry) লজিক না থাকলে ওই ওয়েলকাম ইমেইলটি চিরতরে হারিয়ে যাবে।
  • Message Broker এর সমাধান: RabbitMQ বা Kafka ব্যবহার করলে, User সার্ভিস শুধু { event: "USER_REGISTERED" } মেসেজটি টপিকে পাবলিশ করে ক্লায়েন্টকে 200 OK রেসপন্স দিয়ে দেয়। Email সার্ভিস তার নিজের গতিতে কিউ থেকে মেসেজ টানে। ইমেইল সার্ভিস এক ঘণ্টার জন্য ক্র্যাশ করলেও মেসেজগুলো কিউ-তে জমা হতে থাকে এবং রিবুট হওয়ার পর সে সিরিয়াল অনুযায়ী বাকি কাজ শেষ করে।

Question 5: As a developer, why should you care about Docker? Explain the fundamental architectural difference between Docker Containers and Virtual Machines (VMs).

Short Answer:

Developers care about Docker because it solves the “It works on my machine” problem by packaging the application code, Node.js runtime, OS dependencies, and environment variables into a single, immutable, isolated artifact (an Image). Architecturally, Virtual Machines package a full, heavy, independent Guest Operating System for every application, whereas Docker Containers share the Host OS kernel, making containers start in milliseconds and consume drastically less CPU and RAM.

Detailed Answer:

Understanding Docker is no longer just for DevOps engineers; it is mandatory for full-stack architects.

  • The VM Bottleneck: To run a Node app and a Redis cache securely on a server using VMs, you have to boot two separate Ubuntu Guest OS instances. Each Guest OS consumes ~500MB of RAM just to idle, wasting gigabytes of server memory.
  • The Container Advantage: Docker Engine acts as an isolation layer on top of a single Host OS (like Linux). The Node container and Redis container are just isolated processes running on the same kernel. They use almost zero overhead.
  • The Developer DX: You write a Dockerfile. The CI/CD pipeline builds this image. That exact identical image is run in Staging, QA, and Production. There are no missing global NPM packages, no wrong Node versions, and no OS-level mismatches.

Dockerfile

# English Code Example: A Developer's optimized Dockerfile for Node.js
# 1. Use a lightweight Alpine Linux base image
FROM node:20-alpine AS builder
WORKDIR /app
# 2. Leverage Docker cache by installing dependencies first
COPY package*.json ./
RUN npm ci
# 3. Copy application code
COPY . .
# 4. Expose the port and define the immutable start command
EXPOSE 3000
CMD ["npm", "start"]

প্রশ্ন ৫: একজন ডেভেলপার হিসেবে আপনার কেন Docker নিয়ে কাজ করা উচিত? Docker Containers এবং Virtual Machines (VMs) এর মধ্যে মূল আর্কিটেকচারাল পার্থক্য কী?

সংক্ষিপ্ত উত্তর:

ডেভেলপাররা Docker ব্যবহার করে কারণ এটি “আমার পিসিতে কাজ করছে, কিন্তু সার্ভারে করছে না” সমস্যার সমাধান করে। এটি অ্যাপ্লিকেশনের কোড, Node.js রানটাইম, OS লাইব্রেরি এবং এনভায়রনমেন্ট ভেরিয়েবলগুলোকে একটি একক এবং অপরিবর্তনীয় প্যাকেজে (Image) আবদ্ধ করে। আর্কিটেকচারালি, Virtual Machine প্রতিটি অ্যাপের জন্য সম্পূর্ণ স্বাধীন ও ভারী একটি গেস্ট অপারেটিং সিস্টেম (Guest OS) রান করে, যেখানে Docker কনটেইনারগুলো হোস্ট OS-এর কার্নেল শেয়ার করে। ফলে কনটেইনার মিলি-সেকেন্ডে চালু হয় এবং খুবই কম CPU ও RAM খরচ করে।

বিস্তারিত উত্তর:

Docker বোঝা এখন শুধু DevOps ইঞ্জিনিয়ারদের কাজ নয়, ফুল-স্ট্যাক আর্কিটেক্টদের জন্যও এটি বাধ্যতামূলক।

  • VM এর সীমাবদ্ধতা: সার্ভারে VM দিয়ে একটি Node অ্যাপ এবং একটি Redis ক্যাশ চালাতে হলে আপনাকে দুটি আলাদা Ubuntu গেস্ট OS বুট করতে হবে। প্রতিটি OS আইডল অবস্থাতেই ৫০০ মেগাবাইট RAM খেয়ে বসে থাকবে, যা সার্ভারের প্রচুর মেমরি নষ্ট করে।
  • কনটেইনারের সুবিধা: Docker ইঞ্জিন একটিমাত্র হোস্ট OS (যেমন Linux) এর ওপর আইসোলেশন লেয়ার হিসেবে কাজ করে। Node এবং Redis কনটেইনার মূলত একই কার্নেলে চলা দুটি আইসোলেটেড প্রসেস মাত্র। এগুলোর কোনো ওভারহেড নেই।
  • ডেভেলপার এক্সপেরিয়েন্স: আপনি একটি Dockerfile লেখেন। CI/CD পাইপলাইন সেই ইমেজটি বিল্ড করে। হুবহু একই ইমেজ Staging, QA এবং Production-এ রান হয়। ফলে সার্ভারে কোনো গ্লোবাল NPM প্যাকেজ মিসিং হওয়া বা Node ভার্সন ভুল হওয়ার কোনো সুযোগ থাকে না।

Dockerfile

# বাংলা কোড এক্সাম্পল: Node.js এর জন্য অপ্টিমাইজড Dockerfile
# ১. হালকা Alpine Linux বেস ইমেজ ব্যবহার করা
FROM node:20-alpine AS builder
WORKDIR /app
# ২. বারবার প্যাকেজ ডাউনলোড এড়াতে Docker ক্যাশ ব্যবহার করা
COPY package*.json ./
RUN npm ci
# ৩. অ্যাপ্লিকেশনের কোড কপি করা
COPY . .
# ৪. পোর্ট এক্সপোজ করা এবং স্টার্ট কমান্ড ডিফাইন করা
EXPOSE 3000
CMD ["npm", "start"]

Question 6: How do you architect a CI/CD pipeline for a Node.js API using GitHub Actions, and what is the difference between Continuous Integration and Continuous Deployment?

Short Answer:

A robust CI/CD pipeline automates code validation and delivery. Continuous Integration (CI) ensures that every time code is pushed to a repository, it is automatically built, linted, and unit-tested to catch bugs early and prevent broken code from merging. Continuous Deployment (CD) automates the release process; if the CI passes, the CD step automatically builds a Docker image, pushes it to a container registry, and deploys it to the production server (via SSH or Kubernetes) with zero manual intervention.

Detailed Answer:

As a full-stack engineer, you shouldn’t be manually FTPing or SSHing into servers to pull code and run npm install.

  • The CI Phase (Validation): Triggered on Pull Requests to the main branch. The runner spins up an isolated Ubuntu environment, sets up Node, runs ESLint (npm run lint), runs Jest tests (npm run test), and checks for vulnerable dependencies. If any step fails, the PR is physically blocked from merging.
  • The CD Phase (Delivery): Triggered when code is merged into main. It securely retrieves cloud credentials from GitHub Secrets, builds the production bundle, containerizes it, and orchestrates a Zero-Downtime rolling update on the target server.

YAML

# English Code Example: CI Pipeline in GitHub Actions (.github/workflows/ci.yml)
name: Node.js CI
# Trigger on pull requests to the main branch
on:
pull_request:
branches: [ "main" ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Setup Node.js Environment
uses: actions/setup-node@v3
with:
node-version: '20.x'
cache: 'npm' # Automatically caches node_modules for speed
- name: Install Dependencies securely
run: npm ci
- name: Run Linter
run: npm run lint
- name: Execute Unit Tests
run: npm run test

প্রশ্ন ৬: GitHub Actions ব্যবহার করে Node.js API-এর জন্য আপনি একটি CI/CD পাইপলাইন কীভাবে আর্কিটেক্ট করবেন, এবং Continuous Integration ও Continuous Deployment এর মধ্যে পার্থক্য কী?

সংক্ষিপ্ত উত্তর:

একটি শক্তিশালী CI/CD পাইপলাইন কোড ভ্যালিডেশন এবং ডেলিভারি স্বয়ংক্রিয় করে দেয়। Continuous Integration (CI) নিশ্চিত করে যে রিপোজিটরিতে কোড পুশ হওয়ামাত্রই তা স্বয়ংক্রিয়ভাবে বিল্ড, লিন্ট (lint) এবং ইউনিট-টেস্ট হবে, যাতে আগেভাগেই বাগ ধরা পড়ে এবং ভাঙা কোড মূল ব্রাঞ্চে মার্জ হতে না পারে। Continuous Deployment (CD) রিলিজ প্রসেসকে স্বয়ংক্রিয় করে; CI পাস করলে CD নিজে থেকে ডকার ইমেজ বিল্ড করে, তা কনটেইনার রেজিস্ট্রিতে পাঠায় এবং কোনো ম্যানুয়াল কাজ ছাড়াই প্রোডাকশন সার্ভারে ডিপ্লয় করে দেয়।

বিস্তারিত উত্তর:

ফুল-স্ট্যাক ইঞ্জিনিয়ার হিসেবে আপনার ম্যানুয়ালি সার্ভারে SSH করে কোড পুল বা npm install করা উচিত নয়।

  • CI ফেজ (ভ্যালিডেশন): main ব্রাঞ্চে Pull Request দিলে এটি ট্রিগার হয়। এটি একটি আইসোলেটেড Ubuntu এনভায়রনমেন্ট চালু করে, Node সেটআপ করে, ESLint এবং Jest টেস্ট রান করে। কোনো স্টেপে ফেইল করলে PR মার্জ হওয়া ব্লক হয়ে যায়।
  • CD ফেজ (ডেলিভারি): কোড main এ মার্জ হলে এটি ট্রিগার হয়। এটি GitHub Secrets থেকে নিরাপদে ক্লাউডের ক্রেডেনশিয়াল নেয়, প্রোডাকশন বান্ডেল তৈরি করে এবং সার্ভারে জিরো-ডাউনটাইম আপডেট সম্পন্ন করে।

YAML

# বাংলা কোড এক্সাম্পল: GitHub Actions এর CI পাইপলাইন (.github/workflows/ci.yml)
name: Node.js CI
# main ব্রাঞ্চে Pull Request দিলে এটি রান করবে
on:
pull_request:
branches: [ "main" ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: রিপোজিটরি চেকআউট করা
uses: actions/checkout@v3
- name: Node.js এনভায়রনমেন্ট সেটআপ করা
uses: actions/setup-node@v3
with:
node-version: '20.x'
cache: 'npm' # স্পিডের জন্য node_modules ক্যাশ করে রাখবে
- name: নিরাপদে ডিপেন্ডেন্সি ইনস্টল করা
run: npm ci
- name: Linter রান করা
run: npm run lint
- name: Unit Tests এক্সিকিউট করা
run: npm run test

Are there any specific AWS services (like S3, ECS, or Lambda) or caching strategies (like Write-Through vs. Cache-Aside) you’d like to explore next in the System Design domain?

এখানে আপনার জন্য System Design এবং Architecture-এর ওপর আরও ১০টি অত্যন্ত গুরুত্বপূর্ণ প্রশ্ন ও উত্তর দেওয়া হলো (Q7 থেকে Q16)। একজন সিনিয়র আর্কিটেক্ট কীভাবে স্কেলিং, ক্যাশিং, সিকিউরিটি এবং ডিস্ট্রিবিউটেড সিস্টেম নিয়ে চিন্তা করেন, তা এখানে বিস্তারিতভাবে তুলে ধরা হয়েছে।

Phase 2: Advanced System Design, Caching, Scaling & Network Architecture

Question 7: Load Balancing: What is the architectural difference between Layer 4 and Layer 7 Load Balancing?

Short Answer:

Layer 4 (Transport Layer) load balancing routes traffic based strictly on IP addresses and TCP/UDP ports. It is extremely fast but blind to the actual content. Layer 7 (Application Layer) load balancing inspects the actual HTTP headers, cookies, and URLs. It is slightly slower but allows intelligent, content-based routing (e.g., routing /api traffic to Node.js servers and /images to an S3 bucket).

Detailed Answer:

Get Yeasaleh’s stories in your inbox

Join Medium for free to get updates from this writer.

Architects choose load balancers based on the processing needs of the system.

  • Layer 4: It acts like a high-speed traffic cop. If you have a massive multiplayer game or an IoT backend that relies on pure TCP connections, Layer 4 (like AWS Network Load Balancer or HAProxy) is ideal because it uses minimal CPU.
  • Layer 7: In microservices, Layer 7 (like AWS Application Load Balancer or NGINX) is mandatory. It terminates the SSL connection, reads the HTTP payload, and routes intelligently. For example, if the header contains Language: bn, it can route the user directly to a localized server. It also handles Session Stickiness based on cookies.

প্রশ্ন ৭: Load Balancing: Layer 4 এবং Layer 7 লোড ব্যালেন্সিংয়ের মধ্যে আর্কিটেকচারাল পার্থক্য কী?

সংক্ষিপ্ত উত্তর:

Layer 4 (Transport Layer) লোড ব্যালেন্সিং শুধুমাত্র IP অ্যাড্রেস এবং TCP/UDP পোর্টের ওপর ভিত্তি করে ট্রাফিক রাউট করে। এটি অত্যন্ত ফাস্ট, কিন্তু রিকোয়েস্টের ভেতরে কী ডেটা আছে তা এটি দেখতে পারে না। Layer 7 (Application Layer) লোড ব্যালেন্সিং সরাসরি HTTP হেডার, কুকি এবং URL স্ক্যান করে। এটি সামান্য স্লো হলেও ইনটেলিজেন্ট রাউটিং করতে পারে (যেমন- /api রিকোয়েস্টগুলো Node.js সার্ভারে এবং /images রিকোয়েস্টগুলো S3 বাকেটে পাঠানো)।

বিস্তারিত উত্তর:

সিস্টেমের প্রয়োজনের ওপর ভিত্তি করে আর্কিটেক্টরা লোড ব্যালেন্সার নির্বাচন করেন।

  • Layer 4: এটি একটি হাই-স্পিড ট্রাফিক পুলিশের মতো কাজ করে। যদি আপনার কোনো বিশাল গেম বা IoT ব্যাকএন্ড থাকে যা সরাসরি TCP কানেকশন ব্যবহার করে, তবে Layer 4 (যেমন AWS NLB) সেরা, কারণ এটি প্রসেসরের ওপর খুব কম চাপ ফেলে।
  • Layer 7: মাইক্রোসার্ভিসে Layer 7 (যেমন NGINX বা AWS ALB) বাধ্যতামূলক। এটি SSL টার্মিনেট করে, HTTP রিকোয়েস্ট পড়ে এবং স্মার্টলি রাউট করে। যেমন- হেডারে Language: bn থাকলে এটি ট্রাফিককে সরাসরি নির্দিষ্ট লোকাল সার্ভারে পাঠাতে পারে। এছাড়া কুকি স্ক্যান করে ইউজারের সেশন স্টিকিনেস (Session Stickiness) বজায় রাখে।

Question 8: Caching Strategies: Compare Cache-Aside, Write-Through, and Write-Behind caching.

Short Answer:

  • Cache-Aside (Lazy Loading): The application first checks the cache. If missed, it reads from the DB, saves it to the cache, and returns it. Best for read-heavy workloads.
  • Write-Through: The application writes data to the cache, and the cache immediately writes it synchronously to the database. Guarantees consistency but adds latency to write operations.
  • Write-Behind (Write-Back): The application writes data only to the cache and instantly returns success. The cache asynchronously flushes the data to the database later. Extremely fast writes, but risks data loss if the cache server crashes before the sync.

Detailed Answer:

Choosing the wrong caching strategy causes stale data or lost transactions.

  • Enterprise Use Case: For a blog or E-commerce catalog, Cache-Aside with a Time-To-Live (TTL) is perfect. For a banking application where the user must instantly see their updated balance, Write-Through ensures the Redis cache and Postgres DB are strictly synchronized. For high-velocity metrics (like YouTube video view counts), Write-Behind is used to batch 10,000 views in Redis and write them to the database once every 10 seconds.

প্রশ্ন ৮: Caching Strategies: Cache-Aside, Write-Through এবং Write-Behind ক্যাশিংয়ের তুলনা করুন।

সংক্ষিপ্ত উত্তর:

  • Cache-Aside: অ্যাপ প্রথমে ক্যাশে ডেটা খোঁজে। না পেলে ডেটাবেস থেকে এনে ক্যাশে সেভ করে এবং ইউজারকে দেখায়। এটি Read-Heavy সিস্টেমের জন্য সেরা।
  • Write-Through: অ্যাপ ক্যাশে ডেটা লেখে, এবং ক্যাশ সাথে সাথেই (synchronously) সেটি ডেটাবেসে লেখে। ডেটার কনসিস্টেন্সি ১০০% থাকে, তবে ডেটা রাইট হতে একটু সময় বেশি লাগে।
  • Write-Behind: অ্যাপ শুধুমাত্র ক্যাশে ডেটা লিখে ইউজারকে রেসপন্স দিয়ে দেয়। ক্যাশ পরবর্তীতে ব্যাকগ্রাউন্ডে ডেটাবেসে ডেটা আপডেট করে। এটি অত্যন্ত ফাস্ট, কিন্তু ডেটাবেসে সেভ হওয়ার আগে ক্যাশ সার্ভার ক্র্যাশ করলে ডেটা চিরতরে হারিয়ে যাওয়ার ঝুঁকি থাকে।

বিস্তারিত উত্তর:

ভুল ক্যাশিং পদ্ধতি বেছে নিলে ইউজার পুরনো ডেটা দেখতে পারে বা ডেটা হারিয়ে যেতে পারে।

  • ব্যবহারের ক্ষেত্র: ব্লগ বা ই-কমার্স ক্যাটালগের জন্য TTL সহ Cache-Aside সবচেয়ে ভালো। ব্যাংকিং সিস্টেমে যেখানে আপডেট ব্যালেন্স সাথে সাথে দেখা জরুরি, সেখানে Write-Through ব্যবহার করে Redis এবং Postgres কে নিখুঁতভাবে সিঙ্ক রাখা হয়। আর ইউটিউব ভিউ কাউন্টের মতো প্রচুর রাইট আসা সিস্টেমে Write-Behind ব্যবহার করা হয়, যাতে Redis ১০ হাজার ভিউ জমা করে প্রতি ১০ সেকেন্ড পর পর একবার ডেটাবেসে আপডেট করে।

Question 9: Database Scaling: What is the architectural difference between Read Replicas (Replication) and Database Sharding?

Short Answer:

Replication duplicates the entire database onto multiple nodes (Read Replicas). Writes go to the Primary node, which synchronizes with the replicas. It scales Read capacity but not Write capacity or Storage limit. Sharding partitions a single massive dataset horizontally across multiple independent databases based on a Shard Key (e.g., User IDs A-M go to DB1, N-Z go to DB2). It scales both Write capacity and Storage, but introduces immense architectural complexity.

Detailed Answer:

  • Replication (The First Step): If your Node.js API is overwhelmed by dashboard queries, you add Read Replicas. The application routes all SELECT queries to Replicas and all INSERT/UPDATE queries to the Master. However, if your table reaches 5 Terabytes, Replication won't help because every replica must also store the full 5TB.
  • Sharding (The Last Resort): Sharding is extremely painful. You lose the ability to perform simple SQL JOINs across shards. If a user in US-East (DB1) buys a product from US-West (DB2), your Node.js application must manually query both databases and join the data in memory. You only Shard when vertical scaling and read replicas have been completely exhausted.

প্রশ্ন ৯: ডেটাবেস স্কেলিং: Read Replicas (Replication) এবং Database Sharding এর মধ্যে আর্কিটেকচারাল পার্থক্য কী?

সংক্ষিপ্ত উত্তর:

Replication পুরো ডেটাবেসের হুবহু কপি একাধিক সার্ভারে (Read Replicas) তৈরি করে। রাইট অপারেশনগুলো প্রাইমারি নোডে যায় এবং সেখান থেকে রেপ্লিকাগুলোতে সিঙ্ক হয়। এটি শুধুমাত্র রিড (Read) ক্যাপাসিটি বাড়ায়, রাইট বা স্টোরেজ ক্যাপাসিটি নয়। অন্যদিকে Sharding একটি বিশাল ডেটাবেসকে Shard Key অনুযায়ী (যেমন ইউজার A-M ডেটাবেস ১-এ, N-Z ডেটাবেস ২-এ) আড়াআড়িভাবে 여러 ডেটাবেসে ভাগ করে দেয়। এটি রাইট এবং স্টোরেজ দুটোই স্কেল করে, কিন্তু প্রচুর জটিলতা তৈরি করে।

বিস্তারিত উত্তর:

  • Replication (প্রাথমিক পদক্ষেপ): আপনার API যদি রিড কুয়েরির কারণে স্লো হয়ে যায়, তবে আপনি Read Replica যুক্ত করবেন। অ্যাপ্লিকেশন সব SELECT কুয়েরি রেপ্লিকাতে এবং সব UPDATE/INSERT প্রাইমারিতে পাঠাবে। কিন্তু আপনার টেবিল যদি ৫ টেরাবাইট হয়ে যায়, তবে রেপ্লিকেশন কোনো কাজে আসবে না, কারণ প্রতিটি রেপ্লিকাকেও ৫ টেরাবাইট ডেটাই সেভ করতে হবে।
  • Sharding (শেষ উপায়): শার্ডিং করা অত্যন্ত কঠিন কাজ। এতে ডেটাবেসগুলো আলাদা হয়ে যাওয়ায় আপনি সাধারণ SQL JOIN করতে পারবেন না। DB1-এর কোনো ইউজারের সাথে DB2-এর ডেটা জয়েন করতে হলে, আপনার Node.js সার্ভারকে দুটি ডেটাবেস থেকে ডেটা এনে মেমরিতে জয়েন করতে হবে। যখন অন্য সব অপশন শেষ হয়ে যায়, কেবল তখনই শার্ডিং করা হয়।

Question 10: Event Streaming vs Message Queues: When do you choose Apache Kafka over RabbitMQ?

Short Answer:

RabbitMQ is a traditional Message Queue based on a “Smart Broker / Dumb Consumer” model. Once a message is successfully consumed, it is permanently deleted from the queue. Apache Kafka is a distributed Event Streaming platform based on an “Append-Only Log.” Events are persisted on disk for a configured retention period (e.g., 7 days). Consumers read from logs at their own pace and can “replay” past events.

Detailed Answer:

  • Choose RabbitMQ: For task queuing and async processing where state doesn’t matter after completion. E.g., Sending a password reset email. The Node server puts the task in RabbitMQ. The Email Worker consumes it, sends the email, and the message is deleted.
  • Choose Kafka: For Event Sourcing, data pipelines, and massive throughput (millions of events per second). E.g., A ride-sharing app tracking driver GPS locations every second. If an analytics service crashes for 2 days, upon rebooting, it can simply “rewind” its Kafka consumer offset and replay all the GPS events it missed over the last 48 hours to reconstruct the data.

প্রশ্ন ১০: Event Streaming বনাম Message Queues: আপনি কখন RabbitMQ এর বদলে Apache Kafka বেছে নেবেন?

সংক্ষিপ্ত উত্তর:

RabbitMQ হলো একটি ট্র্যাডিশনাল মেসেজ কিউ (Message Queue) যা কাজ প্রসেস হওয়ার সাথে সাথেই কিউ থেকে মেসেজটি চিরতরে ডিলিট করে দেয়। অন্যদিকে Apache Kafka হলো একটি ইভেন্ট স্ট্রিমিং (Event Streaming) প্ল্যাটফর্ম যা “Append-Only Log” হিসেবে কাজ করে। ইভেন্টগুলো প্রসেস হওয়ার পরও নির্দিষ্ট সময় (যেমন ৭ দিন) পর্যন্ত হার্ডডিস্কে সেভ থাকে। এর ফলে কনজিউমাররা (Consumers) নিজেদের গতিতে ডেটা রিড করতে পারে এবং চাইলে অতীতের ইভেন্টগুলো পুনরায় “রিপ্লে (replay)” করতে পারে।

বিস্তারিত উত্তর:

  • কখন RabbitMQ ব্যবহার করবেন: টাস্ক কিউইং (Task Queuing) এর জন্য এটি সেরা, যেখানে কাজ শেষ হওয়ার পর ডেটার আর দরকার নেই। যেমন- ইমেইল পাঠানো। Node সার্ভার RabbitMQ-তে টাস্ক পাঠায়, ইমেইল সার্ভার তা প্রসেস করে এবং মেসেজ ডিলিট হয়ে যায়।
  • কখন Kafka ব্যবহার করবেন: Event Sourcing, ডেটা পাইপলাইন এবং বিশাল ট্রাফিকের (সেকেন্ডে লাখ লাখ ইভেন্ট) জন্য এটি অপরিহার্য। যেমন- উবারের মতো অ্যাপে প্রতি সেকেন্ডে ড্রাইভারের GPS লোকেশন ট্র্যাক করা। কোনো অ্যানালিটিক্স সার্ভার যদি ২ দিনের জন্য ক্র্যাশ করে, তবে সে আপ হওয়ার পর Kafka-র অফসেট (offset) পেছনের দিকে নিয়ে গত ৪৮ ঘণ্টার সব মিস হওয়া ইভেন্ট আবার নতুন করে রিপ্লে করে ডেটা রিকভার করতে পারে।

Question 11: Explain Rate Limiting Algorithms. What is the difference between Token Bucket and Leaky Bucket?

Short Answer:

Rate limiting protects APIs from abuse. The Token Bucket algorithm assigns a bucket with a maximum token capacity to a user, refilling it at a constant rate. It allows sudden bursts of traffic as long as tokens are available. The Leaky Bucket algorithm acts like a queue; requests enter the bucket and are processed (leak out) at a strict, constant rate. It smooths out traffic bursts entirely, enforcing an absolutely steady output rate.

Detailed Answer:

  • Token Bucket (Standard for APIs): Used by AWS API Gateway and Stripe. If a user is allowed 100 requests/minute, their bucket holds 100 tokens. If they send 50 requests in 1 second, they consume 50 tokens (Burst allowed). If the bucket empties, subsequent requests return 429 Too Many Requests until tokens refill.
  • Leaky Bucket (Traffic Shaping): Useful for protecting legacy databases. If 1,000 requests hit the API at once, the bucket catches them, but only processes exactly 10 requests per second. Excess requests immediately spill over and are dropped. This guarantees the backend is never overwhelmed by sudden spikes.

প্রশ্ন ১১: Rate Limiting অ্যালগরিদমগুলো বুঝিয়ে বলুন। Token Bucket এবং Leaky Bucket এর মধ্যে পার্থক্য কী?

সংক্ষিপ্ত উত্তর:

রেট লিমিটিং API-কে অ্যাবিউস বা স্প্যাম থেকে রক্ষা করে। Token Bucket অ্যালগরিদম একজন ইউজারকে একটি নির্দিষ্ট টোকেন ক্যাপাসিটির বাকেট দেয়, যা নির্দিষ্ট হারে রিফিল (refill) হয়। বাকেটে টোকেন থাকলে এটি হঠাৎ আসা প্রচুর রিকোয়েস্টও (Bursts) একবারে রিসিভ করতে পারে। অন্যদিকে Leaky Bucket একটি কিউ (queue)-এর মতো কাজ করে; রিকোয়েস্টগুলো বাকেটে এসে জমা হয় এবং একটি নির্দিষ্ট ও ধীর গতিতে প্রসেস (লিক) হয়। এটি হঠাৎ আসা ট্রাফিক স্পাইককে পুরোপুরি স্মুথ করে দেয়।

বিস্তারিত উত্তর:

  • Token Bucket (API এর জন্য স্ট্যান্ডার্ড): AWS বা Stripe এটি ব্যবহার করে। ইউজারকে যদি মিনিটে ১০০ রিকোয়েস্টের লিমিট দেওয়া হয়, তবে তার বাকেটে ১০০ টোকেন থাকবে। সে ১ সেকেন্ডে ৫০টি রিকোয়েস্ট পাঠালে ৫০টি টোকেন কেটে নেওয়া হবে (একসাথে রিকোয়েস্ট পাঠানো সম্ভব)। টোকেন শেষ হলে রিফিল না হওয়া পর্যন্ত সে 429 Too Many Requests এরর পাবে।
  • Leaky Bucket (ট্রাফিক শেপিং): এটি পুরনো ডেটাবেসকে অতিরিক্ত চাপ থেকে বাঁচাতে কাজ করে। একসাথে ১,০০০ রিকোয়েস্ট এলেও এটি সেকেন্ডে মাত্র ১০টি রিকোয়েস্টই ডেটাবেসে পাঠাবে। বাকিগুলো ওভারফ্লো হয়ে ড্রপ করবে। ফলে ব্যাকএন্ড সার্ভারে কখনোই হঠাৎ চাপ পড়ে না।

Question 12: Idempotency in API Design: Why is an Idempotency Key strictly required for financial transactions?

Short Answer:

An Idempotent operation produces the exact same result no matter how many times it is executed. In financial APIs (like Stripe), if a client’s network drops right after triggering a payment, the client doesn’t know if the server processed it. If they retry the POST request, they risk double-charging the customer. By generating a unique Idempotency-Key (UUID) on the frontend and sending it in the headers, the server caches the result of the first successful transaction. Any subsequent retry with the same key safely returns the cached response without charging the card twice.

Detailed Answer:

HTTP GET, PUT, and DELETE are naturally idempotent. POST is not.

  1. The Flaw: User clicks “Checkout”. The Node API processes the payment and charges $50. But before the API can send the 200 OK response back, the user's 4G connection drops. The user sees an error and angrily clicks "Checkout" again. The API processes another $50.
  2. The Idempotency Key Architecture: The frontend generates a UUID (e.g., key-123) when the checkout page loads.
  • Attempt 1: API receives key-123, charges $50, and saves { idempotencyKey: 'key-123', status: 'success', data: {...} } in Redis.
  • Attempt 2 (Retry): API receives key-123. It checks Redis, sees it was already processed, completely skips the payment gateway logic, and instantly returns the cached success response.

JavaScript

// English Code Example: Implementing Idempotency in Express
app.post('/api/charge', async (req, res) => {
const idempotencyKey = req.headers['x-idempotency-key'];

// 1. Check if we already processed this key
const cachedResponse = await redis.get(`idempotent_${idempotencyKey}`);
if (cachedResponse) {
return res.json(JSON.parse(cachedResponse)); // Return cached success!
}
    // 2. Perform the actual heavy transaction
const payment = await Stripe.charge(req.body.amount);
// 3. Save result in cache for 24 hours
await redis.setex(`idempotent_${idempotencyKey}`, 86400, JSON.stringify(payment));
res.json(payment);
});

প্রশ্ন ১২: API ডিজাইনে Idempotency কী: আর্থিক লেনদেনের (Financial Transactions) ক্ষেত্রে Idempotency Key কেন বাধ্যতামূলক?

সংক্ষিপ্ত উত্তর:

Idempotent অপারেশন হলো এমন একটি কাজ যা একাধিকবার রান করলেও রেজাল্ট সবসময় একই থাকে। পেমেন্ট API-তে (যেমন Stripe), পেমেন্ট রিকোয়েস্ট পাঠানোর পরপরই ইউজারের নেটওয়ার্ক ড্রপ করলে ইউজার বুঝতে পারে না পেমেন্ট হয়েছে কি হয়নি। সে আবার রিকোয়েস্ট পাঠালে অ্যাকাউন্ট থেকে দুবার টাকা কাটার ঝুঁকি থাকে। ফ্রন্টএন্ড থেকে একটি ইউনিক Idempotency-Key তৈরি করে হেডারে পাঠালে, সার্ভার প্রথম সফল ট্রানজ্যাকশনের রেজাল্ট ক্যাশ করে রাখে। একই কি (key) দিয়ে আবার রিকোয়েস্ট এলে সার্ভার নতুন করে টাকা না কেটে পুরনো সফল রেসপন্সটিই ফেরত দেয়।

বিস্তারিত উত্তর:

HTTP GET, PUT, এবং DELETE ডিফল্টভাবেই আইডেমপোটেন্ট। কিন্তু POST নয়।

১. সমস্যা: ইউজার “Checkout” এ ক্লিক করল। Node API ৫০ ডলার কাটল। কিন্তু 200 OK রেসপন্স ইউজারের কাছে যাওয়ার আগেই তার নেটওয়ার্ক চলে গেল। ইউজার আবার ক্লিক করল, সার্ভার আবার ৫০ ডলার কাটল।

২. Idempotency Key আর্কিটেকচার: চেকআউট পেজ লোড হলেই ফ্রন্টএন্ড একটি UUID (যেমন key-123) তৈরি করে।

* প্রথম চেষ্টা: সার্ভার key-123 রিসিভ করে, টাকা কাটে এবং Redis-এ সেভ করে রাখে।

* দ্বিতীয় চেষ্টা (Retry): সার্ভার আবার key-123 পায়। সে Redis চেক করে দেখে এটি আগেই প্রসেস হয়েছে, তাই পেমেন্ট গেটওয়ের লজিক পুরোপুরি স্কিপ করে আগের সফল রেসপন্সটিই দিয়ে দেয়।

JavaScript

// বাংলা কোড এক্সাম্পল: Express-এ Idempotency ইমপ্লিমেন্ট করা
app.post('/api/charge', async (req, res) => {
const idempotencyKey = req.headers['x-idempotency-key'];

// ১. চেক করা এই কি (key) আগেই প্রসেস হয়েছে কিনা
const cachedResponse = await redis.get(`idempotent_${idempotencyKey}`);
if (cachedResponse) {
return res.json(JSON.parse(cachedResponse)); // ডাবল চার্জ না করে ক্যাশ করা রেজাল্ট দেওয়া!
}
    // ২. আসল পেমেন্ট ট্রানজ্যাকশন রান করা
const payment = await Stripe.charge(req.body.amount);
// ৩. ভবিষ্যতের রিট্রাইয়ের জন্য রেজাল্টটি ২৪ ঘণ্টা সেভ রাখা
await redis.setex(`idempotent_${idempotencyKey}`, 86400, JSON.stringify(payment));
res.json(payment);
});

Question 13: What is the architectural distinction between WebSockets and Server-Sent Events (SSE)? When do you use which?

Short Answer:

WebSockets provide a full-duplex, bidirectional TCP connection where both the client and server can send messages to each other at any time with extremely low latency. Server-Sent Events (SSE) provide a unidirectional (Server-to-Client) stream over standard HTTP protocol. Once the client establishes the connection, only the server can push updates down the pipe.

Detailed Answer:

Choosing the wrong real-time protocol creates unnecessary backend complexity.

  • WebSockets Use Case: Mandatory for multiplayer games, collaborative editing (Google Docs), or real-time chat applications where the client is constantly pushing rapid inputs to the server, and the server is pushing state back.
  • SSE Use Case: Ideal for Live Sports Scores, Stock Market Tickers, or Social Media Notifications. In these scenarios, the client only needs to listen for updates and rarely sends data back. SSE operates over standard HTTP/1.1 or HTTP/2, meaning it doesn’t require special load balancer configurations and bypasses corporate firewalls perfectly, unlike WebSockets which sometimes get blocked.

প্রশ্ন ১৩: WebSockets এবং Server-Sent Events (SSE) এর মধ্যে আর্কিটেকচারাল পার্থক্য কী? আপনি কখন কোনটি ব্যবহার করবেন?

সংক্ষিপ্ত উত্তর:

WebSockets ক্লায়েন্ট এবং সার্ভারের মধ্যে একটি পূর্ণাঙ্গ, দ্বিমুখী (bidirectional) TCP কানেকশন তৈরি করে, যেখানে ক্লায়েন্ট এবং সার্ভার উভয়েই যেকোনো সময় একে অপরকে ডেটা পাঠাতে পারে। অন্যদিকে Server-Sent Events (SSE) সাধারণ HTTP প্রোটোকলের ওপর ভিত্তি করে একমুখী (Server-to-Client) স্ট্রীম তৈরি করে। একবার কানেকশন তৈরি হলে শুধুমাত্র সার্ভারই ক্লায়েন্টের কাছে আপডেট পুশ করতে পারে, ক্লায়েন্ট পারে না।

বিস্তারিত উত্তর:

ভুল রিয়েল-টাইম প্রোটোকল বেছে নিলে ব্যাকএন্ডের জটিলতা অযথাই বেড়ে যায়।

  • WebSockets এর ব্যবহার: মাল্টিপ্লেয়ার গেম, কোলাবোরেটিভ এডিটিং (যেমন Google Docs) বা রিয়েল-টাইম চ্যাটের জন্য এটি বাধ্যতামূলক, যেখানে ক্লায়েন্ট প্রতিনিয়ত সার্ভারে ডেটা পাঠায় এবং সার্ভারও রিপ্লাই দেয়।
  • SSE এর ব্যবহার: লাইভ খেলার স্কোর, শেয়ার বাজারের আপডেট বা নোটিফিকেশনের জন্য এটি সেরা। এসব ক্ষেত্রে ক্লায়েন্টকে শুধু আপডেট শুনতে হয়, সার্ভারে কিছু পাঠানোর দরকার হয় না। SSE সাধারণ HTTP প্রোটোকলে চলে বলে ফায়ারওয়াল একে ব্লক করে না, যেখানে ওয়েবসকেট অনেক সময় কর্পোরেট ফায়ারওয়ালে আটকে যায়।

Question 14: Disaster Recovery: What is the difference between RPO (Recovery Point Objective) and RTO (Recovery Time Objective)?

Short Answer:

In System Architecture, RPO (Recovery Point Objective) defines the maximum acceptable amount of Data Loss measured in time (e.g., “We can afford to lose the last 15 minutes of data”). RTO (Recovery Time Objective) defines the maximum acceptable amount of Downtime before the system must be restored and operational again (e.g., “The site must be back online within 1 hour of a crash”).

Detailed Answer:

These two metrics dictate your entire infrastructure budget and backup strategy.

  • Architecting for RPO: If your business dictates an RPO of 24 hours, taking a simple database backup via pg_dump every night at midnight is sufficient. If the RPO is zero (financial institutions), you must use continuous synchronous replication across multiple regions.
  • Architecting for RTO: If your RTO is 4 hours, you can afford to manually spin up new AWS EC2 servers, download backups from S3, and restore them. If your RTO is 1 minute, you must invest heavily in an “Active-Active” architecture, where multiple identical cloud regions are running simultaneously behind a global traffic manager like Route53 to seamlessly failover instantly.

প্রশ্ন ১৪: ডিজাস্টার রিকভারি (Disaster Recovery): RPO (Recovery Point Objective) এবং RTO (Recovery Time Objective) এর মধ্যে পার্থক্য কী?

সংক্ষিপ্ত উত্তর:

সিস্টেম আর্কিটেকচারে, RPO (Recovery Point Objective) নির্ধারণ করে সিস্টেম ক্র্যাশ করলে সর্বোচ্চ কতটুকু ডেটা লস (Data Loss) ব্যবসা মেনে নিতে পারবে (যেমন- “আমরা সর্বোচ্চ গত ১৫ মিনিটের ডেটা হারালে সমস্যা নেই”)। আর RTO (Recovery Time Objective) নির্ধারণ করে সিস্টেম ক্র্যাশ করার পর পুনরায় সচল হতে সর্বোচ্চ কতক্ষণ ডাউনটাইম গ্রহণযোগ্য হবে (যেমন- “ক্র্যাশ করার ১ ঘণ্টার মধ্যে সাইট অবশ্যই লাইভ হতে হবে”)।

বিস্তারিত উত্তর:

এই দুটি মেট্রিক্সের ওপর ভিত্তি করেই আপনার ইনফ্রাস্ট্রাকচারের বাজেট এবং ব্যাকআপ স্ট্র্যাটেজি নির্ভর করে।

  • RPO আর্কিটেকচার: আপনার ব্যবসার RPO যদি ২৪ ঘণ্টা হয়, তবে প্রতিদিন রাত ১২টায় pg_dump দিয়ে ডেটাবেস ব্যাকআপ নেওয়াই যথেষ্ট। কিন্তু RPO যদি শূন্য হয় (যেমন ব্যাংক), তবে আপনাকে অবশ্যই একাধিক রিজিওনে কন্টিনিউয়াস ডেটা রেপ্লিকেশন (Continuous Replication) চালু রাখতে হবে।
  • RTO আর্কিটেকচার: আপনার RTO যদি ৪ ঘণ্টা হয়, তবে সার্ভার ক্র্যাশ করলে আপনি ম্যানুয়ালি নতুন AWS সার্ভার চালু করে S3 থেকে ডেটা রিস্টোর করার পর্যাপ্ত সময় পাবেন। কিন্তু RTO যদি ১ মিনিট হয়, তবে আপনাকে অনেক টাকা খরচ করে “Active-Active” আর্কিটেকচার বানাতে হবে, যেখানে Route53-এর পেছনে একাধিক ক্লাউড রিজিওন একসাথে লাইভ থাকবে এবং একদিক ডাউন হলে সাথে সাথে অন্যদিক কাজ শুরু করবে।

Question 15: Content Delivery Networks (CDN) and Edge Computing: How do they optimize global latency architectures?

Short Answer:

A CDN consists of a globally distributed network of proxy servers (Edge Nodes). Instead of a user in Australia requesting images from a primary origin server in New York (taking 300ms round-trip), the CDN caches the assets in a Sydney datacenter, serving them locally in 20ms. Edge Computing (like Cloudflare Workers or AWS Lambda@Edge) evolves this by moving actual backend compute logic (JavaScript/WASM) to these CDN nodes, executing code physically closer to the user to bypass origin server latency entirely.

Detailed Answer:

  • Static Asset Caching: A CDN intercepts requests. If it’s a Cache Hit, it serves the file instantly. If it’s a Cache Miss, the CDN pulls the file from the Origin server, caches it at the Edge, and serves it.
  • The Power of Edge Computing: Traditionally, APIs require hitting the central origin server. With Edge functions, you can deploy logic globally. For example, if you need to verify a JWT authentication token or perform an A/B test redirect based on cookies, an Edge function evaluates this logic in milliseconds at the local CDN node, never bothering the origin Node.js server.

প্রশ্ন ১৫: Content Delivery Networks (CDN) এবং Edge Computing: গ্লোবাল ল্যাটেন্সি অপ্টিমাইজ করতে এগুলো কীভাবে কাজ করে?

সংক্ষিপ্ত উত্তর:

CDN হলো বিশ্বজুড়ে ছড়িয়ে থাকা প্রক্সি সার্ভারের (Edge Nodes) একটি নেটওয়ার্ক। অস্ট্রেলিয়ার কোনো ইউজার নিউ ইয়র্কের মূল সার্ভার থেকে ইমেজ রিকোয়েস্ট করলে ৩০০ মিলি-সেকেন্ড সময় লাগতে পারে। এর বদলে CDN ওই ইমেজগুলো সিডনির ডাটা-সেন্টারে ক্যাশ করে রাখে এবং ২০ মিলি-সেকেন্ডে ইউজারকে দিয়ে দেয়। Edge Computing (যেমন Cloudflare Workers) এটিকে আরও একধাপ এগিয়ে নিয়েছে। এটি মূল সার্ভারের ব্যাকএন্ড লজিক (জাভাস্ক্রিপ্ট) সরাসরি ইউজারের কাছের এই CDN নোডগুলোতে রান করে, ফলে মূল সার্ভার পর্যন্ত যাওয়ার ল্যাটেন্সি পুরোপুরি দূর হয়ে যায়।

বিস্তারিত উত্তর:

  • স্ট্যাটিক ক্যাশিং: CDN ইউজারের রিকোয়েস্ট ধরে। ক্যাশে ডেটা থাকলে সাথে সাথে দিয়ে দেয়। না থাকলে মূল সার্ভার থেকে একবার ডেটা এনে ক্যাশ করে রাখে।
  • এজ কম্পিউটিংয়ের শক্তি: সাধারণত API কল করলে মূল সার্ভার পর্যন্ত যেতে হয়। কিন্তু এজ ফাংশন ব্যবহার করে আপনি লজিকগুলো গ্লোবালি ডিপ্লয় করতে পারেন। যেমন- ইউজারের JWT টোকেন ভেরিফাই করা বা কুকি দেখে A/B টেস্টের জন্য রিডাইরেক্ট করা। এই কাজগুলো মূল Node.js সার্ভার পর্যন্ত না গিয়ে, মিলি-সেকেন্ডের মধ্যে ইউজারের নিকটবর্তী CDN নোডেই সম্পন্ন হয়ে যায়।

Question 16: What is a Service Mesh (e.g., Istio) and why is it necessary in a large-scale Kubernetes microservices environment?

Short Answer:

A Service Mesh is a dedicated infrastructure layer that controls and monitors service-to-service communication in a microservices architecture. Instead of writing complex network logic (like retries, circuit breakers, load balancing, and mTLS encryption) inside every single Node.js or Go microservice, a Service Mesh injects a “Sidecar Proxy” alongside every container. The microservices talk only to their local proxy, and the proxies handle the complex, secure communication network.

Detailed Answer:

In a cluster with 50 microservices, managing network reliability in application code is an architectural anti-pattern.

  • The Problem: If Service A calls Service B and it fails, Service A needs retry logic. It needs to know Service B's IP address (Service Discovery). It needs to encrypt the traffic so internal hackers can't sniff it. Writing this code in 50 repositories is unmaintainable.
  • The Service Mesh Architecture (Sidecar Pattern): Istio places an Envoy Proxy (the sidecar) inside the exact same network namespace as your Node.js app container. Your Node app simply does fetch('http://service-b'). The proxy intercepts the call, encrypts it (mTLS), routes it securely to Service B's proxy, handles retries if the network drops, and emits distributed tracing metrics—all completely invisible to your Node.js application code.

প্রশ্ন ১৬: Service Mesh (যেমন Istio) কী, এবং বড় স্কেলের Kubernetes মাইক্রোসার্ভিস এনভায়রনমেন্টে এটি কেন প্রয়োজনীয়?

সংক্ষিপ্ত উত্তর:

Service Mesh হলো মাইক্রোসার্ভিসের মধ্যকার যোগাযোগ নিয়ন্ত্রণ এবং মনিটর করার জন্য একটি আলাদা ইনফ্রাস্ট্রাকচার লেয়ার। প্রতিটি Node.js বা Go মাইক্রোসার্ভিসের কোডের ভেতরে জটিল নেটওয়ার্ক লজিক (যেমন- রিট্রাই, সার্কিট ব্রেকার, লোড ব্যালেন্সিং বা এনক্রিপশন) লেখার বদলে, Service Mesh প্রতিটি কনটেইনারের সাথে একটি “Sidecar Proxy” বসিয়ে দেয়। মাইক্রোসার্ভিসগুলো শুধু তাদের নিজস্ব লোকাল প্রক্সির সাথে কথা বলে, আর প্রক্সিগুলো নিজেদের মধ্যে নিরাপদ ও জটিল যোগাযোগের পুরো নেটওয়ার্কটি ম্যানেজ করে।

বিস্তারিত উত্তর:

৫০টি মাইক্রোসার্ভিসের ক্লাস্টারে, অ্যাপ্লিকেশনের কোডের ভেতরে নেটওয়ার্ক লজিক হ্যান্ডল করা একটি আর্কিটেকচারাল ভুল।

  • সমস্যা: Service A যদি Service B-কে কল করে এবং ফেইল হয়, তবে Service A-তে রিট্রাই লজিক থাকতে হবে। ডেটা এনক্রিপ্ট করতে হবে যাতে ভেতরের কেউ ট্রাফিক হ্যাক করতে না পারে। ৫০টি আলাদা রিপোজিটরিতে এই কোড বারবার লেখা অসম্ভব।
  • সার্ভিস মেশ আর্কিটেকচার (Sidecar Pattern): Istio আপনার Node.js কনটেইনারের ঠিক পাশেই একটি Envoy Proxy (সাইডকার) বসিয়ে দেয়। আপনার Node.js অ্যাপ শুধু fetch('http://service-b') কল করে। প্রক্সিটি সেই রিকোয়েস্ট ধরে, এনক্রিপ্ট (mTLS) করে, নিরাপদে Service B-এর প্রক্সিতে পাঠায়, ফেইল হলে রিট্রাই করে এবং মনিটরিং ডেটা পাঠায়—যার কোনো কিছুই আপনার Node.js কোডকে জানতে হয় না।

--

--