Microservices چیست؟ آموزش کامل معماری میکروسرویس با Python، FastAPI و Docker

Microservices معماری نرم‌افزاری مبتنی بر سرویس‌های مستقل و قابل‌استقرار جداگانه است. در این آموزش، اصول طراحی میکروسرویس و ساخت یک پروژه عملی با Python، FastAPI، Docker Compose و API درواره را یاد می‌گیرید.

Share
Microservices چیست؟ آموزش کامل معماری میکروسرویس با Python، FastAPI و Docker

بسیاری از پروژه‌های نرم‌افزاری با یک Backend ساده شروع می‌شوند. احراز هویت، کاربران، سفارش‌ها، پرداخت، گزارش‌ها، Notification و قابلیت‌های هوش مصنوعی همگی در یک پروژه قرار می‌گیرند و با یک فرایند Deploy منتشر می‌شوند.

این ساختار در مراحل اولیه معمولاً انتخاب خوبی است. توسعه سریع‌تر انجام می‌شود، Debug ساده‌تر است و تیم برای اجرای پروژه به زیرساخت پیچیده‌ای نیاز ندارد.

اما با بزرگ‌شدن محصول ممکن است مشکلاتی ظاهر شوند:

  • تغییر یک بخش به Deploy کل Backend نیاز دارد.
  • خطای یک Module می‌تواند کل برنامه را مختل کند.
  • چند تیم روی یک Codebase بزرگ با هم تداخل پیدا می‌کنند.
  • مقیاس‌کردن یک قابلیت باعث Scale شدن تمام برنامه می‌شود.
  • Releaseها کند و پرریسک می‌شوند.
  • وابستگی میان Moduleها افزایش پیدا می‌کند.
  • تغییر تکنولوژی یک بخش دشوار می‌شود.
  • تست کامل برنامه زمان زیادی می‌گیرد.

معماری Microservices یا میکروسرویس تلاش می‌کند سیستم را به سرویس‌های مستقل و دارای مرز مشخص تقسیم کند. هر سرویس مسئول یک قابلیت کسب‌وکار است، Codebase و چرخه Deploy خود را دارد و معمولاً داده‌هایش را نیز مستقل مدیریت می‌کند.

اما Microservices فقط تقسیم یک پروژه به چند API کوچک نیست. اگر مرز سرویس‌ها اشتباه انتخاب شود یا زیرساخت مناسب وجود نداشته باشد، نتیجه می‌تواند از یک Monolith پیچیده‌تر، کندتر و پرهزینه‌تر باشد.

در این مقاله ابتدا معماری Microservices را دقیق بررسی می‌کنیم، آن را با Monolith و Modular Monolith مقایسه می‌کنیم و سپس یک پروژه عملی با سه سرویس مستقل، Python، FastAPI، Docker Compose و API هوش مصنوعی درواره می‌سازیم.

Microservices چیست؟

Microservices یک سبک معماری نرم‌افزار است که برنامه را به مجموعه‌ای از سرویس‌های کوچک، مستقل و Loosely Coupled تقسیم می‌کند. هر سرویس معمولاً یک قابلیت مشخص کسب‌وکار را پیاده‌سازی می‌کند و می‌تواند مستقل توسعه، تست، Deploy و Scale شود.

برای مثال، یک فروشگاه اینترنتی می‌تواند شامل سرویس‌های زیر باشد:

User Service
Product Service
Order Service
Payment Service
Inventory Service
Notification Service
Search Service
AI Service

هر سرویس API یا Message Contract مشخصی دارد و جزئیات داخلی خود را از سایر سرویس‌ها پنهان می‌کند.

براساس راهنمای معماری Microservices مایکروسافت، میکروسرویس‌ها Componentهای کوچک، مستقل و دارای اتصال ضعیف هستند که هرکدام باید یک قابلیت مشخص را در یک Bounded Context پیاده‌سازی کنند.

ویژگی‌های اصلی Microservice

یک Microservice مناسب معمولاً این ویژگی‌ها را دارد:

  • مسئول یک قابلیت مشخص کسب‌وکار است.
  • مرز داده مشخص دارد.
  • API یا Event Contract واضح دارد.
  • مستقل Deploy می‌شود.
  • مستقل Scale می‌شود.
  • خرابی آن تا حد امکان از سایر سرویس‌ها جداست.
  • پیاده‌سازی داخلی خود را پنهان می‌کند.
  • توسط یک تیم مشخص نگهداری می‌شود.
  • Monitoring و Health Check مستقل دارد.
  • نسخه و چرخه انتشار خود را دارد.

کوچک‌بودن سرویس فقط به تعداد Lineهای Code مربوط نیست. مهم‌تر از اندازه، Cohesion و استقلال قابلیت کسب‌وکار است.

Monolith چیست؟

در معماری Monolith، بیشتر قابلیت‌های برنامه داخل یک واحد اجرایی قرار دارند:

Application
├── Users
├── Products
├── Orders
├── Payments
├── Reports
└── AI Features

این برنامه معمولاً:

  • یک Codebase دارد.
  • یک فرایند Build دارد.
  • یک واحد Deploy دارد.
  • اغلب از یک Database مشترک استفاده می‌کند.
  • همه Moduleها داخل یک Process اجرا می‌شوند.

Monolith لزوماً معماری بدی نیست. برای بسیاری از MVPها، Startupها، پنل‌های مدیریتی و پروژه‌های دارای تیم کوچک، یک Monolith ماژولار بهترین نقطه شروع است.

Modular Monolith چیست؟

Modular Monolith یک برنامه واحد است که داخل آن مرز Moduleها جدی گرفته می‌شود:

Application
├── user_module
├── article_module
├── billing_module
├── notification_module
└── ai_module

همه Moduleها با هم Deploy می‌شوند، اما:

  • وابستگی‌ها کنترل‌شده‌اند.
  • هر Module Interface مشخص دارد.
  • دسترسی مستقیم و نامحدود به اجزای داخلی Moduleها وجود ندارد.
  • منطق Domain از Framework جدا نگه داشته می‌شود.
  • امکان استخراج آینده یک Module به Microservice وجود دارد.

برای بسیاری از پروژه‌ها، Modular Monolith قبل از Microservices انتخاب منطقی‌تری است.

مقایسه Monolith، Modular Monolith و Microservices

معیارMonolithModular MonolithMicroservices
تعداد واحد Deployیکیکچند
پیچیدگی اولیهکممتوسطزیاد
ارتباط داخلیفراخوانی مستقیمInterface داخلیشبکه یا Message Broker
Databaseمعمولاً مشترکمشترک با مرز منطقیترجیحاً مستقل
Scale مستقلدشواردشوارامکان‌پذیر
Deploy مستقلنداردندارددارد
Debugساده‌ترنسبتاً سادهپیچیده‌تر
تراکنشساده‌ترساده‌ترتوزیع‌شده و دشوارتر
نیاز به DevOpsکممتوسطزیاد
مناسب تیم کوچکبلهبلهمعمولاً نه
تحمل خرابیمحدودمحدودبا طراحی مناسب بیشتر
هزینه عملیاتیکمترکمتربیشتر

