تعیین محدوده سرویس ها در معماری مایکروسرویس
تعیین محدوده سرویس ها در معماری مایکروسرویس، یکی از حیاتی ترین و چالش برانگیزترین جنبه های ساخت یک معماری مایکروسرویس موفق، تعیین مرزهای مناسب برای هر مایکروسرویس است. در حالی که ممکن است برای بهبود قابلیت نگهداری یک پایگاه کد بزرگ را به اجزای کوچکتر و با روابط سست تقسیم کنیم، اما چالش واقعی در تصمیمگیری کجایی و چگونی تقسیم کد برای دستیابی به این ویژگیهای دلخواه است.
چگونه تعریف کنیم که یک سرویس کجا تمام می شود و سرویس دیگری شروع می شود؟ این سؤالات اساسی اغلب برای تیمهایی که تازه وارد مایکروسرویس میشوند، به عنوان چالش های اساسی مطرح می شوند. مرزهای نادرست ترسیم شده می تواند به طور قابل توجهی مزایای مایکروسرویس ها را کاهش دهد و در برخی موارد حتی در برخی موارد پروژه را با شکست روبرو کند. جای تعجب نیست که یکی از مهم ترین سوالاتی که متخصصان مایکروسرویس با آن مواجه هستند این است: چگونه یک برنامه بزرگتر را به درستی به مجموعه ای از مایکروسرویس ها تقسیم کنیم؟
چرا مرزها مهم هستند، چه زمانی مهم هستند و چگونه آنها را پیدا کنیم
اصطلاح «مایکرو» در مایکروسرویس ها به این معناست که خدمات باید کوچک باشند، اما “کوچک” در این زمینه واقعاً به چه معناست؟ بیایید برخی از رویکردهای رایج اما معیوب برای تعریف مرزهای مایکروسرویس را بررسی کنیم:
-
خطوط کد (SLOC)
برخی از تیم ها محدود کردن تعداد خطوط کد را راهی برای کوچک نگه داشتن مایکروسرویس ها در نظر می گیرند. به عنوان مثال، آنها ممکن است یک محدودیت دلخواه مانند 500 خط کد در هر سرویس تعیین کنند. در حالی که SLOC از لحاظ تاریخی به عنوان معیاری برای تلاش یا پیچیدگی استفاده می شده است، به طور گسترده به عنوان یک معیار ضعیف شناخته شده است. پیچیدگی و عملکرد کد را نمی توان به تنهایی با تعداد خطوط به طور دقیق تعیین کرد.
-
مرزهای کارکردی
رویکرد دیگر ترسیم مرزها در لبه های کارکردی (Functional Edge) است و هر کارکرد یا قابلیت را به عنوان یک مایکروسرویس در نظر می گیرد. اگرچه این رویکرد ممکن است تمیز و آسان به نظر برسد، اما اغلب منجر به ریزه کاری بیش از حد خیلی زود می شود که پیچیدگی غیر ضروری را در اوایل چرخه عمر پروژه ایجاد می کند. علاوه بر این، این رویکرد بر روی نیازهای فنی به جای قابلیت های کسب و کار تمرکز دارد، که در طراحی مایکروسرویس ها به عنوان یک ضد الگو (Antipattern) در نظر گرفته می شود.
مشکلات تعاریف مرزهای فنی
ترسیم مرزهای مایکروسرویس براساس نیازهای فنی یا لبه های کارکردی یک رویکرد رایج اما ناقص است. برخی از مهمترین دلایل ناقص بودن این رویکرد عبارتند از:
-
تناسب با قابلیت های کسب و کار:
به گفته لوئیس و فاولر، مایکروسرویس ها باید «براساس قابلیت های کسب و کار سازماندهی شوند نه الزامات فنی». این بدان معناست که مرزها باید منعکس کننده کارکردها و حوزه های اصلی کسب و کار باشند، نه فقط بخش های فنی. به طور مشابه، اصل کپسوله سازی مدولار پارناس بر قابلیت نگهداری طولانی مدت و همسویی با نیازهای کسب و کار تأکید دارد.
-
دانه بندی زودرس
تعیین دانه بندی بیش از حد ریزدانه در اوایل پروژه می تواند به پیچیدگی فوق العاده منجر شود. این می تواند تلاش مایکروسرویس ها را حتی قبل از اینکه فرصتی برای موفقیت داشته باشد متوقف کند.
رویکردهای مبتنی بر به روش ها: طراحی دامنه محور (DDD) و Event Storming
برای تعریف مؤثر مرزهای مایکروسرویس، به روشی نیاز داریم که با قابلیتهای کسب و کار و قابلیت نگهداری طولانیمدت همسو باشد. اینجاست که طراحی دامنه محور (DDD) و Event Storming وارد عمل می شوند.
-
طراحی دامنه محور (DDD):
– DDD یک رویکرد استراتژیک برای طراحی نرم افزار است که بر درک و مدل سازی حوزه های کسب و کار تمرکز دارد. بر روی حوزه های کسب و کاری مشخص (Bounded Context) تأکید می کند، که مرزهای واضح و کاملاً مشخص پیرامون مناطق خاصی از کسب و کار شکل می دهند. با شناسایی Bounded Contextها، تیمها میتوانند مایکروسرویسهایی ایجاد کنند که با قابلیتهای کسب و کار همسو باشد و اطمینان حاصل کنند که هر سرویس منسجم بوده، ارتباطات سستی با سایر سرویس ها دارد و همسو با اهداف سازمان است.
-
Event Storming:
– Event Storming یک تکنیک کارگاهی گروهی است که برای کشف و مدلسازی حوزههای کسب و کار پیچیده استفاده میشود. این تکنیک شامل نقشه برداری از رویدادهای کسب و کار، فرآیندها و تصمیمات کلیدی برای شناسایی مرزها و تعاملات طبیعی است. – این رویکرد به تیم ها کمک می کند تا جریان فرآیندهای کسب و کار را تجسم کنند و مرزهای مایکروسرویس ها را شناسایی کنند.
برای تکمیل DDD و Event Storm
لحاظ نمودن معیارهای زیر نیز در تعیین مرز مایکروسرویس ها مفید است:
- پیچیدگی کسب و کار: تعداد قابلیت های کسب و کار متمایز که یک سرویس آنها را پشتیبانی می کند.
- اندازه تیم: تعداد توسعه دهندگانی که می توانند به طور موثر سرویس را مدیریت کنند.
- سربار عملیاتی: هزینه استقرار، نظارت و نگهداری سرویس.
با لحاظ نمودن این عوامل، تیم ها می توانند مایکروسرویس هایی ایجاد کنند که نه خیلی بزرگ و نه خیلی کوچک (بیش از حد ریز دانه) باشند.
یک دلیل مهم برای اجتناب از رویکرد مبتنی بر اندازه هنگام تقسیم یک برنامه کاربردی به میکروسرویسها، خطر بهینهسازی زودهنگام است. داشتن سرویس بسیار بسیار کوچک که در ابتدای پروژه تیم را با دردسر روبرو می سازد. پذیرندگان اولیه مایکروسرویس ها، مانند نتفلیکس، ساوند کلود، آمازون و دیگران، در نهایت دریافتند که با مایکروسرویس های زیادی روبرو هستند.
با این حال، این بدان معنا نیست که این شرکت ها با صدها مایکروسرویس بسیار ریزدانه در روز اول شروع به کار کردند. در عوض، تعداد زیادی از ریزسرویسها همان چیزی است که آنها پس از سالها توسعه برای آن بهینهسازی کردند، زیرا به بلوغ عملیاتی دست یافتهاند که میتواند سطح پیچیدگی مرتبط با دانهبندی بالای ریزسرویسها را مدیریت کند.
دستیابی به اندازه سرویسها در معماری میکروسرویسها قطعاً سفری است که باید طی زمان بدان دست یافت. تلاش برای طراحی یک سیستم بسیار دانهدار در اوایل پروژه، تیم را با ریسک اساسی روبرو خواهد ساخت.
نرم افزار مونولیت
چه در حال کار بر روی یک پروژه گرینفیلد باشید و چه در حال تجزیه یک نرم افزار مونولیت (Monolith)، رویکرد کاملاً باید این باشد که تنها با تعداد انگشت شماری از سرویس ها شروع کنید و به تدریج تعداد مایکروسرویسها را در طول زمان افزایش دهید. اگر این منجر به این شود که برخی از میکروسرویسهای شما در ابتدا بزرگتر از حالت هدف خود باشند، کاملاً مشکلی نیست. می توانید بعداً آنها را تقسیم کنید.
سم نیومن در کتاب خود با عنوان Building Microservices برخی از قوانین اساسی را معرفی کرد. او پیشنهاد کرد که هنگام ترسیم مرزهای سرویس، باید برای چنین طراحی تلاش کنیم که نتایج آن سرویس هایی با مشخصات زیر است:
ارتباطات و وابستگی های سست در تعیین محدوده سرویس ها در معماری مایکروسرویس
سرویسها باید نسبتاً ناآگاه و مستقل از یکدیگر باشند، به طوری که تغییر کد در یکی از آنها منجر به اثرات در سایرین نشود. همچنین احتمالاً میخواهیم تعداد انواع تماسهای زمان اجرا (Runtime) را از یک سرویس به سرویس دیگر محدود کنیم، زیرا فراتر از مشکل عملکرد بالقوه، ارتباطات مستمر میتواند منجر به اتصال شدید سرویس ها شود.
بسیار منسجم
کارکردهای موجود در یک سرویس باید بسیار مرتبط و منسجم باشند، در حالی که ویژگی های نامرتبط باید در جای دیگری محصور شوند. به این ترتیب، اگر نیاز به تغییر یک واحد منطقی عملکرد دارید، باید بتوانید آن را در یک مکان تغییر دهید و زمان انتشار آن تغییر را به حداقل برسانید (یک معیار مهم). در مقابل، اگر مجبور بودیم کد را در تعدادی از سرویسها تغییر دهیم، باید تعداد زیادی سرویس مختلف را همزمان برای ارائه آن تغییر منتشر کنیم. این امر مستلزم سطوح قابل توجهی از هماهنگی است، به خصوص اگر آن سرویس ها در اختیار چندین تیم باشد و این امر به صورت مستقیم هدف ما برای به حداقل رساندن هزینه های هماهنگی را به خطر می اندازد.
همسو با قابلیت های کسب و کار
از آنجایی که بیشتر درخواستها برای اصلاح یا گسترش کارکردها بر اساس نیازهای کسب و کار انجام میشود، اگر مرزهای سرویس با مرزهای قابلیتهای کسب و کار همسو باشد، طبیعتاً به این نتیجه میرسد که الزامات طراحی فوق (ارتباطات سست و انسجام درونی)، آسانتر برآورده میشوند. در دوران معماریهای یکپارچه، مهندسان نرمافزار اغلب سعی میکردند «مدلهای داده فراگیر» را استاندارد کنند.
با این حال پیروی از این روش، بارها نشان داد که مدلهای دادههای دقیق برای مدلسازی واقعیتها، برای مدت طولانی دوام نمیآورند. آنها اغلب تغییر میکنند و استانداردسازی آنها منجر به دوباره کاری مکرر میشود. در عوض، آنچه بادوام تر است مجموعه ای از قابلیت های کسب و کار است که سرویس ها ارائه می دهند. یک ماژول حسابداری همیشه قادر خواهد بود مجموعه ای از قابلیت های مورد نظر را به سیستم بزرگتر ارائه دهد، صرف نظر از اینکه چگونه کارکرد درونی آن ممکن است طی طول زمان تکامل یابد.