استفاده از الگوی طراحی سلکتور در Node.js — از صفر تا صد
همچنان که دنیای توسعه بکاند به خصوص اکوسیستم Node.js در حال پیشرفت است، تجزیه تابعی (functional decomposition) به یک راهبرد ضروری برای پایداری سیستم بدل شده است و محرک اصلی حرکت این صنعت به سوی معماری میکروسرویس بوده است. در این مقاله یک رویکرد خاص به نام الگوی طراحی سلکتور را در محیط زمان اجرای Node.js بررسی میکنیم.
دیگر آن روزها که سیستمهای با طراحی شیءگرای شبیه جاوا به صورت تکبعدی موجب میشدند تجزیه سرویسهای دامنه سطح بالای دشوار شود، گذشته است. با این که این راهبردها برای کدبیسهای کوچک تا متوسط و حتی تا حدودی بزرگ مقیاس مناسب است، اما با معماری میکروسرویس به خوبی انطباق پیدا نمیکند. برای شرکتهایی که میخواهند تیمهای کوچک و مستقل دو پیتزایی (کنایه از تیمهایی که میتوانند برای ناهار صرفاً دو پیتزا صرف کنند) داشته باشند، توانایی فورک کردن کامپوننتهای کاملاً منسجم و با تزویج سست به ریپازیتوریهای مجزا که از سوی تیمهای خودمختار به سبک DevOps مدیریت میشوند، به امری ضروری تبدیل شده است.
به همین دلیل الگوی «مدل دامنه آنمی» (anemic domain model) از سبک طراحی مبتنی بر دامنه ترجیح دارد. بدین ترتیب زبان بین سرویسها دیگر «شیء» نیست، بلکه صرفاً «داده» است. اجرای عملیات روی اشیای دادهای قدیمی ساده از نظر نوشتن، خواندن، تست و مهمتر از همه تجزیه و مقیاسبندی، آسان است. بازگشت سرمایه کدنویسی لازم برای پشتیبانی از طراحیهای شیءگرا در این چشمانداز نوین دیگر توجیهی ندارد. نگهداری تعاریف کلاس بزرگ، factory-ها، اعتبارسنجی تجمیعی، کدهای نگاشت شیء-رابطهای، شِماها و غیره برای نگهداری یکپارچگی مدل موجود به جای ساخت ساختارهای پشتیبان ضروری برای عرضه ارزش واقعی بیزینس کار بیهوده محسوب میشوند.
اینک سؤال این است که مقصدمان رو به کجاست؟ چگونه کدبیس موجود سنتی با معماری شیءگرا را برداریم و به طور امنی به منظور رشد سریع بسط دهیم. البته پاسخ این سؤال در یک مقاله نمیگنجد و ما در این مقاله صرفاً قصد داریم یکی از الگوهایی که برای خروج از معماری تکبعدی مناسب است را توضیح دهیم که آن را الگوی «سلکتور» (Selector) مینامیم. الگوی سلکتور روشی کاملاً آسان و شهودی برای تبدیل منطق دامنه تشکیل یافته در طی زمان به برنامهنویسی تابعی بدون زحمت زیاد است.
یک سلکتور تابعی همگام است که برای محاسبه مقدار (های) مشتق از یک شیء مورد استفاده قرار میگیرد. بدین ترتیب منطق دسترسی به مشخصه به تابعهای با استفاده مجدد تبدیل میشود تا قابلیت استفاده مجدد و تستپذیری در لایه سرویس افزایش یابد.
زمانی که در پارادایم برنامهنویسی تابعی برنامه مینویسیم، کدها غالباً به صورت زیر هستند:
1class User {
2 firstName = 'foo';
3 lastName = 'bar';
4 fullName = () => `${this.firstName} ${this.lastName}`;
5}
6const user = new User();
7console.log(user.fullName()); // foo bar
استفاده از سلکتورها به عنوان الگو درون کدبیس موجب بازطراحی کد فوق به صورت زیر میشود:
1class User {
2 firstName = 'foo';
3 lastName = 'bar';
4}
5// selectors.js
6export const fullName = (user) => `${user.firstName} ${user.lastName}`;
7// main.js
8const user = new User();
9console.log(fullName(user)); // foo bar
اما اینک سؤال این است که چرا باید چنین کاری بکنیم؟ تابعهای کوچک قابل جابجایی هستند. استفاده مجدد، اشتراک یا کپی کردن منطق تابعی روی مرزهای دامنه یا سرویسها (ریپازیتوریهای گیت مجزا) بدون خدشهدار کردن خطوط چارچوب کراندار و دسترسی به متدهای شیء دامنه در چارچوبهای بیگانه در این حالت بسیار آسان است.
علاوه بر طراحی معماری، مزیتهای دیگری نیز وجود دارد.
- کلیدواژه this: کلیدواژه this در جاوا اسکریپت یکی از کمتر شناختهشدهترین اجزای این زبان و عموماً منشأ بسیاری از باگها است. selectors یک رویکرد برنامهنویسی تابعی است و استفاده از آن موجب کاهش چشمگیری در نیاز به استفاده از کلیدواژه this در کد میشود. بدین ترتیب چارچوب اجرایی به صورت صریح به عنوان یک آرگومان به تابع ارسال میشود و از این رو هیچ نیازی به «اتصال تابع» (function binding) وجود ندارد.
- حالت ضمنی در برابر حالت صریح: در زمان تعریف کردن تابعهای سلکتور (به خصوص در تایپ اسکریپت) ایجاد اینترفیسهای تمیز، روشن و خوانا بسیار آسان است. تجزیه و انتسابهای پیشفرض ES6 موجب سادهتر شدن درک ورودیهای تابع شده و بدین ترتیب تست unit سرراست و آسان گشته است، زیرا میتوانید با اشیای جاوا اسکریپت قدیمی ساده (POJO) نیز تست کنید. با بهرهگیری از رویکرد شیءگرا کدهای آماده زیادی وجود دارند که باید در فایل تست قرار گیرند تا اشیایی با حالت معتبر برای تست برخی کارکردهای ساده ایجاد شوند.
- کاهش شلوغی لایه سرویس: لایه سرویس در چارچوب یک معماری ششگوشه، اجرای منطق I/O را در پاسخ به برخی محرکها هماهنگ میسازد. در اغلب موارد اتفاقات زیادی در جریان هستند و برای کاستن از پیچیدگی سایکلومتیک کد، منطق بیزینس غالباً به صورت تابعهای مجزایی در تابعهای سرویس کوچکتر (تابعی) یا متدهای شیء (شیءگرا) جداسازی میشود. رویکرد سلکتورها یک مکان مشخص برای ذخیرهسازی محاسبات فراهم میسازد تا این متدهای بزرگ خواناتر شوند.
- قابلیت کَش شدن: memorization در مرورگر در رویکرد سلکتورها موضوع مهمی است، زیرا اپلیکیشنهای مبتنی بر مرورگر نسبتاً عملکرد پایینی دارند و انتظار نمیرود که استفاده از حافظه در اپلیکیشنهای با عمر کوتاه رشد چندانی داشته باشد. در سمت سرور این حالت وجود ندارد. بسته به اپلیکیشن یک سلکتور در یک موجودیت با عمر طولانی در سمت سرور میتواند در حافظه قرار گیرد یا در آن کَش شود تا عملکرد بهبود یابد. به بیان ساده زمانی که سلکتور مقدار محاسبهشده را فرامیخواند که در حافظه ذخیره شده است و زمانی که دوباره با همان ورودی فراخوانی میشود، مقدار کش به جای محاسبه مجدد بازگشت مییابد. این حالت نیز در توسعه سمت سرور معمولاً ایده بدی محسوب میشود، اما اگر بدانید چگونه از آن استفاده کنید، ابزار مفیدی به خصوص در کد سطح زیرساخت به حساب میآید.
فایل سیستم چگونه است؟
src/ CatController.js CatRepository.js CatService.js // <- move 'selector' code from here selectors.js // <- to here
پیادهسازی سلکتورها نخستین گام از مسیر طولانی تجزیه رویکرد تکبعدی، معکوس سازی منطق بیزینس و ساخت معماری تکاملی محسوب میشود.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- مجموعه آموزشهای برنامهنویسی
- آموزش Node.js — مجموعه مقالات مجله فرادرس
- آموزش Node.js: مفاهیم مقدماتی — بخش اول
- Node.js چیست؟ — به زبان ساده
==