چه زمانی از Microservices استفاده کنیم؟

Microservices زمانی ارزش بیشتری ایجاد می‌کند که مسئله واقعی برای حل‌کردن داشته باشید.

نشانه‌های مناسب:

  • چند تیم مستقل روی Domainهای متفاوت کار می‌کنند.
  • بخش‌های مختلف سیستم نرخ Scale متفاوتی دارند.
  • Release یک Module نباید به Release کل برنامه وابسته باشد.
  • خرابی یک قابلیت باید از سایر قابلیت‌ها جدا شود.
  • بعضی سرویس‌ها به تکنولوژی یا Storage متفاوت نیاز دارند.
  • حجم ترافیک بالا یا الگوی بار نامتوازن است.
  • Domain پیچیده و دارای مرزهای روشن است.
  • CI/CD، Monitoring و عملیات Production قابل‌اتکا دارید.
  • هزینه زیرساخت توزیع‌شده قابل‌قبول است.

مثال:

یک سرویس تولید تصویر ممکن است منابع و مقیاس متفاوتی از سرویس مدیریت حساب کاربری داشته باشد. جداکردن این دو سرویس می‌تواند منطقی باشد.

چه زمانی Microservices انتخاب مناسبی نیست؟

در شرایط زیر بهتر است ابتدا Monolith یا Modular Monolith را بررسی کنید:

  • محصول هنوز به Product-Market Fit نرسیده است.
  • تیم فنی کوچک است.
  • Domain هنوز مرتب تغییر می‌کند.
  • مرز قابلیت‌ها مشخص نیست.
  • ترافیک محدود است.
  • Deploy برنامه فعلی مشکل جدی ندارد.
  • Monitoring متمرکز ندارید.
  • تیم تجربه سیستم‌های توزیع‌شده ندارد.
  • هزینه نگهداری چند سرویس قابل‌توجیه نیست.
  • فقط برای استفاده از Docker یا Kubernetes به Microservices فکر می‌کنید.

Microservices نباید هدف پروژه باشد. هدف باید حل مسئله کسب‌وکار و کاهش محدودیت معماری موجود باشد.

تفاوت SOA و Microservices

Service-Oriented Architecture یا SOA نیز سیستم را به سرویس‌ها تقسیم می‌کند، اما معمولاً در مقیاس سازمانی و با Integration مرکزی‌تر استفاده می‌شود.

ویژگیSOAMicroservices
اندازه سرویسمعمولاً بزرگ‌ترمعمولاً کوچک‌تر و متمرکز
Integrationگاهی ESB مرکزیAPI و Eventهای سبک‌تر
Deployممکن است وابسته باشدترجیحاً مستقل
Governanceمتمرکزترغیرمتمرکزتر
مالکیت دادهممکن است مشترک باشدترجیحاً مستقل
تمرکز اصلیIntegration سازمانیاستقلال تیم و قابلیت

این مرز همیشه قطعی نیست و معماری واقعی ممکن است ویژگی‌هایی از هر دو داشته باشد.

Bounded Context چیست؟

یکی از مهم‌ترین مفاهیم طراحی Microservices، Bounded Context در Domain-Driven Design است.

Bounded Context محدوده‌ای است که در آن یک مدل و اصطلاحات Domain معنی مشخصی دارند.

برای مثال واژه «کاربر» می‌تواند در بخش‌های مختلف معنی متفاوتی داشته باشد:

  • در Identity Service: حساب، رمز، Session و دسترسی
  • در Billing Service: مشتری، کیف پول و صورتحساب
  • در Support Service: تیکت و سابقه پشتیبانی
  • در Analytics Service: Segment و رفتار استفاده

قرار دادن تمام این مفاهیم در یک مدل مشترک بزرگ، Coupling ایجاد می‌کند. هر سرویس باید فقط بخشی از اطلاعات را که برای Domain خودش لازم است مدیریت کند.

چگونه مرز Microservice را انتخاب کنیم؟

مرز سرویس نباید فقط براساس Tableهای Database یا نام Entityها انتخاب شود.

روش مناسب‌تر:

  1. قابلیت‌های کسب‌وکار را شناسایی کنید.
  2. Workflowهای اصلی را ترسیم کنید.
  3. داده‌ها و قوانین هر Workflow را مشخص کنید.
  4. تغییراتی را که معمولاً با هم رخ می‌دهند پیدا کنید.
  5. مالکیت تیم را بررسی کنید.
  6. نیاز Scale هر بخش را بسنجید.
  7. وابستگی‌های Sync را اندازه‌گیری کنید.
  8. Bounded Contextها را مشخص کنید.
  9. قبل از جداسازی فیزیکی، مرزها را در Modular Monolith آزمایش کنید.

نمونه مرزبندی نامناسب:

Name Service
Address Service
Phone Service
Email Service

این سرویس‌ها بیش‌ازحد ریز هستند و برای انجام یک عملیات ساده به تعداد زیادی Request نیاز دارند.

نمونه منطقی‌تر:

Customer Profile Service

Microservice چقدر باید کوچک باشد؟

عدد ثابتی برای تعداد Endpoint، Table یا Line Code وجود ندارد.

یک سرویس زمانی بیش‌ازحد بزرگ است که:

  • چند قابلیت مستقل کسب‌وکار را مدیریت می‌کند.
  • چند تیم مرتب روی آن تداخل دارند.
  • بخش‌هایش نیاز Scale متفاوت دارند.
  • Release یک قابلیت به سایر قابلیت‌ها وابسته است.

یک سرویس زمانی بیش‌ازحد کوچک است که:

  • به‌تنهایی ارزش کسب‌وکاری ندارد.
  • برای هر Request باید چند سرویس دیگر را فراخوانی کند.
  • مدل و داده مستقلی ندارد.
  • Deployment مستقل آن مزیتی ایجاد نمی‌کند.
  • بیشتر وقت تیم صرف مدیریت ارتباط سرویس‌ها می‌شود.

Database per Service

یکی از اصول متداول Microservices این است که هر سرویس مالک داده‌های خودش باشد:

User Service → User Database
Order Service → Order Database
AI Service → AI Job Database

سرویس دیگر نباید مستقیماً Tableهای داخلی سرویس را Query کند. ارتباط باید از طریق API یا Event انجام شود.

این اصل به معنی آن نیست که هر سرویس الزاماً به یک Server فیزیکی Database جدا نیاز دارد. ممکن است چند Database منطقی روی یک Cluster قرار داشته باشند، اما مالکیت Schema و دسترسی باید جدا باشد.

چرا Database مشترک مشکل ایجاد می‌کند؟

اگر همه سرویس‌ها مستقیماً از یک Schema مشترک استفاده کنند:

  • تغییر Schema چند سرویس را هم‌زمان تحت‌تأثیر قرار می‌دهد.
  • سرویس‌ها جزئیات داخلی یکدیگر را می‌شناسند.
  • Deploy مستقل دشوار می‌شود.
  • مالکیت داده نامشخص می‌شود.
  • یک Query سنگین می‌تواند سایر سرویس‌ها را مختل کند.
  • استخراج یا تغییر Storage یک سرویس دشوار می‌شود.

