জ্যাঙ্গো (Django) এবং স্প্রিং বুটের (Spring Boot) মতো হেভিওয়েট ফ্রেমওয়ার্কগুলোর পর এখন তুমি পাইথন ইকোসিস্টেমের সবচেয়ে আধুনিক ও দ্রুতগতির ফ্রেমওয়ার্ক FastAPI নিয়ে জানতে চেয়েছ। পাইথন ব্যাকএন্ড ডেভেলপমেন্টে, বিশেষ করে এআই-পাওয়ার্ড (AI-powered) অ্যাপ্লিকেশন, মাইক্রোসার্ভিস বা ফাস্ট ডেটা প্রসেসিংয়ের ক্ষেত্রে FastAPI বর্তমানে ডেভেলপারদের প্রথম পছন্দ।
FastAPI-এর আর্কিটেকচার জ্যাঙ্গো বা স্প্রিং বুটের চেয়ে একটু ভিন্ন। এটি মূলত Starlette (ওয়েব পার্ট) এবং Pydantic (ডেটা পার্ট)-এর ওপর ভিত্তি করে তৈরি। এটি খুব লাইটওয়েট হলেও এর কোর আর্কিটেকচারে ক্লাসিক ডিজাইন প্যাটার্নগুলোর অত্যন্ত স্মার্ট ও মডার্ন ইমপ্লিমেন্টেশন রয়েছে। চলো, গভীরভাবে বিশ্লেষণ করে দেখি FastAPI কীভাবে ডিজাইন প্যাটার্নগুলো ব্যবহার করে।
১. Dependency Injection (DI) Pattern (ডিপেন্ডেন্সি ইনজেকশন)
FastAPI তে ব্যবহার: Depends() সিস্টেম। গভীর বিশ্লেষণ: FastAPI-এর সবচেয়ে বড় শক্তি এবং সিগনেচার ফিচার হলো এর ডিপেন্ডেন্সি ইনজেকশন সিস্টেম। স্প্রিং বুটে যেমন @Autowired কাজ করে, FastAPI-তে ঠিক একইভাবে কাজ করে Depends()। তুমি যখন কোনো রাউটে (Route) ডাটাবেস কানেকশন, অথেনটিকেশন বা কোনো থার্ড-পার্টি সার্ভিস চাও, তখন ফাংশনের প্যারামিটারে Depends() কল করে দাও। FastAPI রানটাইমে নিজে থেকেই সেই অবজেক্ট বা ডিপেন্ডেন্সি তৈরি করে তোমার ফাংশনে ইনজেক্ট করে দেবে। এটি কোডকে লুজলি কাপলড (Loosely Coupled) এবং টেস্টিংয়ের জন্য দারুণ উপযোগী করে তোলে।
২. Singleton Pattern (সিঙ্গেলটন প্যাটার্ন)
FastAPI তে ব্যবহার: গ্লোবাল সেটিংস এবং lru_cache। গভীর বিশ্লেষণ: FastAPI-তে প্রজেক্টের কনফিগারেশন বা এনভায়রনমেন্ট ভেরিয়েবল (Env variables) লোড করার জন্য Pydantic-এর BaseSettings ব্যবহার করা হয়। কিন্তু প্রতিবার রিকোয়েস্ট আসার পর যদি ফাইল রিড করে সেটিংস লোড করা হয়, তবে পারফরম্যান্স ড্রপ করবে। এই সমস্যা সমাধানে FastAPI অফিশিয়ালি পাইথনের বিল্ট-ইন @lru_cache ডেকোরেটর ব্যবহার করতে রেকমেন্ড করে। এটি একটি ফাংশনকে সিঙ্গেলটন হিসেবে কাজ করতে বাধ্য করে। অর্থাৎ, প্রথমবারের পর কেউ সেটিংস চাইলে এটি মেমোরিতে থাকা ক্যাশড (Cached) ইনস্ট্যান্সটিই রিটার্ন করে।
৩. Decorator Pattern (ডেকোরেটর প্যাটার্ন)
FastAPI তে ব্যবহার: রাউটিং এবং মিডলওয়্যার। গভীর বিশ্লেষণ: FastAPI-তে রাউট ডিফাইন করা পুরোটাই ডেকোরেটর প্যাটার্ন-নির্ভর। তুমি যখন কোনো ফাংশনের ওপরে @app.get("/users") লেখো, তখন FastAPI মূল ফাংশনটিকে মডিফাই না করেই তার সাথে HTTP GET মেথড, পাথ অপারেশন, ভ্যালিডেশন এবং ডকুমেন্টেশন (Swagger UI) জেনারেট করার লজিক যুক্ত করে দেয়। এটি ডেকোরেটর প্যাটার্নের একটি চমৎকার ব্যবহার।
৪. Factory Pattern (ফ্যাক্টরি প্যাটার্ন)
FastAPI তে ব্যবহার: APIRouter এবং Pydantic Models। গভীর বিশ্লেষণ: বড় প্রজেক্টে সব রাউট একটি main.py ফাইলে রাখা সম্ভব নয়। তাই আমরা APIRouter ব্যবহার করে আলাদা আলাদা মডিউলে (যেমন: users, posts) রাউটগুলো ভাগ করে নিই এবং পরে মূল অ্যাপে যুক্ত করি। এখানে APIRouter মূলত অবজেক্ট তৈরি এবং রাউটগুলো ম্যানেজ করার জন্য একটি ফ্যাক্টরি হিসেবে কাজ করে। এছাড়া Pydantic যখন ইনকামিং JSON ডেটা পার্স করে ক্লাসের অবজেক্ট তৈরি করে, তখন সেটি ফ্যাক্টরি প্যাটার্ন অনুসরণ করেই ডেটা ভ্যালিডেট ও ইনিশিয়ালাইজ করে।
৫. Facade Pattern (ফেসেড প্যাটার্ন)
FastAPI তে ব্যবহার: FastAPI ক্লাস নিজেই। গভীর বিশ্লেষণ: FastAPI নিজে একটি চমৎকার Facade। এর ব্যাকগ্রাউন্ডে দুটি বিশাল ও জটিল সিস্টেম কাজ করে—একটি হলো Starlette (যা ASGI সার্ভার এবং ওয়েব রিকোয়েস্ট হ্যান্ডেল করে) এবং অন্যটি Pydantic (যা ডেটা টাইপ চেকিং এবং ভ্যালিডেশন করে)। ডেভেলপার হিসেবে তোমাকে Starlette-এর রিকোয়েস্ট লাইফসাইকেল বা Pydantic-এর কোর লজিক নিয়ে ভাবতে হয় না। FastAPI তোমাকে একটি খুব সহজ এবং ক্লিন ইন্টারফেস (Facade) দেয়, যার মাধ্যমে তুমি এই দুই শক্তিশালী সিস্টেমের সুবিধা একসাথে পাও।
৬. Chain of Responsibility Pattern (চেইন অফ রেসপনসিবিলিটি)
FastAPI তে ব্যবহার: BaseHTTPMiddleware। গভীর বিশ্লেষণ: জ্যাঙ্গো বা স্প্রিং বুটের মতোই FastAPI-তেও মিডলওয়্যারগুলো Chain of Responsibility প্যাটার্ন মেনে চলে। ক্লায়েন্ট থেকে কোনো রিকোয়েস্ট আসলে সেটি এক বা একাধিক মিডলওয়্যার (যেমন: CORS, Authentication, Logging) ক্রস করে। প্রতিটি মিডলওয়্যার রিকোয়েস্টটিকে প্রসেস করে call_next(request)-এর মাধ্যমে পরবর্তী ধাপে পাঠায়। যেকোনো ধাপে সমস্যা হলে চেইন ব্রেক করে রেসপন্স ফিরিয়ে দেওয়া যায়।
৭. Observer Pattern (অবজার্ভার প্যাটার্ন)
FastAPI তে ব্যবহার: Lifespan Events (@asynccontextmanager) এবং Background Tasks। গভীর বিশ্লেষণ: অ্যাপ্লিকেশন স্টার্ট হওয়ার সময় ডাটাবেস কানেক্ট করা বা মেশিন লার্নিং মডেল মেমোরিতে লোড করা এবং অ্যাপ্লিকেশন বন্ধ হওয়ার সময় সেগুলো ক্লিনআপ করার জন্য FastAPI-তে 'Lifespan Events' ব্যবহার করা হয়। এটি মূলত ইভেন্ট-ড্রিভেন বা অবজার্ভার প্যাটার্ন, যেখানে ফ্রেমওয়ার্ক স্টেট পরিবর্তনের (Startup/Shutdown) সিগন্যাল ব্রডকাস্ট করে। এছাড়া BackgroundTasks ব্যবহার করে ব্যাকগ্রাউন্ডে ইমেইল পাঠানো বা ডেটা প্রসেস করার কাজগুলোও ইভেন্ট-ড্রিভেন আর্কিটেকচার ফলো করে।
Python Code Example: FastAPI-তে Singleton এবং Dependency Injection
নিচে একটি রিয়েল-লাইফ এক্সাম্পল দেওয়া হলো, যেখানে FastAPI তে @lru_cache (Singleton) এবং Depends() (Dependency Injection)-এর চমৎকার ব্যবহার দেখানো হয়েছে। (কোডটি সম্পূর্ণ ইংরেজিতে লেখা হয়েছে)।
from fastapi import FastAPI, Depends
from pydantic_settings import BaseSettings
from functools import lru_cache
# ==========================================
# 1. Singleton Pattern via @lru_cache
# ==========================================
class DBSettings(BaseSettings):
database_url: str = "postgresql://user:password@localhost:5432/mydb"
max_connections: int = 10
# Using lru_cache ensures that the settings object is created only ONCE.
# Subsequent calls will return the exact same memory instance (Singleton).
@lru_cache()
def get_settings() -> DBSettings:
print("Loading database configurations... (This should print only once!)")
return DBSettings()
# ==========================================
# 2. Dependency Injection Pattern
# ==========================================
class DatabaseService:
def __init__(self, db_url: str):
self.db_url = db_url
def fetch_data(self):
return {"status": "success", "source": self.db_url}
# Factory/Provider function for Dependency Injection
def get_db_service(settings: DBSettings = Depends(get_settings)) -> DatabaseService:
# Injecting the singleton settings into the service
return DatabaseService(db_url=settings.database_url)
# ==========================================
# 3. FastAPI App Implementation
# ==========================================
app = FastAPI(title="FastAPI Patterns Demo")
@app.get("/users")
def read_users(db_service: DatabaseService = Depends(get_db_service)):
# The developer doesn't manually instantiate DatabaseService.
# FastAPI's DI system resolves get_db_service and injects it here.
data = db_service.fetch_data()
return {"message": "Fetching users", "data": data}
@app.get("/posts")
def read_posts(db_service: DatabaseService = Depends(get_db_service)):
# Calling another route reuses the singleton settings automatically.
data = db_service.fetch_data()
return {"message": "Fetching posts", "data": data}জ্যাঙ্গোর তুলনায় FastAPI অনেক বেশি আন-ওপিনিওনেটেড (Unopinionated)। অর্থাৎ, এটি তোমাকে নির্দিষ্ট কোনো আর্কিটেকচার চাপিয়ে দেয় না। কিন্তু এর ডিপেন্ডেন্সি ইনজেকশন সিস্টেম এতটাই পাওয়ারফুল যে, তুমি চাইলে এর ভেতরে খুব সহজেই সলিড (SOLID) প্রিন্সিপাল এবং যেকোনো ক্লাসিক ডিজাইন প্যাটার্ন ইমপ্লিমেন্ট করে এন্টারপ্রাইজ গ্রেডের স্কেলেবল ব্যাকএন্ড তৈরি করতে পারো।