Skip to content

فصل ۴ — تجزیۀ معماری

خلاصهٔ تحلیلی — این متن صورت‌بندی تحلیلیِ فصل به فارسی است، نه ترجمهٔ واژه‌به‌واژه.

مسئله و زمینه

فصل قبل می‌پرسید «چرا» باید سامانه را ماژولار کرد؛ این فصل بر «چگونه» تمرکز دارد. بیرون‌کشیدن تصادفی چند قابلیت از مونولیت—anti-pattern «مهاجرت فیل»—می‌تواند مقصد را به مونولیت توزیع‌شده تبدیل کند: مرزها تصادفی‌اند، dependencyها باقی‌اند و هزینهٔ عملیات بالا می‌رود.

شروع درست، دید کلی از ساختار است: آیا codebase اصلاً قابل تجزیه است؟ آیا componentهای قابل تشخیص دارد؟ پاسخ این پرسش‌ها انتخاب میان تجزیهٔ مبتنی بر کامپوننت و tactical forking را تعیین می‌کند.

مفاهیم و الگوهای مرکزی

ارزیابی قابلیت تجزیه

Big Ball of Mud سیستمی است که در آن ساختار درونی قابل اتکا نیست؛ مانند handlerهایی که مستقیم با database کار می‌کنند و مرز مسئولیت ندارند. هیچ شاخص یگانه‌ای حکم نهایی نمی‌دهد، اما metrics وابستگی دیدی از سلامت پایه فراهم می‌کنند:

  • Ca (afferent coupling): شمار وابستگی‌های ورودی به یک artefact؛ چه تعداد جزء به آن تکیه دارند.
  • Ce (efferent coupling): شمار وابستگی‌های خروجی؛ آن جزء به چند بخش بیرونی تکیه دارد.
  • A، انتزاعی‌بودن: نسبت artefactهای انتزاعی (مثل interface) به کل artefactهای انتزاعی و concrete.
  • I، ناپایداری: Ce / (Ce + Ca)؛ مقدار نزدیک یک یعنی جزء در معرض تغییرات بیرونی بیشتری است.
  • فاصله از توالی اصلی: D = A + I - 1؛ توازن A و I را می‌سنجد. نزدیکی به خط مطلوب، تعادل تقریبی را نشان می‌دهد؛ گوشهٔ «درد» کد concrete و سخت‌تغییر، و گوشهٔ «بیهودگی» انتزاع‌های زیاد و کم‌مصرف را نشان می‌دهد.

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

تجزیهٔ مبتنی بر کامپوننت

در codebase نسبتاً ساخت‌یافته، سرویس را از کامپوننت بسازید نه از class منفرد. کامپوننت نقش، مسئولیت و عملیات روشن دارد و در namespace یا directory قابل مشاهده است. با refactoring تدریجی، این بلوک‌ها به domain serviceهای مستقل تبدیل می‌شوند.

سرویس‌محورِ درشت‌دانه می‌تواند نقطهٔ میانی مفیدی باشد: تیم ابتدا مرزهای دامنه را می‌آزماید، بدون آنکه فوراً database را جدا، containerization را اجباری یا همهٔ workflowها را توزیع کند. سپس فقط دامنه‌هایی که واقعاً نیاز دارند، ریزتر می‌شوند.

Tactical Forking

در codebase آشفته، بیرون کشیدن یک تکه معمولاً dependencyهای پنهان زیادی را نیز همراه می‌آورد. tactical forking مسیر برعکس است: از مونولیت clone می‌گیرند، هر تیم رفتار هدف را نگه می‌دارد و کدی را که دیگر لازم نیست با آزمون/کامپایل حذف می‌کند. این روش برای رسیدن سریع به سرویس‌های درشت‌دانه مفید است.

مصالحه‌ها

تجزیهٔ مبتنی بر کامپوننت تحلیل و refactoring بیشتری در ابتدا می‌خواهد، اما مرزهای طبیعی‌تری می‌سازد، shared functionality را آشکار می‌کند و احتمال duplication را پایین می‌آورد. tactical forking سریع‌تر آغاز می‌شود و حذف کد در آشفتگی آسان‌تر است، اما کد نهفتهٔ اضافی، نام‌گذاری ناسازگار و منطق مشترکِ تکراری برجای می‌گذارد. به‌تنهایی، ساختار داخلی سرویس‌های حاصل را سالم نمی‌کند.

در نمونهٔ فصل، وجود component boundaryهای مناسب و حساسیت بالا به نگهداشت‌پذیری و قابلیت اطمینان، هزینهٔ بیشتر روش کامپوننتی را توجیه می‌کند.

راهنمای عملی

  1. پیش از استخراج، inventory کامپوننت‌ها و نمودار coupling بسازید.
  2. با A، I و D تنها به‌عنوان علائم استفاده کنید؛ خروجی را با شناخت دامنه و کد بررسی کنید.
  3. اگر مرزهای کامپوننتی قابل تشخیص‌اند، اول آن‌ها را تمیز و دامنه‌بندی کنید؛ بعد سرویس بسازید.
  4. اگر ساختار بسیار آشفته است و زمان حیاتی است، fork را تاکتیکی و موقت بدانید و برای پاک‌سازی پس از جداسازی برنامه داشته باشید.
  5. تصمیم انتخاب مسیر را در ADR با شواهد، هزینهٔ کوتاه‌مدت و پیامدهای بلندمدت ثبت کنید.

نکات کلیدی فصل

  • شکستن مونولیت بدون نقشه، روش مهاجرت نیست.
  • تجزیه‌پذیری، پیش‌شرط انتخاب الگوی استخراج است.
  • metrics coupling و تعادل انتزاع/ناپایداری، ریسک بنیاد کد را آشکار می‌کنند.
  • سرویس‌محورِ درشت‌دانه می‌تواند پله‌ای کم‌خطرتر از جهش مستقیم به microservices باشد.

یادداشت مترجم

  • «تجزیه» در این فصل به شکستن ساختار معماری به مرزهای قابل استقرار اشاره دارد، نه صرفاً decomposition الگوریتمی.
  • نام‌های Ca، Ce، A، I و D حفظ شده‌اند تا خواننده بتواند با ابزارهای تحلیل کد تطبیق دهد.