آیا هر سرویس می‌تواند Database متفاوت داشته باشد؟

بله. این مفهوم Polyglot Persistence نامیده می‌شود:

Identity Service → PostgreSQL
Cache Service → Redis
Search Service → Elasticsearch
Document Service → MongoDB
Analytics Service → ClickHouse

اما استفاده از تکنولوژی متفاوت برای هر سرویس هزینه نگهداری زیادی دارد. فقط زمانی Storage متفاوت انتخاب کنید که نیاز واقعی وجود داشته باشد.

ارتباط Synchronous بین Microservices

در ارتباط Synchronous، سرویس A درخواست را ارسال و منتظر پاسخ سرویس B می‌ماند.

ابزارهای رایج:

  • REST API
  • gRPC
  • GraphQL در لایه Composition

مثال:

API Gateway
→ Article Service
→ AI Service
→ Response

مزایا:

  • پیاده‌سازی و درک ساده‌تر
  • پاسخ فوری
  • Debug راحت‌تر در Workflowهای کوتاه
  • مناسب Query و عملیات تعاملی

معایب:

  • افزایش Latency در زنجیره
  • وابستگی زمانی سرویس‌ها
  • انتشار خطا به سرویس بالادستی
  • احتمال Cascading Failure
  • نیاز به Timeout و Circuit Breaker

ارتباط Asynchronous

در ارتباط Asynchronous، Producer یک Event یا Message منتشر می‌کند و منتظر پایان Consumer نمی‌ماند.

ابزارهای رایج:

  • RabbitMQ
  • Apache Kafka
  • Redis Streams
  • سرویس‌های مدیریت‌شده Queue

مثال:

Article Service
→ article.created Event
→ AI Worker
→ Summary Created Event

مزایا:

  • Coupling زمانی کمتر
  • تحمل بهتر اختلال موقت
  • مناسب Jobهای سنگین
  • قابلیت Scale مستقل Consumer
  • امکان Retry و Queue کردن بار

معایب:

  • نتیجه فوری نیست.
  • Debug پیچیده‌تر می‌شود.
  • Eventual Consistency ایجاد می‌شود.
  • Idempotency ضروری است.
  • مدیریت Retry و DLQ لازم است.

چه زمانی Sync و چه زمانی Async؟

سناریوروش پیشنهادی
دریافت اطلاعات پروفایلSync
بررسی موجودی لحظه‌ایSync
ارسال ایمیلAsync
تولید گزارش سنگینAsync
پردازش متن با مدلبسته به UX
ایجاد ThumbnailAsync
دریافت وضعیت JobSync
انتشار رویداد سفارشAsync
Validation فوریSync
AnalyticsAsync

معماری واقعی معمولاً ترکیبی از هر دو روش است.

API Gateway در معماری Microservices

API Gateway ورودی مرکزی Clientهاست:

Client
→ API Gateway
├── Identity Service
├── Article Service
├── Billing Service
└── AI Service

وظایف احتمالی Gateway:

  • Routing
  • Authentication
  • Rate Limiting
  • Request ID
  • Response Aggregation
  • Protocol Translation
  • محدودکردن دسترسی مستقیم به سرویس‌های داخلی

Gateway نباید به یک Monolith جدید تبدیل شود. منطق اصلی کسب‌وکار باید در سرویس‌های Domain باقی بماند.

Service Discovery

در محیط Docker Compose، سرویس‌ها می‌توانند با نام Service یکدیگر را پیدا کنند:

http://article-service:8000
http://ai-service:8000

در Kubernetes معمولاً DNS داخلی Service Discovery را انجام می‌دهد.

آدرس IP یک Container یا Pod نباید در Code هاردکد شود؛ زیرا Instanceها ممکن است جایگزین شوند.

داده و تراکنش در Microservices

در Monolith می‌توان چند Table را در یک Transaction تغییر داد. در Microservices، داده‌ها ممکن است در Databaseهای مختلف باشند.

فرض کنید ثبت سفارش شامل این مراحل است:

  1. ایجاد سفارش
  2. کاهش موجودی
  3. ثبت پرداخت
  4. ارسال Notification

اجرای یک Transaction سنتی روی همه سرویس‌ها معمولاً دشوار یا نامناسب است.

روش‌های رایج:

  • Saga Pattern
  • Transactional Outbox
  • Eventual Consistency
  • Compensating Action
  • Idempotent Consumer

Saga Pattern

Saga یک Workflow را به چند Transaction محلی تقسیم می‌کند.

مثال:

Order Created
→ Inventory Reserved
→ Payment Completed
→ Order Confirmed

اگر پرداخت شکست بخورد:

Payment Failed
→ Inventory Released
→ Order Cancelled

عملیات جبرانی دقیقاً Rollback فنی Transaction قبلی نیست؛ بلکه یک عملیات کسب‌وکاری برای جبران نتیجه است.

Transactional Outbox

Outbox Pattern برای هماهنگی تغییر Database و انتشار Event استفاده می‌شود.

در یک Transaction محلی:

ثبت داده اصلی
+
ثبت Event در جدول Outbox

Worker جداگانه Eventهای Outbox را منتشر و پس از موفقیت علامت‌گذاری می‌کند.

این روش احتمال گم‌شدن Event میان Commit دیتابیس و انتشار Message را کاهش می‌دهد.

مشکلات سیستم‌های توزیع‌شده

در Microservices باید بپذیرید که شبکه قابل‌اعتماد نیست.

ممکن است:

  • سرویس مقصد Down باشد.
  • Request Timeout شود.
  • پاسخ دیر برسد.
  • Request انجام شود اما پاسخ گم شود.
  • Message دوباره تحویل داده شود.
  • سرویس‌ها نسخه متفاوت داشته باشند.
  • Clock سرورها دقیقاً هماهنگ نباشد.
  • داده‌ها موقتاً ناسازگار باشند.

بنابراین کدنویسی Microservices فقط ساخت چند Endpoint نیست.

Timeout

هر فراخوانی شبکه باید Timeout مشخص داشته باشد:

timeout = httpx.Timeout(
    connect=3.0,
    read=60.0,
    write=10.0,
    pool=5.0,
)

نبود Timeout می‌تواند Connectionها را برای مدت نامحدود اشغال کند.

Retry

Retry فقط برای خطاهای موقت و عملیات قابل‌تکرار مناسب است.

نمونه خطاهای قابل Retry:

  • Timeout
  • خطای موقت 502 یا 503
  • Rate Limit با زمان انتظار مشخص
  • قطع کوتاه شبکه

Retry برای همه خطاها مناسب نیست. درخواست نامعتبر با Retry درست نمی‌شود.

Retry باید:

  • محدود باشد.
  • Backoff داشته باشد.
  • Jitter داشته باشد.
  • با Idempotency هماهنگ باشد.
  • Metric و Log داشته باشد.

Circuit Breaker

Circuit Breaker پس از افزایش خطا، برای مدتی فراخوانی سرویس ناسالم را متوقف می‌کند.

حالت‌های مفهومی:

