توی دنیای برنامه ‌نویسی سنتی، ما عادت داشتیم همه چیز رو یکجا بسازیم. مثل یک قلعه بزرگ و سنگی که همه سرباز ها، آشپز ها و پادشاه توی یک ساختمان زندگی می کردند. به این مدل میگن Monolith (یکپارچه). اما وقتی وارد دنیای مدرن و ابزار هایی مثل FastAPI میشی، قضیه فرق میکنه. اینجا دیگه صحبت از یک قلعه نیست، صحبت از یک ارتش چابک و کماندو های جداگانه است.

اینجا یک سوال مهم پیش میاد: آیا خرد کردن برنامه به تیکه‌ های کوچیک کارمون رو سخت نمیکنه؟ میکرو سرویس اصلا چیه و چرا FastAPI بهترین رفیق ما تو این مسیره؟

تصور کن رفتی به یک فودکورت (Food Court) بزرگ. برخلاف رستوران سنتی که یک آشپزخانه واحد داره و هم پیتزا میزنه هم کباب، اینجا غرفه پیتزا جداست، غرفه نوشیدنی جداست و غرفه دسر هم جدا. هر غرفه (سرویس) کار خودش رو عالی انجام میده، اما همه با هم کار میکنن تا تو (کاربر) یک وعده غذایی کامل داشته باشی. معماری میکرو سرویس با FastAPI دقیقا همین فودکورت رو شبیه ‌سازی میکنه.

معماری میکروسرویس چیست؟

معماری میکروسرویس چیست؟

میکرو سرویس یک سبک معماریه که در اون اپلیکیشن به مجموعه ‌ای از سرویس‌ های کوچک و مستقل شکسته میشه. هر سرویس مسئول یک کار خاصه و با بقیه سرویس‌ ها حرف میزنه. به زبان ساده هدف میکرو سرویس اینه که: "به جای یک برنامه که ۱۰۰ تا کار انجام میده، ۱۰۰ تا برنامه کوچک داشته باشیم که هر کدوم ۱ کار رو عالی انجام میدن."

تفاوت: Monolith در برابر Microservices

تفاوت: Monolith در برابر Microservices

خیلی‌ ها فکر میکنن اگر پروژه رو بزرگ کنن یعنی دارن پیشرفت میکنن، اما گاهی بزرگی بیش از حد یعنی کندی. بیایید این گره رو باز کنیم:

در معماری سنتی (Monolith): همه چیز (دیتابیس، پرداخت، لاگین) توی یک پروژه بزرگ کد نویسی شده.

مشکل: اگر کد بخش "پرداخت" باگ داشته باشه، ممکنه کل سایت بیاد پایین! درست مثل اینکه اگر اجاق گاز رستوران خراب بشه، دیگه سالاد هم نمیتونن بدن.

در معماری میکروسرویس: هر بخش یک پروژه جداگانه است.

مزیت: اگر سرویس "نظرات" از کار بیفته، مردم هنوز میتونن "خرید" کنن. هر سرویس زنده و مستقله.

پیاده‌سازی با FastAPI

حرف بدون عمل باد هواست! تئوری قشنگه ولی تا نبینیم چطوری با چند خط کد میشه یک سرویس رو بالا آورد، قدرت FastAPI رو درک نمیکنیم. بیایید همون مثال فودکورت رو کد نویسی کنیم. ما اینجا دو تا میکرو سرویس ساده میسازیم که با هم حرف می‌زنند:

  1. سرویس انبار (Inventory): لیست غذا ها و قیمت‌ ها رو داره.

  2. سرویس سفارش (Order): سفارش مشتری رو میگیره و قیمت رو از انبار می ‌پرسه.

  سرویس انبار (آشپزخانه)

این فایل inventory_service.py هست. کارش فقط اینه که بگه چی داریم و قیمتش چنده. ببین چقدر تمیز و کوتاهه:

from fastapi import FastAPI, HTTPException

app = FastAPI()

# دیتابیس فرضی ما (همون یخچال رستوران)
food_db = {
    1: {"name": "پیتزا پپرونی", "price": 150000},
    2: {"name": "همبرگر ذغالی", "price": 120000},
    3: {"name": "سالاد سزار", "price": 95000}
}

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    # چک میکنیم غذا هست یا نه
    if item_id not in food_db:
        raise HTTPException(status_code=404, detail="این غذا رو نداریم!")
    
    # اطلاعات غذا رو برمیگردونیم
    return food_db[item_id]

