حالت تاریک
فصل ۵ — الگوهای تجزیۀ مبتنی بر کامپوننت
خلاصهٔ تحلیلی — این متن چکیدهای تحلیلی و مستقل به فارسی است، نه ترجمهٔ واژهبهواژه.
مسئله و زمینه
پس از انتخاب تجزیهٔ مبتنی بر کامپوننت، مسئله دیگر صرفاً «کدام سرویس را اول بیرون بکشیم؟» نیست. پیش از استقرار جداگانه باید ساختار مونولیت را طوری اصلاح کرد که اجزای منطقی، وابستگیها و دامنهها قابل مشاهده باشند. فصل یک زنجیرهٔ ششمرحلهای پیشنهاد میکند که ابتدا codebase را قابل فهم میکند و سپس آن را به domain service منتقل میسازد.
هر الگو باید با architecture story همراه باشد: کاری ساختاری با انگیزهٔ معماری/کسبوکار، مانند افزایش چابکی یا extensibility. این با داستان «بعداً کد را تمیز کنیم» فرق دارد؛ خروجی آن باید مرز، کیفیت یا قابلیت تغییر سیستم را بهبود دهد.
مفاهیم و الگوهای مرکزی
۱. شناسایی و اندازهگذاری کامپوننتها
برای هر کامپوننت inventory شامل نام، namespace/directory، سهم از کل کد، تعداد statement و تعداد فایل بسازید. شمار خط یا class معیار قابل اتکایی نیست؛ statement نمای تقریبیتری از حجم رفتار است. کامپوننتهای بسیار بزرگ یا بسیار کوچک نسبت به میانگین، با درصد سهم و انحراف معیار شناسایی میشوند.
هدف یکسانکردن مصنوعی اندازهها نیست. یک جزء بزرگ را فقط وقتی بشکنید که subdomain یا مسئولیت مستقل روشنی دارد؛ در غیر این صورت، عدد بهتنهایی دلیل جداسازی نیست. نمونهٔ گزارشگیری Sysops Squad با سهم بسیار بزرگ، پس از تفکیک به گزارش تیکت، متخصص، مالی و منطق مشترک، به مرزهای معنادارتری رسید.
۲. گردآوری کامپوننتهای مشترک دامنه
منطق دامنهٔ تکراری—مثلاً notification یا auditing در چند جریان—را پیدا و در یک کامپوننت واحد جمع کنید تا بعدها سرویسهای تکراری نسازید. آن را با infrastructure عمومی مانند logging، metrics یا security اشتباه نگیرید؛ اولی رفتار کسبوکاریِ مشترکِ برخی فرایندهاست، دومی نیاز عملیاتیِ همهٔ فرایندها.
اشتراکگذاری همیشه سود خالص نیست. consolidation میتواند coupling ورودی یک جزء را بالا ببرد و به گلوگاه تغییر تبدیل شود. بنابراین پیش و پس از ادغام، Ca را تحلیل کنید و میان shared service و shared library آگاهانه انتخاب کنید.
۳. تختکردن کامپوننتها
کامپوننت را leaf node namespace/directory تعریف کنید. اگر survey هم کد دارد و هم survey.templates زیر آن است، survey root/subdomain است و کدش orphaned محسوب میشود؛ در نتیجه مرز سرویس مبهم میماند. راهها عبارتاند از ادغام فرزند در ریشه، یا انتقال کد ریشه به leafهای جدید بر اساس مسئولیت، از جمله leaf جدا برای shared code.
معیار عملی روشن است: source code فقط در leaf nodeها قرار گیرد. درصد زیاد کد مشترک نیز هشدار میدهد که تفکیک آینده ممکن است با وابستگی کتابخانههای مشترک خنثی شود.
۴. تعیین وابستگی کامپوننتها
نمودار dependency باید در سطح کامپوننت ساخته شود، نه class. Ca، Ce و مجموعشان نشان میدهند کدام مرزها به refactoring نیاز دارند و اندازهٔ تقریبی مهاجرت چیست. گراف خلوت، refactoring سادهتر را نوید میدهد؛ گراف متراکم ممکن است ترکیبی از بازنویسی و refactoring یا حتی توقف مهاجرت را اقتضا کند.
کامپوننت shared را در تحلیل کورکورانه بهعنوان dependency دامنه حساب نکنید: اگر قرار است library کامپایلشونده باشد، فیلترکردن آن تصویر واقعیتری از coupling رفتاریِ سرویسها میدهد.
۵. ساخت دامنههای کامپوننت
کامپوننتهای مرتبط را با همکاری مالک محصول در domainهای منسجم قرار دهید و namespaceها را با آن مرزها همراستا کنید. رابطه معمولاً یکبهچند است: یک domain service چند کامپوننت همخانواده دارد. namespace سلسلهمراتبی میتواند domain، subdomain و component را آشکار کند، اما نامگذاری فنی نباید جای مدل دامنه را بگیرد.
۶. ساخت domain service
پس از اندازهگذاری، تختسازی و domain alignment، گروه هر domain را به واحدی جداگانه deploy کنید. service-based architecture با سرویسهای درشتدانه و حتی database مشترک، مقصد موقت یا نهایی معتبر است. این پله به تیم فرصت میدهد پیش از پذیرش کامل پیچیدگی microservices—دادهٔ مستقل، تراکنش توزیعشده و عملیات سنگین—اندازهٔ مناسب هر دامنه را بیاموزد.
حاکمیت و مصالحهها
الگوها فقط refactoring یکباره نیستند. fitness functionها باید inventory تغییرکرده را هشدار دهند، outlierهای اندازه را بیابند، کد در root namespace را منع کنند، سقف coupling را کنترل کنند، dependencyهای ممنوع را شکست دهند و namespaceهای domain را محدود کنند.
با این حال، آستانهها باید با اندازه و تاریخچهٔ همان سیستم تنظیم شوند. یک کامپوننت ۱۰٪ در برنامهای با ۵۰ جزء، معنایی متفاوت از سامانهای با ۱۰ جزء دارد. همچنین تشخیص منطق مشترک دامنه تا حدی انسانی است؛ هشدار خودکار، جای داوری معماری را نمیگیرد.
راهنمای عملی
- شش الگو را در ترتیب پیشنهادی اجرا کنید؛ بیرونکشیدن سرویس پیش از همراستایی همهٔ domainها جابهجاییهای تکراری میسازد.
- برای هر تغییر، architecture story، معیار پذیرش و fitness function متناظر تعریف کنید.
- نمودارها و inventory را در CI بهروز نگه دارید؛ آنها خروجی تزئینی مرحلهٔ تحلیل نیستند.
- قبل از تبدیل domain به service، ownership، قرارداد UI و وابستگیهای shared را مشخص کنید.
- مرحلهٔ service-based را فرصت مشاهده و یادگیری بدانید، نه شکست در رسیدن به microservices.
نکات کلیدی فصل
- سرویسهای سالم از کامپوننتهای با مسئولیت روشن ساخته میشوند، نه از classهای منفرد.
- اندازه، coupling و ساختار namespace ابزارهای تصمیماند؛ تفسیرشان وابسته به دامنه است.
- consolidation کد تکراری باید با هزینهٔ coupling مشترک سنجیده شود.
- مهاجرت ساختیافته، تدریجی و قابل حاکمیت از مهاجرت شهودی قابل اعتمادتر است.
یادداشت مترجم
- «تختکردن» برای Flatten Components بهکار رفته است؛ مقصود حذف لایههای مبهم namespace و قرارگرفتن کد در leaf nodeهاست.
- architecture story بهصورت «داستان معماری» توضیح داده شده تا با user story و technical-debt story اشتباه نشود.