Closed → درخواست‌ها عبور می‌کنند
Open → درخواست‌ها موقتاً متوقف می‌شوند
Half-Open → چند درخواست آزمایشی ارسال می‌شود

Circuit Breaker از فشار بیشتر روی سرویس مختل جلوگیری می‌کند.

Bulkhead

Bulkhead منابع سرویس‌ها یا عملیات مختلف را جدا می‌کند.

برای مثال، اگر همه Connectionهای HTTP توسط تولید گزارش مصرف شوند، درخواست‌های ساده نیز مختل می‌شوند. با Pool یا محدودیت Concurrency جداگانه می‌توان خرابی را محدود کرد.

Idempotency

درخواست Idempotent با اجرای چندباره اثر نهایی ناخواسته ایجاد نمی‌کند.

برای عملیات مهم از Idempotency Key استفاده کنید:

Idempotency-Key: 7bc69fdd-daf2-4a8f-9658-16cd92419c16

سرویس مقصد نتیجه درخواست قبلی را برای همان Key بازیابی می‌کند و عملیات را دوباره انجام نمی‌دهد.

Observability در Microservices

در Monolith معمولاً یک Log برای دنبال‌کردن Request کافی است. در Microservices یک Request ممکن است از چند سرویس عبور کند.

سه ستون اصلی Observability:

  • Logs
  • Metrics
  • Traces

Correlation ID و Trace ID

هر Request باید یک شناسه قابل‌ردیابی داشته باشد:

X-Request-ID: 2b3f3750-f50e-414d-a5c5-f21998ea7ae3

Gateway این شناسه را ایجاد و به سرویس‌های بعدی ارسال می‌کند.

Log نمونه:

{
  "service": "ai-service",
  "request_id": "2b3f3750-f50e-414d-a5c5-f21998ea7ae3",
  "operation": "summarize",
  "duration_ms": 2480,
  "status": "success"
}

Health Check

هر سرویس بهتر است حداقل دو مفهوم سلامت داشته باشد:

  • Liveness: Process زنده است؟
  • Readiness: سرویس آماده دریافت ترافیک است؟

Endpoint ساده:

GET /health

در پروژه واقعی Readiness باید وابستگی‌های ضروری مانند Database را نیز بررسی کند؛ اما نباید هر Health Check باعث اجرای عملیات سنگین شود.

پروژه عملی: ساخت Microservices با FastAPI و Docker Compose

در این پروژه سه سرویس می‌سازیم:

Article Service

وظایف:

  • ایجاد مقاله
  • ذخیره مقاله در Database مستقل
  • بازیابی مقاله

AI Service

وظایف:

  • دریافت متن
  • اتصال به API درواره
  • تولید خلاصه

API Gateway

وظایف:

  • ورودی Client
  • ایجاد Correlation ID
  • فراخوانی Article Service
  • فراخوانی AI Service
  • مدیریت Timeout و خطا
  • برگرداندن پاسخ نهایی

معماری پروژه:

Client
→ API Gateway
→ Article Service
→ SQLite اختصاصی Article Service

API Gateway
→ AI Service
→ API درواره

این پروژه برای آموزش از SQLite استفاده می‌کند. در Production می‌توانید PostgreSQL اختصاصی Article Service را جایگزین کنید.

ساختار پروژه

fastapi-microservices/
├── .env
├── docker-compose.yml
├── gateway/
│   ├── Dockerfile
│   ├── requirements.txt
│   └── main.py
├── article-service/
│   ├── Dockerfile
│   ├── requirements.txt
│   └── main.py
└── ai-service/
    ├── Dockerfile
    ├── requirements.txt
    └── main.py

تنظیم متغیرهای محیطی

فایل .env:

DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL_ID=YOUR_MODEL_ID

فایل .env را به .gitignore اضافه کنید:

.env
__pycache__/
*.pyc

ساخت Article Service

فایل article-service/requirements.txt:

fastapi>=0.115,<1
uvicorn[standard]>=0.34,<1
aiosqlite>=0.21,<1
pydantic>=2.10,<3

فایل article-service/main.py:

import os
from contextlib import asynccontextmanager
from datetime import datetime, timezone
from uuid import uuid4

import aiosqlite
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field

DATABASE_PATH = os.getenv(
    "DATABASE_PATH",
    "/data/articles.db",
)


class ArticleCreate(BaseModel):
    title: str = Field(
        min_length=3,
        max_length=300,
    )

    content: str = Field(
        min_length=10,
        max_length=100_000,
    )


class ArticleResponse(BaseModel):
    id: str
    title: str
    content: str
    created_at: str


async def init_database() -> None:
    directory = os.path.dirname(DATABASE_PATH)

    if directory:
        os.makedirs(
            directory,
            exist_ok=True,
        )

    async with aiosqlite.connect(
        DATABASE_PATH
    ) as database:
        await database.execute(
            """
            CREATE TABLE IF NOT EXISTS articles (
                id TEXT PRIMARY KEY,
                title TEXT NOT NULL,
                content TEXT NOT NULL,
                created_at TEXT NOT NULL
            )
            """
        )

        await database.commit()


@asynccontextmanager
async def lifespan(app: FastAPI):
    await init_database()
    yield


app = FastAPI(
    title="Article Service",
    version="1.0.0",
    lifespan=lifespan,
)


@app.get("/health")
async def health():
    try:
        async with aiosqlite.connect(
            DATABASE_PATH
        ) as database:
            await database.execute(
                "SELECT 1"
            )

        return {
            "status": "ready",
            "service": "article-service",
        }

    except Exception as exc:
        raise HTTPException(
            status_code=503,
            detail="Database is unavailable",
        ) from exc


@app.post(
    "/articles",
    response_model=ArticleResponse,
    status_code=201,
)
async def create_article(
    payload: ArticleCreate,
):
    article_id = str(uuid4())
    created_at = datetime.now(
        timezone.utc
    ).isoformat()

    async with aiosqlite.connect(
        DATABASE_PATH
    ) as database:
        await database.execute(
            """
            INSERT INTO articles (
                id,
                title,
                content,
                created_at
            )
            VALUES (?, ?, ?, ?)
            """,
            (
                article_id,
                payload.title,
                payload.content,
                created_at,
            ),
        )

        await database.commit()

    return ArticleResponse(
        id=article_id,
        title=payload.title,
        content=payload.content,
        created_at=created_at,
    )


@app.get(
    "/articles/{article_id}",
    response_model=ArticleResponse,
)
async def get_article(
    article_id: str,
):
    async with aiosqlite.connect(
        DATABASE_PATH
    ) as database:
        database.row_factory = (
            aiosqlite.Row
        )

        cursor = await database.execute(
            """
            SELECT
                id,
                title,
                content,
                created_at
            FROM articles
            WHERE id = ?
            """,
            (article_id,),
        )

        row = await cursor.fetchone()

    if row is None:
        raise HTTPException(
            status_code=404,
            detail="Article not found",
        )

    return ArticleResponse(
        id=row["id"],
        title=row["title"],
        content=row["content"],
        created_at=row["created_at"],
    )