# اجرا روی پورت ۸۰۰۰: uvicorn inventory_service:app --port 8000

چرا اینجا FastAPI میدرخشه؟ سادگی و سرعت. بدون هیچ کلاس اضافه یا تنظیمات پیچیده، سرور آمادست و داده‌ها رو به صورت خودکار Valid میکنه (مثلا item_id حتما باید عدد باشه).

 سرویس سفارش (گارسون)

حالا میرسیم به جایی که جادو اتفاق می‌افته. سرویس سفارش (order_service.py) قراره سفارش مشتری رو بگیره، اما قیمت رو نمیدونه! پس باید زنگ بزنه به سرویس انبار. (نکته: از httpx استفاده می‌کنیم که نسخه مدرن و Async کتابخانه requests هست)

from fastapi import FastAPI
import httpx # ابزار ارتباط با بقیه سرویس‌ها

app = FastAPI()

# آدرس سرویس انبار (که الان روی پورت ۸۰۰۰ روشنه)
INVENTORY_URL = "http://localhost:8000/items/"

@app.post("/create-order/{item_id}")
async def create_order(item_id: int):
    # مرحله ۱: ارتباط با سرویس انبار (بدون فریز شدن برنامه)
    async with httpx.AsyncClient() as client:
        # اینجا گارسون داد میزنه به آشپزخونه: قیمت این غذا چنده؟
        resp client.get(f"{INVENTORY_URL}{item_id}")
    
    # اگر انبار ارور داد (غذا نبود)
    if response.status_code != 200:
        return {"status": "failed", "msg": "شرمنده، مواد اولیه این غذا رو نداریم"}
    
    # مرحله ۲: محاسبه قیمت نهایی
    item_data = response.json()
    total_price = item_data['price'] * 1.09  # افزودن ۹ درصد مالیات
    
    # مرحله ۳: تحویل سفارش
    return {
        "status": "success",
        "food": item_data['name'],
        "final_bill": total_price,
        "message": "سفارش شما ثبت شد و رفت برای پخت!"
    }

# اجرا روی پورت ۸۰۰۱: uvicorn order_service:app --port 8001

تحلیل فنی: قدرت Async در FastAPI

توی کد بالا یک نکته طلایی وجود داره: await client.get(...). در فریمورک‌ های قدیمی، وقتی برنامه منتظر جواب انبار میمونه، کل سرور "فریز" میشه. اما در FastAPI، سرور میگه: "تا انبار داره جواب میده و میگرده، من میرم کار بقیه مشتری ‌ها رو راه میندازم". این یعنی با منابع کمتر، هزاران درخواست بیشتر رو همزمان مدیریت میکنه. این یعنی قدرت واقعی میکروسرویس!

چرخه حیات یک درخواست

چرخه حیات یک درخواست (پشت پرده چه خبره؟)

بیایید مسیری که طی شد رو مرور کنیم:

  1. درخواست (Request): کاربر درخواست خرید پیتزا (ID: 1) رو به سرویس سفارش (پورت ۸۰۰۱) میفرسته.

  2. استعلام (Internal Call): سرویس سفارش خودش قیمت رو نداره، پس یک درخواست داخلی سریع میزنه به سرویس انبار (پورت ۸۰۰۰).

  3. پاسخ انبار: سرویس انبار میگه: "پیتزا داریم، قیمتش ۱۵۰ تومنه".

  4. پاسخ نهایی (Response): سرویس سفارش مالیات رو حساب میکنه و فاکتور نهایی رو به کاربر نشون میده.

همه این ‌ها در کسری از ثانیه و به لطف معماری Async انجام میشه.

جمع بندی

معماری میکروسرویس با FastAPI طراحی شده تا وقتی پروژه بزرگ شد، زیر بار خودش له نشه.

اگر میخوای دیتابیس انبار رو عوض کنی؟ به سرویس سفارش ربطی نداره.

اگر ترافیک روی سفارش‌ دهی زیاده؟ فقط سرور های همون بخش رو زیاد میکنی.

کد ها تمیز، جداگانه و قابل فهم باقی می مونن.

این تفکیک وظایف باعث میشه که شما به جای درگیر شدن با یک "غول سنگی" بزرگ، با یک تیم چابک و سریع از سرویس‌ ها کار کنید. با FastAPI، کد های شما مدرن می مانند و توسعه لذت‌ بخش ‌تر میشود.