اگر برای انتشار یک تغییر کوچک باید هشت سرویس را هماهنگ کنید، معماری شما میکروسرویس نیست؛ یک مونولیت توزیعشده و گران است. در طرف مقابل، اگر یک برنامه واحد با ماژولهای روشن، تست سریع و انتشار مطمئن دارید، «مونولیت» بودن عیب محسوب نمیشود. مسئله تعداد Repository یا Container نیست؛ مسئله این است که مرزهای تغییر، داده و مسئولیت تا چه اندازه با نیاز واقعی کسبوکار هماهنگاند.
این راهنما انتخاب میان Monolith، Modular Monolith، Service-based architecture و Microservices را برای تیمهای وب بررسی میکند. وضعیت منابع و ابزارها در ۶ اوت ۲۰۲۶ (۱۵ مرداد ۱۴۰۵) بازبینی شده است. خروجی مقاله یک پاسخ مد روز نیست؛ یک فرایند تصمیم، PoC، ADR و مسیر مهاجرت قابل بازگشت است.
مونولیت و میکروسرویس را درست تعریف کنیم
Monolith برنامهای است که بخشهای اصلی آن بهعنوان یک واحد Deploy میشوند. این تعریف درباره کیفیت کد، تعداد ماژولها یا تعداد Instanceها حکم نمیدهد. یک مونولیت میتواند روی چند Replica افقی اجرا شود، Cache و Worker جدا داشته باشد و ساختار داخلی بسیار منظم یا بسیار آشفتهای داشته باشد.
Microservices مجموعهای از سرویسهاست که هرکدام حول یک قابلیت یا Bounded context معنادار ساخته شده، قرارداد و داده تحت مالکیت خود دارد و بتواند مستقل از دیگران تغییر و Deploy شود. استقلال مطلق وجود ندارد؛ هدف، کمکردن Coupling و هماهنگی اجباری است. سرویس کوچک بدون مالکیت و قابلیت انتشار مستقل فقط یک مرز شبکه اضافه میکند.
| سبک | واحد انتشار | مرز داده | مناسب برای |
|---|---|---|---|
| مونولیت لایهای | کل برنامه | معمولاً Database مشترک | محصول ساده یا دامنه کمتغییر |
| مونولیت ماژولار | کل برنامه | مالکیت منطقی Schema/Table برای هر ماژول | تیم کوچک تا متوسط و دامنه در حال کشف |
| Service-based / Macroservices | چند سرویس درشت | بسته به مرز؛ گاهی Database مشترک کنترلشده | جداسازی چند قابلیت یا تیم بدون ریزدانهسازی |
| Microservices | هر سرویس | داده خصوصی زیر مالکیت سرویس | دامنه پیچیده، چند تیم مستقل و نیاز عملیاتی روشن |
| Functions / Serverless | تابع یا مجموعه تابع | وابسته به طراحی | رویدادهای Burst، Jobهای مستقل و Glue؛ نه پاسخ خودکار به همه دامنهها |
«یک Repository» نیز مترادف Monolith نیست. چند سرویس میتوانند در Monorepo باشند و مستقل Deploy شوند؛ چند Repository هم ممکن است به نسخه هم قفل باشند. Topology کد، Deployment topology و Data ownership سه تصمیم جدا هستند.
مونولیت الزاماً کد درهمتنیده نیست
نسخه بد یک مونولیت، Big Ball of Mud است: هر ماژول مستقیماً جدول و کلاس دیگری را تغییر میدهد، چرخه Dependency دارد و هیچ Contract یا تست مرزی وجود ندارد. نسخه سالم، ماژولهایی با API داخلی محدود، جهت Dependency روشن و مالکیت داده مشخص دارد. تفاوت اصلی با Microservices این است که فراخوانیها درون Process و انتشار مشترک باقی میمانند.
مونولیت ماژولار میتواند این قواعد را enforce کند:
- هر ماژول یک قابلیت کسبوکار و Owner مشخص داشته باشد؛
- دسترسی مستقیم به Table یا کلاس داخلی ماژول دیگر ممنوع باشد؛
- ارتباط از Public API داخلی یا Event تعریفشده انجام شود؛
- تست معماری Cycle و Dependency غیرمجاز را در CI رد کند؛
- Migration داده به مالک همان ماژول نسبت داده شود؛
- از Shared utilities بزرگ که Domain logic را پخش میکند پرهیز شود.
میکروسرویس فقط «برش کد» نیست
با انتقال یک Method به Container جدا، شبکه، Serialization، Timeout، Retry، Versioning، امنیت و Partial failure وارد مسیر میشود. سرویس باید بتواند با Contract پایدار و داده تحت مالکیت خود کار کند. اگر برای هر تغییر، Database مشترک، Library مشترک و Release train مشترک دارید، استقلال اسمی است.
راهنمای رسمی معماری میکروسرویس Microsoft هم مزایایی مانند انتشار و مقیاس مستقل را مشروط به طراحی درست میداند و هم پیچیدگی کل سیستم، سازگاری داده، تراکنش، ارتباط، Versioning و مهارت عملیاتی را جزو چالشهای اصلی معرفی میکند.
چه مسئلهای را میخواهید حل کنید؟
قبل از انتخاب Architecture style، علامت را از علت جدا کنید. کندی Release ممکن است از تست شکننده یا فرایند Approval باشد، نه از Monolith. هزینه بالا ممکن است از Query بد یا ظرفیتگذاری غلط بیاید، نه از نبود Auto-scaling. اختلاف تیمها هم با ایجاد API حل نمیشود اگر مالکیت محصول نامشخص باشد.
| علامت | سؤال تشخیصی | راهحل کمهزینهتر قبل از Split |
|---|---|---|
| Release کند | Build، Test، Approval یا Coupling کدام مانع است؟ | Pipeline موازی، تست هدفمند، Feature flag، مالکیت ماژول |
| بخش خاص داغ | CPU، I/O، Memory یا Queue کجاست؟ | Profiling، Cache، Worker جدا یا Scale کل Replica |
| خرابی گسترده | Failure mode و Blast radius چیست؟ | Timeout، Resource isolation، Queue و Graceful degradation |
| تیمها مزاحم هماند | مرز محصول و مسئول تصمیم روشن است؟ | Module ownership، API داخلی و برنامه Release |
| فناوری محدودکننده | آیا کل سیستم باید عوض شود یا یک قابلیت؟ | Process/Worker جانبی یا Adapter |
| Database گلوگاه | Query، Lock، مدل داده یا بار نوشتن مشکل است؟ | Index، Partition، Read model و Capacity plan |
تصمیم را با Quality Attribute Scenario بسازید
عبارتهایی مثل «مقیاسپذیر باشد» قابل آزمون نیستند. هر ویژگی کیفی را به سناریوی عدددار تبدیل کنید: منبع محرک، رویداد، محیط، پاسخ و معیار.
- Performance: در پیک کمپین، Checkout با ۳۰۰ درخواست بر ثانیه، P95 زیر ۴۰۰ms و Error کمتر از ۰٫۵٪ بماند.
- Availability: خرابی پیشنهاد محصول، تکمیل سفارش را متوقف نکند.
- Deployability: تیم پرداخت بتواند تغییر سازگار را در کمتر از ۳۰ دقیقه و بدون هماهنگی Release کل سیستم Rollback کند.
- Modifiability: افزودن روش ارسال جدید به بیش از دو ماژول تغییر ندهد.
- Compliance: داده کارت وارد Log یا سرویسهای غیرمرتبط نشود.
- Recoverability: برای سفارش RPO نزدیک صفر و RTO پانزده دقیقه رعایت شود.
سپس وزن هر سناریو و شاهد فعلی را در ADR ثبت کنید. اگر هدف عدد ندارد، بعداً نمیدانید مهاجرت موفق بوده یا فقط پیچیدگی جابهجا شده است.
مقایسه واقعی Monolith و Microservices
| معیار | مونولیت ماژولار | میکروسرویس | نکته تصمیم |
|---|---|---|---|
| شروع و Feedback | سریعتر و Local setup سادهتر | Platform و Contract اولیه لازم | در ابهام محصول، سرعت یادگیری مهمتر است |
| انتشار | واحد مشترک؛ قابل اتوماسیون | مستقل، اگر Coupling پنهان نباشد | Change failure و Coordination را اندازه بگیرید |
| مقیاس | Scale کل Process؛ گاهی کاملاً اقتصادی | Scale ریزتر برای Load نامتقارن | Profiling و Cost model لازم است |
| Latency | Call درون Process | Network hop و Serialization | Chain طولانی P99 را بدتر میکند |
| سازگاری داده | تراکنش محلی سادهتر | Workflow چندسرویسی و Eventual consistency | نیازهای Strong consistency را مشخص کنید |
| تحمل خطا | Blast radius مشترکتر؛ ولی سادهتر برای فهم | Isolation ممکن؛ Cascade نیز ممکن | Timeout، Bulkhead و Degradation تعیینکنندهاند |
| تست و Debug | Local و Transaction ساده؛ Suite کل ممکن است کند شود | Component/Contract قوی؛ E2E گرانتر | Trace و Test data ضروری است |
| امنیت | سطح شبکه کوچکتر | هویت Service، Secret و API بیشتر | Zero trust عملیاتی هزینه دارد |
| حاکمیت فناوری | یک Stack و استاندارد ساده | آزادی بیشتر همراه Sprawl | Paved road و فهرست فناوری لازم است |
| TCO | Infrastructure و On-call کمتر | Platform، Telemetry و هماهنگی بیشتر | هزینه انسان را کنار Cloud حساب کنید |
مقیاسپذیری؛ Monolith هم میتواند Scale شود
یک برنامه Stateless را میتوان پشت Load balancer در چند Replica اجرا کرد. این راه گاهی با وجود Scale شدن بخشهای کممصرف، از هزینه تیم و زیرساخت Microservices کمتر است. جداسازی زمانی ارزش دارد که Load profile یک قابلیت واقعاً متفاوت، پایدار و پرهزینه باشد؛ مانند پردازش ویدئو در کنار پنل مدیریت کمبار.
CPU، Memory، I/O، Connection، Queue depth، Throughput و P95/P99 را جدا بسنجید. اگر Database مشترک گلوگاه است، شکستن Application بدون حل الگوی داده، ظرفیت ایجاد نمیکند. Load test باید Journey واقعی، Warm/cold cache، Failure dependency و محدودیت شبکه را پوشش دهد.
استقلال انتشار؛ وعده اصلی و سخت میکروسرویس
یک سرویس زمانی مستقل است که تیم بتواند نسخه Backward-compatible آن را Build، Test، Deploy، Observe و Rollback کند بدون اینکه سرویسهای دیگر همزمان Release شوند. اگر تغییر API نیازمند هماهنگی همه Consumerهاست، فقط Repositoryها جدا شدهاند.
حداقلهای استقلال:
- Contract نسخهپذیر و Consumer-driven contract test؛
- Migration داده Expand-and-contract و سازگار با دو نسخه؛
- Pipeline، Secret، Config و Rollback خودکار؛
- Feature flag و Progressive delivery برای تغییر پرخطر؛
- Owner و On-call روشن با SLO؛
- Dashboard و Alert مربوط به Outcome همان سرویس.
راهنمای طراحی CI/CD امن جزئیات Gate، Artifact، Rollback و Progressive delivery را پوشش میدهد.
مرز سرویس را از دامنه پیدا کنید، نه از جدول و فعل
«UserService»، «OrderService» و «EmailService» فقط ناماند. مرز خوب مجموعهای از قواعد و دادههایی است که معمولاً با هم تغییر میکنند و یک زبان کسبوکار مشترک دارند. Event storming، تحلیل Use case، Change coupling و گفتوگو با Domain expert برای یافتن Bounded context مفیدند.
| نشانه مرز خوب | نشانه مرز بد |
|---|---|
| یک قابلیت قابل نامگذاری از دید کسبوکار | یک Entity CRUD یا لایه فنی عمومی |
| قواعد و واژگان منسجم درون مرز | یک واژه با معنای متناقض در چند سرویس |
| تغییرات بیشتر داخل مرز رخ میدهند | هر Feature چند سرویس را همزمان تغییر میدهد |
| داده Owner مشخص دارد | همه به جدول مشترک Write میکنند |
| Contract کوچک و Outcomeمحور است | API داخلی Database را بیرون نشت میدهد |
| تیم میتواند End-to-end مالک باشد | تحویل میان چند تیم دستبهدست میشود |
اندازه سرویس با تعداد خط یا Endpoint تعیین نمیشود. دو قابلیت که همیشه با هم تغییر میکنند و تراکنش مشترک دارند، احتمالاً زود جدا شدهاند. سرویس ریزدانه Chatty call، Latency و هماهنگی ایجاد میکند.
مالکیت داده؛ سختترین بخش جداسازی
«Database per service» به معنای الزام یک سرور یا فناوری جدا برای هر سرویس نیست؛ اصل، مالکیت و جلوگیری از دسترسی مستقیم سرویس دیگر به Schema داخلی است. در آغاز میتوان روی یک Cluster، Database یا Schemaهای جدا داشت، به شرط کنترل Permission و Migration.
در Monolith modular نیز Tableها را به ماژول مالک نسبت دهید. Query بینماژولی را پشت Read model، API داخلی یا View کنترلشده ببرید. این انضباط پیش از شبکهایکردن مرز، کیفیت آن را آشکار میکند.
تراکنش توزیعشده را با جادو حل نکنید
در یک مونولیت با Database واحد، ACID transaction سادهتر است. در Microservices، Workflowهایی مانند «ثبت سفارش، رزرو موجودی و آغاز پرداخت» ممکن است بین چند مالک داده پخش شوند. باید مشخص کنید کدام Invariant فوراً Consistent است و کدام میتواند با تأخیر همگرا شود.
- Saga: زنجیره اقدامهای محلی با جبران؛ جبران همیشه Undo واقعی نیست.
- Transactional outbox: تغییر Business و ثبت Event در یک تراکنش محلی؛ سپس Publisher آن را تحویل میدهد.
- Idempotency: پردازش تکراری همان پیام نتیجه اضافی نسازد.
- Inbox/Deduplication: Message ID مصرفشده ثبت و تکرار کنترل شود.
- CQRS/Materialized view: Read model برای Query چندمرزی؛ فقط وقتی هزینهاش توجیه دارد.
Exactly-once را بهعنوان ویژگی سراسری فرض نکنید؛ اثر کسبوکار را Idempotent و قابل آشتی بسازید. Dual write مستقیم به Database و Broker بدون Atomic boundary میتواند داده یا Event گمشده ایجاد کند.
ارتباط Sync یا Async؛ بر اساس Semantics انتخاب کنید
REST/gRPC برای پاسخ فوری و Query روشن مناسب است؛ Messaging برای Decoupling زمانی، Buffer بار و Workflowهای طولانی مفید است. Async بودن، خطا را حذف نمیکند؛ آن را به Retry، Ordering، Duplicate، Poison message و Lag تبدیل میکند.
| سؤال | Sync | Async |
|---|---|---|
| آیا Caller همین حالا پاسخ لازم دارد؟ | معمولاً بله | خیر یا Poll/Callback |
| رفتار در قطعی Consumer | Timeout/Fail | Queue میتواند Buffer کند |
| Coupling زمانی | بیشتر | کمتر |
| پیچیدگی داده | Call chain و Retry | Duplicate، Ordering و Eventual consistency |
| Debug | Trace مسیر درخواست | Trace + Message ID + Lag/DLQ |
برای مرزبندی REST و GraphQL و اثر آن بر Contract، راهنمای مقایسه GraphQL و REST را ببینید. اگر Event-driven میشوید، ابتدا Semantics پیام و Failure recovery را طراحی کنید، نه اینکه صرفاً Broker نصب کنید.
Timeout، Retry و Circuit breaker باید بودجه داشته باشند
در سیستم توزیعشده، Failure جزئی عادی است. هر Call باید Deadline داشته باشد؛ Retry فقط برای خطای گذرا و عملیات Idempotent، با Backoff و Jitter انجام شود. Retry چندلایه میتواند Storm بسازد: سه تلاش در Gateway × سه در سرویس × سه در Client، یک درخواست را به ۲۷ تلاش تبدیل میکند.
Circuit breaker از فشار مداوم بر Dependency خراب جلوگیری میکند؛ Bulkhead منابع را جدا میکند؛ Rate limit و Load shedding از اشباع محافظت میکنند. Fallback نباید داده دروغین نشان دهد. برای فروشگاه ایرانی، خرابی سرویس پیشنهاد محصول میتواند با حذف آن بخش Graceful باشد، اما وضعیت پرداخت نباید حدس زده شود.
تحمل خطا در Microservices خودکار نیست
تفکیک Process میتواند Blast radius را محدود کند، اما وابستگی Sync زنجیرهای خرابی را گسترش میدهد. اگر صفحه محصول برای پاسخ به Profile، Inventory، Pricing، Review و Recommendation همزمان نیاز داشته باشد، Availability کل از تکتک آنها کمتر میشود مگر Degradation و Cache طراحی شده باشد.
Failure modeها را روی نمودار Dependency بنویسید: Slow، Error، stale data، duplicate، reorder، partition و total outage. برای هرکدام Detection، پاسخ، مالک و Recovery تعریف کنید. Chaos test فقط بعد از داشتن SLO، Guardrail و Rollback معنا دارد.
Observability پیشنیاز جداسازی است
در مونولیت میتوان یک Request را با Debugger دنبال کرد؛ در Microservices باید Context از مرزها عبور کند. Trace ID، Span، Structured log، Metric و Event کسبوکار را قبل از استخراج اولین سرویس استاندارد کنید. مستندات رسمی OpenTelemetry Signals چهار دسته Traces، Metrics، Logs و Baggage را شرح میدهد.
- Correlation ID در HTTP، Queue و Job حفظ شود؛
- نام Service، Version، Environment و Region جزو Resource attribute باشد؛
- SLI بر Outcome کاربر تعریف شود، نه فقط CPU و Uptime؛
- Sampling برای Error و Trace مهم با سیاست مناسب انجام شود؛
- Log شامل Token، رمز، داده کارت یا اطلاعات حساس نباشد؛
- Dashboard بتواند Release را به تغییر Error و Latency وصل کند.
برای طراحی SLI، Alert و Runbook، راهنمای مانیتورینگ عملکرد و آپتایم نقطه شروع مناسبی است.
تست در دو معماری چه تفاوتی دارد؟
Monolith معمولاً تست محلی و Transactional fixture سادهتری دارد، اما با رشد Codebase ممکن است Suite کند و Release همهچیز پرریسک شود. Microservices به Pyramid متفاوت نیاز دارد: Unit و Component test سریع، Contract test میان Producer/Consumer، Integration test با زیرساخت واقعی و تعداد محدود Journey انتهابهانتها.
| لایه تست | هدف | خطای قابل کشف |
|---|---|---|
| Architecture test | مرز Module/Service | Dependency و دسترسی داده غیرمجاز |
| Unit/Component | قواعد داخل مرز | منطق، Validation و Edge case |
| Contract | سازگاری API/Event | Breaking change و Schema mismatch |
| Integration | Database/Broker/Provider واقعی | Migration، Serialization و Permission |
| Resilience | Latency، Timeout و Duplicate | Cascade، Retry storm و Corruption |
| E2E | Journey حیاتی | اتصال نهایی چند جزء |
محیط تست Microservices را با دهها Mock دروغین نسازید. Contract و Testcontainer/سرویس مدیریتشده آزمایشی، تفاوت رفتار واقعی را بهتر آشکار میکند. Test data باید قابل Reset و فاقد داده شخصی Production باشد.
امنیت؛ سطح حمله با تعداد مرزها رشد میکند
در Microservices هر Endpoint، Identity، Secret، Queue و Pipeline یک Asset است. TLS فقط رمزنگاری مسیر است؛ Authorization باید در سطح Object/Property/Function و با Tenant context اعمال شود. Service-to-service identity، Rotation، Least privilege و Egress policy لازم است.
Gateway میتواند Authentication، Rate limit و Routing را متمرکز کند، اما نباید Domain authorization را ببلعد یا تنها خط دفاع باشد. Service mesh نیز هزینه عملیات و Debug دارد. Threat model و Inventory API را با چکلیست امنیت API هماهنگ کنید.
Kubernetes الزام Microservices نیست
میکروسرویس را میتوان روی Managed container، VM، PaaS یا Serverless اجرا کرد. Kubernetes ابزار Orchestration است، نه سبک معماری و نه پیششرط. طبق مستند رسمی اجزای Kubernetes، حتی یک Cluster پایه Control plane، API server، etcd، Scheduler، Controllerها و Node components دارد؛ این سطح عملیاتی باید با نیاز محصول متناسب باشد.
اگر تیم هنوز Deployment تکرارپذیر، Secret management، Monitoring و On-call ندارد، افزودن Kubernetes مشکل معماری را حل نمیکند. Managed service ممکن است هزینه زیرساخت بیشتری داشته باشد اما هزینه نیروی Platform را کم کند؛ مقایسه را با TCO انجام دهید.
تیم و قانون Conway؛ ساختار سازمان روی سیستم میافتد
میکروسرویس برای یک تیم سهنفره، استقلال تیمی ایجاد نمیکند؛ فقط Ownership بیشتری به هر نفر میدهد. هر سرویس نیازمند توسعه، Review، Security، Pipeline، Dashboard و On-call است. اگر یک تیم همه سرویسها را تغییر میدهد، مرز شبکه احتمالاً ارزش کمی دارد.
تیم Stream-aligned باید بتواند یک قابلیت را End-to-end اداره کند و برای Platform یک Paved road داشته باشد: Template سرویس، CI، Telemetry، Secret، Policy و Runtime استاندارد. آزادی انتخاب زبان برای هر سرویس اگر بدون ظرفیت نگهداری باشد، Patch و Hiring را دشوار میکند.
هزینه کل مالکیت را قبل از برش حساب کنید
| هزینه | مونولیت ماژولار | میکروسرویس |
|---|---|---|
| Compute/Storage | Replica بزرگتر؛ اشتراک منابع | Overhead هر Runtime، Sidecar و حداقل Replica |
| Network | Call داخلی بیشتر | Traffic، Load balancer، NAT/Egress و Broker |
| Tooling | Pipeline و Dashboard کمتر | Registry، Orchestrator، Trace، Catalog و Secrets |
| انسان | Cognitive load کد بزرگ | Platform، SRE، On-call و Contract governance |
| تغییر | هماهنگی Release مشترک | هماهنگی داده و Contract؛ در حالت سالم کمتر |
| Incident | مسیر سادهتر، Blast radius ممکن است بزرگتر | تشخیص سختتر، Isolation بالقوه بهتر |
برای هر گزینه، هزینه ماهانه زیرساخت، نفر-ساعت عملیات، زمان Lead، Incident و آموزش را در سناریوی Base و Peak بسنجید. صرفهجویی فرضی Auto-scaling را بدون Minimum instance، Queue و Database حساب نکنید.
ملاحظات ایران؛ دسترسی، هزینه ارزی و مسیر شبکه
برای تیم ایرانی، وابستگی خارجی بخشی از Architecture risk است. Registry، Package repository، SaaS Observability، DNS، SMS و Payment provider ممکن است محدود، ناپایدار یا ارزی باشند. تعداد سرویس بیشتر تعداد Pull، Secret، Certificate و Dependency عملیاتی را افزایش میدهد.
- Mirror یا Registry داخلیِ کنترلشده و فرایند Pin/Scan Artifact بسازید؛
- Failover را بین Providerهایی که واقعاً مستقلاند آزمایش کنید؛
- Latency داخل/خارج ایران و مسیر کاربران هدف را جدا اندازه بگیرید؛
- قیمت ارز، انتقال داده، لایسنس و نیروی On-call را در TCO وارد کنید؛
- قوانین نگهداری داده و قرارداد سرویسدهنده را با مشاور ذیصلاح بررسی کنید؛
- Runbook قطعی Provider و دسترسی اضطراری را آفلاین و قابل اجرا نگه دارید.
طراحی Multi-cloud کامل برای تیم کوچک اغلب پیچیدگی بیشتری از ریسک رفعشده میسازد. Portability را در نقاط پرریسک با Backup قابل بازیابی، IaC، Export داده و Exit test بسازید.
چه زمانی مونولیت ماژولار انتخاب بهتری است؟
- Problem-solution fit و مرزهای دامنه هنوز تغییر میکنند؛
- یک تیم کوچک یا چند نفر روی کل محصول کار میکنند؛
- بار را با چند Replica و Database/Cache مناسب پاسخ میدهید؛
- تراکنش قوی میان قابلیتها بخش مهم مدل است؛
- Platform، On-call و Distributed tracing بالغ ندارید؛
- سرعت Feedback و کاهش هزینه عملیاتی اولویت دارد.
این انتخاب به معنی بیتوجهی به آینده نیست. Module boundary، تست معماری، مالکیت داده، Event داخلی و ADR همان گزینه خروج آینده را میسازند. مدیریت این انضباط با راهنمای شناسایی و کاهش بدهی فنی آسانتر میشود.
چه زمانی Microservices توجیه دارد؟
- چند Bounded context نسبتاً پایدار و قابل مالکیت دارید؛
- چند تیم باید با Cadence متفاوت و واقعاً مستقل Deploy کنند؛
- Load، Availability یا Compliance بخشها تفاوت معنادار و اندازهگیریشده دارد؛
- تغییرات Cross-boundary کم و Contractها قابل Versioning هستند؛
- CI/CD، IaC، Observability، Security و On-call در عمل کار میکنند؛
- ارزش Lead time/Isolation از هزینه Platform و Consistency بیشتر است.
وجود یک مورد کافی نیست. مثلاً نیاز به Scale مستقل یک Worker تصویر میتواند با جداکردن همان Worker حل شود، نه مهاجرت کل محصول.
Scorecard انتخاب معماری
به هر گزاره از صفر تا دو امتیاز بدهید: صفر یعنی شواهد نداریم، یک یعنی بخشی برقرار است، دو یعنی با داده Production یا فرایند اثبات شده است.
| معیار | ۰ | ۱ | ۲ |
|---|---|---|---|
| مرز دامنه | نامشخص | فرضی | پایدار و تأییدشده با تغییرات |
| استقلال تیم | یک تیم | مالکیت جزئی | تیم End-to-end با On-call |
| نیاز Deploy مستقل | ادعا | گاهبهگاه | Lead time و Blocker اندازهگیریشده |
| Load نامتقارن | حدس | Peak محدود | پروفایل پایدار و پرهزینه |
| بلوغ Platform | دستی | CI/IaC ناقص | Self-service، Policy و Rollback |
| Observability | Log پراکنده | Metric پایه | Trace/SLI/SLO و Release correlation |
| Data autonomy | Write مشترک | مالکیت منطقی | API/Event و Migration مستقل |
| TCO | محاسبه نشده | فقط Cloud | Cloud+ابزار+انسان+Incident |
جمع امتیاز حکم خودکار نیست. اگر Platform، Observability یا Data ownership صفر است، آن را Gate بدانید و پیش از Split اصلاح کنید. نتیجه و فرضها را در ADR ثبت و تاریخ بازنگری تعیین کنید.
PoC چهار هفتهای قبل از تصمیم بزرگ
- هفته اول: یک Journey و Bottleneck واقعی، Baseline فنی و کسبوکار و معیار موفقیت انتخاب کنید.
- هفته دوم: مرز پیشنهادی را ابتدا در Monolith enforce و Contract/Data owner را مشخص کنید.
- هفته سوم: یک Vertical slice را در محیط Production-like با Pipeline، Trace، Failure injection و Rollback بسازید.
- هفته چهارم: Lead time، Latency، Error، هزینه، Cognitive load و Recovery را با وضعیت قبل مقایسه کنید.
PoC فقط Happy path نیست. Timeout، Duplicate message، Schema migration، Version قدیم Consumer، قطعی Provider و بازگشت را آزمایش کنید. اگر برای یک Slice کوچک Platform سنگین لازم است، همان نتیجه بخشی از تصمیم است.
مهاجرت از Monolith؛ Rewrite بزرگ نکنید
راهنمای رسمی تجزیه Monolith در AWS الگوهایی مانند Business capability، Subdomain، Transaction، Service per team، Strangler fig و Branch by abstraction را پوشش میدهد. الگو را بر اساس مشکل و مرز انتخاب کنید؛ Strangler نیز بهتنهایی مشکل داده و عملیات را حل نمیکند.
- هدف و Baseline را ثبت کنید: Lead time، Change failure، Cost، SLO و Bottleneck.
- Dependency و Data flow را با Runtime trace و تحلیل تغییرات بسازید.
- داخل Monolith مرز کاندید را Modular و دسترسی مستقیم را مسدود کنید.
- قابلیتی Cohesive، دارای ارزش و با Coupling قابل کنترل انتخاب کنید.
- Facade یا Anti-corruption layer بسازید تا Caller به محل اجرا وابسته نباشد.
- Data ownership، Migration، Reconciliation و Source of truth را مشخص کنید.
- ترافیک را Canary کنید؛ Metric کسبوکار و فنی و Rollback خودکار داشته باشید.
- مسیر قدیم، Table و Job یتیم را پس از Evidence حذف و ADR را بهروز کنید.
اولین سرویس را چگونه انتخاب کنیم؟
بهترین کاندید لزوماً مهمترین Core نیست. قابلیتی را انتخاب کنید که مرز قابل فهم، ارزش قابل سنجش، وابستگی کم و Failure قابل تحمل داشته باشد. Notification، پردازش Media یا گزارشگیری گاهی کاندید آموزشی خوبی است؛ اما اگر فقط CRUD کوچک و بدون نیاز مستقل باشد، ممکن است برش مصنوعی بسازد.
چهار محور را امتیاز دهید: Business value، Cohesion، Coupling و Migration risk. قابلیتی با ارزش بالا و ریسک متوسط از یک «Easy service» بیاثر بهتر است، ولی پرداخت یا هویت را بدون Runbook و صلاحیت لازم اولین آزمایش نکنید.
مهاجرت داده بدون Source of truth دوگانه
برای هر Entity بنویسید کدام سیستم در هر Phase مرجع Write است. Read میتواند مدتی از Replica یا View جدید باشد، اما دو Writer همزمان بدون Conflict policy خطرناک است. Backfill باید Restartable، Rate-limited، قابل آشتی و دارای Checksum/Count باشد.
الگوی مرحلهای معمول:
- Schema مقصد و Migration سازگار ایجاد شود؛
- Backfill با Snapshot یا Batch کنترلشده انجام شود؛
- تغییرات جدید از Outbox/CDC منتقل و Lag پایش شود؛
- Read shadow و مقایسه نتیجه بدون اثر کاربر اجرا شود؛
- Read سپس Write با Feature flag جابهجا شود؛
- Rollback window و Reconciliation حفظ شود؛
- پس از اطمینان، مسیر و داده قدیم Retire شود.
Rollback و بازیابی را قبل از Cutover طراحی کنید
Rollback کد وقتی Data contract یا Event منتشر شده کافی نیست. Forward fix، سازگاری دو نسخه و Restore داده را جدا تمرین کنید. برای هر سرویس RPO/RTO و وابستگیهای Recovery را ثبت کنید؛ جزئیات در راهنمای Backup و بازیابی فاجعه آمده است.
Cutover موفق باید معیار توقف داشته باشد: Error budget burn، اختلاف Reconciliation، Queue lag، P99 و Conversion. اگر Guardrail شکسته شد، مسیر ترافیک برگردد و دادههای نیمهپردازششده با Runbook آشتی داده شوند.
نشانههای Distributed Monolith
- همه سرویسها یک Database یا Schema را مستقیم Write میکنند؛
- یک Library Domain مشترک باید همزمان در همه Release شود؛
- برای هر Feature چند Repository و تیم تغییر میکنند؛
- Service A بدون تماس Sync با زنجیره B→C→D پاسخ نمیدهد؛
- محیط Local برای اجرا به کل سازمان نیاز دارد؛
- Contract test، Trace و Owner وجود ندارد؛
- یک Pipeline مرکزی همه سرویسها را یکجا Deploy میکند؛
- مرزها بر اساس Table/CRUD یا لایه فنیاند، نه قابلیت کسبوکار.
در این وضعیت، سرویس جدید اضافه نکنید. Change coupling و Runtime dependency را اندازه بگیرید، سرویسهای همیشههمراه را ادغام کنید و مالکیت داده را اصلاح کنید. برگشت از Microservices ناموفق به سرویسهای درشتتر شکست نیست؛ کاهش هزینه هماهنگی است.
اشتباههای پرهزینه در انتخاب معماری
- کپی معماری Netflix یا Amazon بدون Scale، تیم و محدودیت مشابه؛
- شروع با دهها سرویس برای MVP و دامنه ناشناخته؛
- برش بر اساس Entity، Controller یا Function؛
- Database مشترک با Write آزاد و نام «Microservices»؛
- فرض اینکه Queue یا Kubernetes Coupling را حذف میکند؛
- آزادی نامحدود زبان، Database و Framework بدون Paved road؛
- Retry بدون Deadline، Jitter، Idempotency و Budget؛
- تکیه به تست End-to-end بهجای Contract و Component؛
- Migration بزرگ بدون Baseline، Canary و Rollback داده؛
- محاسبه فقط هزینه Server و نادیدهگرفتن Platform/On-call.
چکلیست نهایی تصمیم
- مشکل با داده و Quality attribute عدددار تعریف شده است.
- گزینههای Modular Monolith، Service-based و جداسازی محدود بررسی شدهاند.
- مرز دامنه با Change/runtime coupling و Domain expert آزموده شده است.
- هر داده Owner، Source of truth و سیاست Consistency دارد.
- ارزش Deploy/Scale مستقل از پیچیدگی توزیعشده بیشتر است.
- تیم Owner، On-call و ظرفیت Cognitive لازم را دارد.
- CI/CD، Contract test، Observability، Security و Rollback آمادهاند.
- TCO شامل Cloud، ابزار، نفر-ساعت و Incident محاسبه شده است.
- ریسکهای دسترسی و هزینه ایران در Provider/Registry/SaaS ثبت شدهاند.
- PoC Production-like و ADR با Trigger بازبینی وجود دارد.
جمعبندی؛ مرز تغییر را به مرز شبکه تبدیل کنید، نه برعکس
مونولیت ماژولار برای بسیاری از محصولات یک مرحله موقت خجالتآور نیست؛ معماری درست و اقتصادی است. Microservices نیز مقصد بلوغ همه سیستمها نیست. وقتی دامنه، سازمان و Load مرزهای مستقل را اثبات کردهاند و تیم توان اداره Distributed system را دارد، جداسازی میتواند Lead time، Scale و Fault isolation را بهتر کند.
ابتدا مرز را در Code و Data روشن کنید، سپس با یک Vertical slice و معیار قبل/بعد ارزش Network boundary را بسنجید. اگر برای معماری موجود، ADR، Service boundary workshop یا برنامه Strangler به ارزیابی مستقل نیاز دارید، درخواست مشاوره فنی Mindio را ثبت کنید.
سؤالات متداول Monolith و Microservices
برای استارتاپ کوچک مونولیت بهتر است یا میکروسرویس؟
در بیشتر موارد، مونولیت ماژولار شروع کمریسکتری است؛ چون دامنه هنوز تغییر میکند و تیم باید سریع از بازار یاد بگیرد. استثنا زمانی است که از روز اول مرز مستقل و نیاز سخت Compliance، Scale یا فناوری دارید و توان عملیاتی آن نیز فراهم است. تصمیم را با شواهد بگیرید، نه تعداد اعضا بهتنهایی.
آیا میکروسرویس بدون Kubernetes ممکن است؟
بله. میتوان سرویسها را روی PaaS، Managed containers، VM یا Serverless اجرا کرد. Kubernetes قابلیت Orchestration قدرتمندی دارد، اما Control plane، شبکه، Storage، Policy و عملیات خود را اضافه میکند. گزینهای را انتخاب کنید که تیم بتواند با SLO و هزینه قابل قبول اداره کند.
آیا هر میکروسرویس باید Database جدا داشته باشد؟
هر سرویس باید مالک منطقی داده و Schema خود باشد و سرویس دیگر از مسیر Contract به آن دسترسی بگیرد. این الزاماً به معنای سرور Database جدا نیست؛ میتوان ابتدا روی Cluster مشترک با Schema و Permission جدا کار کرد. Shared write مستقیم استقلال را از بین میبرد.
از کجا بفهمیم مونولیت باید شکسته شود؟
وقتی مرز دامنه پایدار است و شواهد نشان میدهد Coupling انتشار، Load نامتقارن، Availability یا Compliance با راهحل سادهتر رفع نمیشود. Slow build یا Codebase بزرگ بهتنهایی کافی نیست. Baseline بگیرید و یک Slice را با معیار Lead time، Reliability، Cost و Cognitive load آزمایش کنید.
مهاجرت کامل به Microservices چقدر طول میکشد؟
عدد عمومی معتبری وجود ندارد. زمان به اندازه دامنه، Coupling داده، پوشش تست، بلوغ Platform و تعداد تیمها وابسته است. بهجای پروژه «مهاجرت کامل»، Outcomeهای مرحلهای تعریف کنید؛ ممکن است بهترین پایان، چند سرویس درشت در کنار یک مونولیت ماژولار باشد.