Article Service تنها سرویس دارای دسترسی مستقیم به Database مقاله‌هاست. Gateway و AI Service نباید فایل SQLite یا Tableهای آن را مستقیماً بخوانند.

Dockerfile مربوط به Article Service

فایل article-service/Dockerfile:

FROM python:3.13-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

COPY requirements.txt .

RUN pip install \
    --no-cache-dir \
    -r requirements.txt

COPY main.py .

CMD [
    "uvicorn",
    "main:app",
    "--host",
    "0.0.0.0",
    "--port",
    "8000"
]

ساخت AI Service

فایل ai-service/requirements.txt:

fastapi>=0.115,<1
uvicorn[standard]>=0.34,<1
openai>=1.100,<2
pydantic>=2.10,<3

فایل ai-service/main.py:

import os
from contextlib import asynccontextmanager

from fastapi import FastAPI, HTTPException
from openai import AsyncOpenAI
from pydantic import BaseModel, Field

DARVAREH_API_KEY = os.getenv(
    "DARVAREH_API_KEY",
    "",
)

DARVAREH_MODEL_ID = os.getenv(
    "DARVAREH_MODEL_ID",
    "YOUR_MODEL_ID",
)

DARVAREH_BASE_URL = (
    "https://api.darvareh.ir/v1"
)


class SummaryRequest(BaseModel):
    text: str = Field(
        min_length=10,
        max_length=100_000,
    )


class SummaryResponse(BaseModel):
    summary: str
    model: str


@asynccontextmanager
async def lifespan(app: FastAPI):
    if not DARVAREH_API_KEY:
        raise RuntimeError(
            "DARVAREH_API_KEY is missing"
        )

    client = AsyncOpenAI(
        api_key=DARVAREH_API_KEY,
        base_url=DARVAREH_BASE_URL,
        timeout=60,
        max_retries=2,
    )

    app.state.ai_client = client

    try:
        yield
    finally:
        await client.close()


app = FastAPI(
    title="AI Service",
    version="1.0.0",
    lifespan=lifespan,
)


@app.get("/health")
async def health():
    return {
        "status": "ready",
        "service": "ai-service",
    }


@app.post(
    "/summaries",
    response_model=SummaryResponse,
)
async def create_summary(
    payload: SummaryRequest,
):
    try:
        response = (
            await app.state.ai_client
            .chat.completions.create(
                model=DARVAREH_MODEL_ID,
                temperature=0.2,
                max_tokens=800,
                messages=[
                    {
                        "role": "system",
                        "content": (
                            "متن ورودی را دقیق، روان "
                            "و بدون افزودن اطلاعات جدید "
                            "خلاصه کن."
                        ),
                    },
                    {
                        "role": "user",
                        "content": payload.text,
                    },
                ],
            )
        )

        summary = response.choices[
            0
        ].message.content

        if not summary:
            raise ValueError(
                "Model returned empty content"
            )

        return SummaryResponse(
            summary=summary,
            model=DARVAREH_MODEL_ID,
        )

    except Exception as exc:
        raise HTTPException(
            status_code=502,
            detail=(
                "AI provider request failed"
            ),
        ) from exc

AI Service تنها سرویس دارای دسترسی به کلید API درواره است. Article Service و Gateway نیازی به مشاهده کلید ندارند.

آدرس پایه API:

https://api.darvareh.ir/v1

Endpoint فراخوانی‌شده توسط SDK:

https://api.darvareh.ir/v1/chat/completions

برای دریافت کلید API و مشاهده مدل‌های قابل‌استفاده می‌توانید به درواره مراجعه کنید. شناسه مدل‌ها و قیمت به‌روز آن‌ها در صفحه مدل‌های درواره قرار دارد.

Dockerfile مربوط به AI Service

فایل ai-service/Dockerfile:

FROM python:3.13-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

COPY requirements.txt .

RUN pip install \
    --no-cache-dir \
    -r requirements.txt

COPY main.py .

CMD [
    "uvicorn",
    "main:app",
    "--host",
    "0.0.0.0",
    "--port",
    "8000"
]

ساخت API Gateway

فایل gateway/requirements.txt:

fastapi>=0.115,<1
uvicorn[standard]>=0.34,<1
httpx>=0.28,<1
pydantic>=2.10,<3

فایل gateway/main.py:

import os
from contextlib import asynccontextmanager
from uuid import uuid4

import httpx
from fastapi import (
    FastAPI,
    Header,
    HTTPException,
    Request,
    Response,
)
from pydantic import BaseModel, Field

ARTICLE_SERVICE_URL = os.getenv(
    "ARTICLE_SERVICE_URL",
    "http://article-service:8000",
)

AI_SERVICE_URL = os.getenv(
    "AI_SERVICE_URL",
    "http://ai-service:8000",
)


class ArticleCreate(BaseModel):
    title: str = Field(
        min_length=3,
        max_length=300,
    )

    content: str = Field(
        min_length=10,
        max_length=100_000,
    )


@asynccontextmanager
async def lifespan(app: FastAPI):
    timeout = httpx.Timeout(
        connect=3.0,
        read=60.0,
        write=10.0,
        pool=5.0,
    )

    limits = httpx.Limits(
        max_connections=100,
        max_keepalive_connections=20,
    )

    client = httpx.AsyncClient(
        timeout=timeout,
        limits=limits,
    )

    app.state.http_client = client

    try:
        yield
    finally:
        await client.aclose()


app = FastAPI(
    title="Microservices API Gateway",
    version="1.0.0",
    lifespan=lifespan,
)


def get_request_id(
    incoming_request_id: str | None,
) -> str:
    return (
        incoming_request_id
        or str(uuid4())
    )


@app.get("/health")
async def health():
    return {
        "status": "ready",
        "service": "api-gateway",
    }


@app.post(
    "/articles",
    status_code=201,
)
async def create_article(
    payload: ArticleCreate,
    request: Request,
    response: Response,
    x_request_id: str | None = Header(
        default=None,
    ),
):
    request_id = get_request_id(
        x_request_id
    )

    response.headers[
        "X-Request-ID"
    ] = request_id

    try:
        upstream = (
            await request.app.state.http_client.post(
                f"{ARTICLE_SERVICE_URL}/articles",
                json=payload.model_dump(),
                headers={
                    "X-Request-ID": request_id,
                },
                timeout=10.0,
            )
        )

    except httpx.RequestError as exc:
        raise HTTPException(
            status_code=503,
            detail=(
                "Article service is unavailable"
            ),
        ) from exc

    if upstream.status_code == 422:
        raise HTTPException(
            status_code=422,
            detail=upstream.json(),
        )

    if upstream.status_code >= 400:
        raise HTTPException(
            status_code=502,
            detail=(
                "Article service returned "
                "an unexpected response"
            ),
        )

    return upstream.json()


@app.get(
    "/articles/{article_id}/summary",
)
async def summarize_article(
    article_id: str,
    request: Request,
    response: Response,
    x_request_id: str | None = Header(
        default=None,
    ),
):
    request_id = get_request_id(
        x_request_id
    )

    response.headers[
        "X-Request-ID"
    ] = request_id

    client: httpx.AsyncClient = (
        request.app.state.http_client
    )

    headers = {
        "X-Request-ID": request_id,
    }

    try:
        article_response = await client.get(
            (
                f"{ARTICLE_SERVICE_URL}"
                f"/articles/{article_id}"
            ),
            headers=headers,
            timeout=5.0,
        )

    except httpx.RequestError as exc:
        raise HTTPException(
            status_code=503,
            detail=(
                "Article service is unavailable"
            ),
        ) from exc

    if article_response.status_code == 404:
        raise HTTPException(
            status_code=404,
            detail="Article not found",
        )

    if article_response.status_code >= 400:
        raise HTTPException(
            status_code=502,
            detail=(
                "Article service returned "
                "an unexpected response"
            ),
        )

    article = article_response.json()

    try:
        summary_response = await client.post(
            f"{AI_SERVICE_URL}/summaries",
            json={
                "text": article["content"],
            },
            headers=headers,
            timeout=60.0,
        )

    except httpx.RequestError as exc:
        raise HTTPException(
            status_code=503,
            detail="AI service is unavailable",
        ) from exc

    if summary_response.status_code >= 400:
        raise HTTPException(
            status_code=502,
            detail=(
                "AI service could not "
                "generate the summary"
            ),
        )

    summary_data = summary_response.json()

    return {
        "request_id": request_id,
        "article": {
            "id": article["id"],
            "title": article["title"],
        },
        "summary": summary_data["summary"],
        "model": summary_data["model"],
    }

در این Gateway:

  • Client فقط Gateway را می‌شناسد.
  • آدرس داخلی سرویس‌ها در Environment Variable قرار دارد.
  • Connection Pool برای HTTP Client استفاده می‌شود.
  • برای فراخوانی سرویس‌ها Timeout داریم.
  • خطای داخلی بدون نمایش جزئیات حساس به Client تبدیل می‌شود.
  • X-Request-ID برای ردیابی زنجیره Request استفاده می‌شود.

Dockerfile مربوط به Gateway

فایل gateway/Dockerfile:

FROM python:3.13-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

COPY requirements.txt .

RUN pip install \
    --no-cache-dir \
    -r requirements.txt

COPY main.py .

CMD [
    "uvicorn",
    "main:app",
    "--host",
    "0.0.0.0",
    "--port",
    "8000"
]

ساخت Docker Compose

فایل docker-compose.yml:

services:
  gateway:
    build:
      context: ./gateway
    container_name: microservices-gateway
    environment:
      ARTICLE_SERVICE_URL: http://article-service:8000
      AI_SERVICE_URL: http://ai-service:8000
    ports:
      - "127.0.0.1:8080:8000"
    depends_on:
      article-service:
        condition: service_healthy
      ai-service:
        condition: service_healthy
    restart: unless-stopped

  article-service:
    build:
      context: ./article-service
    container_name: microservices-article
    environment:
      DATABASE_PATH: /data/articles.db
    volumes:
      - article_data:/data
    healthcheck:
      test:
        [
          "CMD",
          "python",
          "-c",
          "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health')"
        ]
      interval: 10s
      timeout: 5s
      retries: 10
    restart: unless-stopped

  ai-service:
    build:
      context: ./ai-service
    container_name: microservices-ai
    environment:
      DARVAREH_API_KEY: ${DARVAREH_API_KEY}
      DARVAREH_MODEL_ID: ${DARVAREH_MODEL_ID}
    healthcheck:
      test:
        [
          "CMD",
          "python",
          "-c",
          "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health')"
        ]
      interval: 10s
      timeout: 5s
      retries: 10
    restart: unless-stopped

volumes:
  article_data:

فقط Gateway روی Host منتشر شده است:

127.0.0.1:8080

Article Service و AI Service فقط از شبکه داخلی Docker Compose قابل دسترسی‌اند.

اجرای پروژه

ساخت و اجرای سرویس‌ها:

docker compose up \
  --build \
  -d

مشاهده وضعیت:

docker compose ps

مشاهده Log همه سرویس‌ها:

docker compose logs -f

مشاهده Log یک سرویس:

docker compose logs \
  -f \
  ai-service

مستندات Gateway:

http://127.0.0.1:8080/docs

ایجاد مقاله

curl -X POST \
  "http://127.0.0.1:8080/articles" \
  -H "Content-Type: application/json" \
  -H "X-Request-ID: test-request-001" \
  -d '{
    "title": "کاربرد هوش مصنوعی در توسعه نرم‌افزار",
    "content": "هوش مصنوعی می‌تواند در توضیح کد، تولید تست، مستندسازی، تحلیل خطا و بازنویسی بخش‌های مشخص نرم‌افزار به توسعه‌دهندگان کمک کند. بااین‌حال، خروجی مدل باید بررسی و آزمایش شود و نباید بدون بازبینی وارد محیط Production شود."
  }'

پاسخ:

{
  "id": "ARTICLE_ID",
  "title": "کاربرد هوش مصنوعی در توسعه نرم‌افزار",
  "content": "هوش مصنوعی می‌تواند...",
  "created_at": "2026-08-09T10:00:00+00:00"
}

مقدار id را برای مرحله بعد نگه دارید.

تولید خلاصه مقاله

curl \
  "http://127.0.0.1:8080/articles/ARTICLE_ID/summary" \
  -H "X-Request-ID: test-request-002"

جریان داخلی:

Client
→ Gateway
→ Article Service
→ Gateway
→ AI Service
→ API درواره
→ AI Service
→ Gateway
→ Client

پاسخ نمونه:

{
  "request_id": "test-request-002",
  "article": {
    "id": "ARTICLE_ID",
    "title": "کاربرد هوش مصنوعی در توسعه نرم‌افزار"
  },
  "summary": "هوش مصنوعی می‌تواند به توسعه‌دهندگان در تحلیل، تست و مستندسازی کد کمک کند، اما خروجی آن باید پیش از استفاده در Production بررسی شود.",
  "model": "YOUR_MODEL_ID"
}

چرا AI Service را جدا کردیم؟

جداکردن AI Service می‌تواند این مزایا را داشته باشد:

  • کلید API فقط در یک سرویس نگهداری می‌شود.
  • انتخاب مدل متمرکز مدیریت می‌شود.
  • Rate Limit در یک نقطه اعمال می‌شود.
  • Retry و Timeout یکپارچه می‌شود.
  • چند سرویس داخلی می‌توانند از AI Service استفاده کنند.
  • Usage و هزینه متمرکز اندازه‌گیری می‌شود.
  • تغییر مدل به تغییر همه سرویس‌ها نیاز ندارد.
  • AI Service می‌تواند مستقل Scale شود.

اما اگر فقط یک قابلیت کوچک هوش مصنوعی دارید، ایجاد Microservice مستقل ممکن است پیچیدگی غیرضروری باشد. در آن حالت یک Module مشخص داخل Modular Monolith می‌تواند کافی باشد.

اضافه‌کردن Job غیرهم‌زمان

در پروژه فعلی Gateway تا دریافت پاسخ AI Service منتظر می‌ماند. برای متن کوتاه و UX تعاملی، این روش قابل‌قبول است.

برای فایل‌های بزرگ یا Jobهای طولانی بهتر است معماری Async شود:

Gateway
→ Job Service
→ Message Broker
→ AI Worker
→ Result Database

Client وضعیت را بررسی می‌کند:

POST /jobs
GET /jobs/{job_id}

یا پس از تکمیل Job یک Webhook دریافت می‌کند.

API Versioning

تغییر ناسازگار API می‌تواند Consumerهای دیگر را خراب کند.

روش متداول:

/v1/articles
/v2/articles

قواعد مناسب:

  • Field جدید را تا حد امکان Optional اضافه کنید.
  • Field قبلی را ناگهانی حذف نکنید.
  • رفتار یک Endpoint را بدون Version تغییر ندهید.
  • زمان Deprecation مشخص کنید.
  • Contract Test داشته باشید.
  • Clientهای وابسته را شناسایی کنید.

Contract Testing

در Microservices فقط Unit Test کافی نیست. سرویس‌ها باید روی شکل Request و Response توافق داشته باشند.

نمونه Contract:

{
  "id": "string",
  "title": "string",
  "content": "string",
  "created_at": "ISO-8601 string"
}

Contract Test بررسی می‌کند Provider تغییری ایجاد نکرده باشد که Consumer را خراب کند.

تست Microservices

Unit Test

منطق هر سرویس را مستقل تست کنید:

  • Validation
  • Domain Rules
  • تبدیل داده
  • مدیریت خطا
  • ساخت Prompt
  • Idempotency

Integration Test

ارتباط واقعی با Database یا Message Broker آزمایشی را بررسی کنید.

Contract Test

سازگاری API میان Provider و Consumer را بسنجید.

End-to-End Test

مسیر اصلی کاربر را از Gateway تا سرویس‌های داخلی اجرا کنید.

Failure Test

این خطاها را آزمایش کنید:

  • Article Service خاموش است.
  • AI Service خاموش است.
  • API درواره Timeout می‌شود.
  • مدل نامعتبر است.
  • پاسخ AI خالی است.
  • Database در دسترس نیست.
  • Request تکراری است.
  • شبکه کند است.
  • یک سرویس نسخه قدیمی دارد.

Deployment مستقل

هدف مهم Microservices استقلال Deploy است. اگر همه سرویس‌ها همیشه باید هم‌زمان Deploy شوند، احتمالاً Coupling زیادی وجود دارد.

هر سرویس بهتر است:

  • Repository یا مسیر Build مشخص داشته باشد.
  • Docker Image نسخه‌بندی‌شده بسازد.
  • تست مستقل داشته باشد.
  • Migration مستقل داشته باشد.
  • Health Check داشته باشد.
  • Rollback مستقل داشته باشد.
  • Release Notes داشته باشد.

نمونه Tag:

article-service:1.4.2
ai-service:2.1.0
gateway:1.8.3

از Tag مبهم latest برای Deploy قابل‌تکرار Production استفاده نکنید.

Kubernetes برای Microservices ضروری است؟

خیر. Microservices یک سبک معماری است و Kubernetes یک پلتفرم Orchestration محسوب می‌شود.

برای تعداد محدود سرویس می‌توان از این گزینه‌ها استفاده کرد:

  • Docker Compose روی یک Server
  • Container Service مدیریت‌شده
  • Platform as a Service
  • Kubernetes
  • Nomad

Kubernetes زمانی ارزش بیشتری دارد که به Scheduling، Auto Scaling، Self-Healing، Service Discovery و مدیریت تعداد زیادی Workload نیاز داشته باشید.

شروع مستقیم با Kubernetes برای یک پروژه کوچک می‌تواند هزینه یادگیری و عملیات را افزایش دهد.

اشتباه‌های رایج در Microservices

تقسیم سرویس‌ها براساس Table

ساخت یک سرویس برای هر Table معمولاً باعث سرویس‌های بسیار ریز و Chatty Communication می‌شود.

مرز را براساس قابلیت کسب‌وکار انتخاب کنید، نه صرفاً Entityهای Database.

Database مشترک بدون مرز

اگر همه سرویس‌ها آزادانه Tableهای یکدیگر را تغییر دهند، استقلال واقعی وجود ندارد.

زنجیره طولانی Requestهای Sync

این زنجیره شکننده است:

Gateway
→ Service A
→ Service B
→ Service C
→ Service D

Latency و احتمال شکست در هر مرحله افزایش پیدا می‌کند. Workflow را بازطراحی یا بعضی بخش‌ها را Async کنید.

Shared Library بزرگ

کتابخانه مشترکی که مدل Domain و منطق همه سرویس‌ها را نگه دارد، Coupling ایجاد می‌کند. Library مشترک را به موارد پایدار و فنی محدود کنید.

Deploy هماهنگ همه سرویس‌ها

اگر هر تغییر به Release هم‌زمان همه سرویس‌ها نیاز دارد، Contractها یا مرزها به‌اندازه کافی مستقل نیستند.

نبود Observability

بدون Log ساختاریافته، Metrics و Trace، Debug سیستم توزیع‌شده بسیار دشوار می‌شود.

Retry بدون محدودیت

Retry نامحدود می‌تواند اختلال کوچک را به فشار گسترده روی سیستم تبدیل کند.

استفاده از Microservices برای تیم کوچک

اگر یک یا دو توسعه‌دهنده مسئول ده‌ها سرویس باشند، هزینه نگهداری می‌تواند از مزایا بیشتر شود.

Distributed Monolith

Distributed Monolith سیستمی است که از چند سرویس تشکیل شده، اما سرویس‌ها استقلال واقعی ندارند:

  • Database مشترک دارند.
  • همیشه با هم Deploy می‌شوند.
  • فراخوانی‌های Sync زیادی دارند.
  • تغییر یک سرویس چند سرویس را می‌شکند.
  • مدل Domain مشترک بزرگی دارند.
  • تست مستقل دشوار است.

این معماری پیچیدگی شبکه را دارد، بدون آنکه مزایای اصلی Microservices را به دست آورد.

مهاجرت از Monolith به Microservices

همه سیستم را یک‌باره بازنویسی نکنید.

روش تدریجی:

  1. Monolith را ماژولار کنید.
  2. وابستگی Moduleها را اندازه‌گیری کنید.
  3. مرزهای Domain را مشخص کنید.
  4. یک قابلیت دارای ارزش مستقل انتخاب کنید.
  5. API یا Event Contract تعریف کنید.
  6. داده آن قابلیت را به‌تدریج جدا کنید.
  7. ترافیک را به سرویس جدید Route کنید.
  8. Monitoring و Rollback داشته باشید.
  9. نتیجه را ارزیابی کنید.
  10. فقط در صورت موفقیت، قابلیت بعدی را جدا کنید.

این رویکرد با Strangler Fig Pattern هم‌راستاست.

چک‌لیست طراحی Microservices

پیش از جداکردن هر سرویس بررسی کنید:

  • قابلیت کسب‌وکار مشخصی دارد.
  • Bounded Context روشن است.
  • تیم یا مالک مشخص دارد.
  • داده تحت مالکیت سرویس مشخص است.
  • API یا Event Contract تعریف شده است.
  • دلیل Deploy مستقل روشن است.
  • نیاز Scale مستقل وجود دارد.
  • وابستگی‌های Sync محدود هستند.
  • Timeout برای همه درخواست‌ها تعیین شده است.
  • Retry محدود و هدفمند است.
  • Idempotency بررسی شده است.
  • Failure Modeها طراحی شده‌اند.
  • Health Check وجود دارد.
  • Log ساختاریافته وجود دارد.
  • Request ID میان سرویس‌ها منتقل می‌شود.
  • Metrics و Trace قابل جمع‌آوری هستند.
  • Versioning API مشخص است.
  • Contract Test وجود دارد.
  • Migration Database مستقل است.
  • Rollback سرویس آزمایش شده است.
  • هزینه زیرساخت قابل‌توجیه است.
  • Modular Monolith به‌عنوان گزینه ساده‌تر بررسی شده است.

پرسش‌های متداول

Microservices چیست؟

Microservices سبک معماری نرم‌افزار است که برنامه را به سرویس‌های مستقل، دارای مرز مشخص و قابل‌استقرار جداگانه تقسیم می‌کند.

میکروسرویس چه تفاوتی با Monolith دارد؟

در Monolith همه قابلیت‌ها در یک واحد Deploy قرار دارند. در Microservices هر سرویس می‌تواند Codebase، Database، Scale و چرخه Deploy مستقل داشته باشد.

آیا Monolith معماری بدی است؟

خیر. Monolith برای بسیاری از پروژه‌های کوچک و متوسط انتخاب مناسبی است. مشکل اصلی معمولاً Monolith بدون مرز و ساختار است، نه خود Monolith.

Modular Monolith چیست؟

Modular Monolith یک برنامه واحد با Moduleهای دارای مرز و وابستگی کنترل‌شده است. همه Moduleها با هم Deploy می‌شوند، اما ساختار داخلی منظم باقی می‌ماند.

هر Microservice باید Database جدا داشته باشد؟

هر سرویس بهتر است مالک داده خودش باشد. این جداسازی می‌تواند منطقی یا فیزیکی باشد. سرویس‌های دیگر نباید مستقیماً Schema داخلی آن را تغییر دهند.

آیا هر Microservice باید زبان برنامه‌نویسی متفاوتی داشته باشد؟

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

REST بهتر است یا gRPC؟

REST برای APIهای عمومی، Debug ساده و سازگاری گسترده مناسب است. gRPC برای ارتباط داخلی Typed و کم‌Latency قابل بررسی است. انتخاب به نیاز پروژه بستگی دارد.

چه زمانی از Message Broker استفاده کنیم؟

برای Jobهای طولانی، Eventها، Notification، پردازش فایل و Workflowهایی که نیاز به پاسخ فوری ندارند، ارتباط Async با Message Broker مناسب است.

API Gateway چیست؟

API Gateway ورودی مرکزی Clientهاست و می‌تواند Routing، Authentication، Rate Limit، Request ID و Aggregation را مدیریت کند.

آیا Kubernetes برای Microservices لازم است؟

خیر. Microservices را می‌توان با Docker Compose یا سایر پلتفرم‌ها Deploy کرد. Kubernetes برای Orchestration در مقیاس بزرگ‌تر مفید است.

مهم‌ترین مشکل Microservices چیست؟

پیچیدگی سیستم توزیع‌شده، Consistency داده، ارتباط شبکه، Monitoring، تست Integration و عملیات Production از مهم‌ترین چالش‌ها هستند.

آیا می‌توان API هوش مصنوعی را به‌صورت Microservice ساخت؟

بله. یک AI Service می‌تواند انتخاب مدل، فراخوانی API، Rate Limit، Retry، Usage و هزینه را متمرکز مدیریت کند. این جداسازی زمانی مفید است که چند سرویس داخلی به قابلیت‌های هوش مصنوعی نیاز داشته باشند.

هزینه استفاده از AI Service چگونه محاسبه می‌شود؟

هزینه به مدل انتخابی، تعداد Tokenهای ورودی و خروجی و تعداد Requestها وابسته است. اطلاعات به‌روز در صفحه مدل‌ها و قیمت‌های درواره قابل مشاهده است.

جمع‌بندی

Microservices فقط تقسیم یک Backend به چند پروژه کوچک نیست. یک معماری میکروسرویس واقعی به مرزهای Domain، مالکیت داده، Deploy مستقل، ارتباط قابل‌اتکا، Observability، Versioning، Idempotency و مدیریت خطا نیاز دارد.

در این مقاله تفاوت Monolith، Modular Monolith و Microservices را بررسی کردیم و دیدیم که Microservices همیشه بهترین نقطه شروع نیست. برای بسیاری از محصولات، آغاز با Modular Monolith و استخراج تدریجی سرویس‌ها تصمیم کم‌ریسک‌تری است.

در پروژه عملی، سه سرویس مستقل با Python، FastAPI و Docker Compose ساختیم:

  • Article Service برای مدیریت داده مقاله
  • AI Service برای اتصال به API درواره
  • API Gateway برای ارتباط با Client و Orchestration درخواست

این ساختار یک نمونه آموزشی از Service Discovery، Database Ownership، ارتباط Synchronous، Timeout، Connection Pool، Health Check و Correlation ID ارائه می‌دهد و می‌تواند در مراحل بعد با PostgreSQL، Message Broker، Distributed Tracing و Orchestration تکمیل شود.

اگر می‌خواهید یک AI Service مستقل برای نرم‌افزار یا معماری Microservices خود بسازید، در درواره ثبت‌نام کنید، کلید API بگیرید و مدل مناسب هر Workflow را از صفحه مدل‌ها انتخاب کنید.

منابع پیشنهادی

مقالات مرتبط

برای مطالعه شرایط استفاده و محدودیت‌های مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.

Read more

اتوماسیون هوش مصنوعی چیست؟ کاربردها و آموزش ساخت AI Automation

اتوماسیون هوش مصنوعی چیست؟ کاربردها و آموزش ساخت AI Automation

اتوماسیون هوش مصنوعی با ترکیب گردش‌کارهای خودکار و مدل‌های هوش مصنوعی، پردازش متن، دسته‌بندی، استخراج اطلاعات و تصمیم‌های پیشنهادی را خودکار می‌کند. در این راهنما، معماری و ساخت نمونه عملی آن با API درواره را می‌آموزید.

Agentic Commerce چیست؟ آینده خرید با ایجنت هوش مصنوعی

Agentic Commerce چیست؟ آینده خرید با ایجنت هوش مصنوعی

Agentic Commerce شیوه‌ای جدید برای خرید اینترنتی است که در آن ایجنت هوش مصنوعی می‌تواند نیاز کاربر را بفهمد، محصولات را جست‌وجو و مقایسه کند و فرایند خرید را پیش ببرد. در این راهنما با معماری، UCP، ACP و پیاده‌سازی آن با API درواره آشنا می‌شوید.