نکته: کدنویسیهای موجود در این مقاله، در وبسایت codesandbox.io قرار دارند، که لینک مربوط به هر بخش، در همان بخش قرار دارد.
NodeJS یکی از فناوریهایی است که در حال حکمرانی بازار توسعه نرمافزار هستند. اما درست به مانند هر فناوری دیگری، حیاتیترین بخش آن، خود فناوری نیست، بلکه نحوه استفاده از آن برای رسیدن به اهداف است. از این رو، در این پست تلاش میشود تا تمام تجربیاتی که در حین کار با NodeJS به دست آمده است (که معمولا با نامهای «معماری پاک» و «روشهای خوب» شناخته میشود) جمعآوری شود تا یک معماری خوب و آزمایش شده تعریف شود و شما بتوانید از آن استفاده کنید و backend خود را به صورتی طراحی کنید که با اکثر پروژهها تطبیق پیدا کند و به روش درستی مقیاسبندی شود.
نکته مهم:
این یک راهنمای بینقص برای طراحی بهترین برنامهها نیست. مطمئنا بهبودهای بیشتری هم هستند که میتوانید اعمال کنید؛ مخصوصا با توجه به نیازهای هر برنامه. اما فکر میکنم که بتوانید از تجربه من در آزمایش این معماری برای جلوگیری از مشکلات در میان پروژه استفاده کنید.
مشکلات رایج در معماری NodeJS
بیایید شروع به طراحی معماری خود بکنیم. بسیاری از مفاهیم میتواند به فناوریهای دیگری مانند .NET Core و پایتون نیز صدق کنند، اما بیایید بر روی این مورد تمرکز کنیم. ما Express را به عنوان API و Mongoose را برای MongoDB انتخاب میکنیم. گرچه، بسیاری از مفاهیم استفاده شده در این بخش، نسبت به فناوری استفاده شده شفاف خواهند بود. کد ما در جهت درک بهتر، در ES5 نوشته خواهد شد. برای کار خود، از یک سرور NodeJS استفاده میکنیم، که منبع خوبی برای ساخت کاربران جدید را دارد:
این سرور، چندین ایده خوب پشت خود دارد. همچمین این سرور، لایههای مدیریت درخواستهای API و کوئریهای دیتابیس، و همچنین فایلهای ماژولهای منابع REST و ماژولهای دیتابیس را از هم جدا کرده است. (گرچه یک ماژول مدیریت کاربران به این مثال اضافه شده است) به نظر میرسد که این سرور همزمان با افزایش کمیت و پیچیگی منابع، تشدید میشود، اما این مسئله کاملا صحیح نیست. بیایید فرض کنیم که باید منبع جدیدی را به سرورمان اضافه کنیم، تا یک گروه بسازیم. یک گروه، برای شامل شدن کاربران طراحی شده است. اما منطق کار ما میگوید یک گروه، باید یک کاربر پیشفرض بسازد که مدیریت گروه را به عهده داشته باشد. همانطور که میتوانید در لینک زیر ببینید، پیادهسازی اقدام اولیه بسیار ساده است:
اما چگونه باید مجددا از کد قبلی خود برای ساخت یک کاربر استفاده کنیم؟
وارد کردن متد در لایه routeها آزار دهنده است، زیرا باید شروع به انتقال آبجکتهای express link شده (req، res و next) از یک مکان به مکان دیگر کنیم. وارد کردن متد در لایه دیتایس نیز نادرست است، زیرا ما کاراییهایی که به طور استراتژیک به لایه routeها اضافه شده است را از دست میدهیم.
فرض کنید که میخواهیم به عملکرد فعلی خود، چند واحد آزمایشی اضافه کنیم.
اما...
- آیا آزمایش دامنههای route ما، کمی express link شده نخواهد بود؟
- آیا اگر به عنوان مثال به یک GraphQLAPI منتقل شویم، مجبور میشویم تمام آنها را بازنویسی کنیم؟
- چگونه این مشکلات را حل خواهید کرد؟
- متدهای فعلی را تقسیم میکنید؟
- لایه جدیدی میسازید؟ یا شاید چند لایه؟
چیزی را بازتولید نکنید، بلکه معماری را پاکسازی کنید
من یک راه حل دنبالهدار را آشکار نمیکنم؛ بلکه فقط مدلهای معماری فعلی را ارزیابی کردهام و آنها را با نیازهای خودم تطبیق دادهام.
پس حالا شروع به تجزیه و تحلیل یکی از معروفترین الگوهای معماری، به نام «معماری پاک» میکنیم. ایده اصلی این معماری، مستقل کردن یک مدل از فریموورک، کتابخانه، دیتابیس و... با ساخت یک لایه حد واسط به نام Interface adapters (آداپتورهای رابط) است. این معماری همچنین بر روی آسانی آزمایش کد تاکید دارد. پس با توجه با این قوانین، این معماری منطق کار ما را در فریموورک API جدا کرده، و یک لایه دامنه جدید میسازد:
کد دامنه ما قابلیت استفاده مجدد را دارد و از فریموورک API ما مستقل است.
کد موجود کاملا قابل آزمایش است. در مثال آخر خواهیم دید که عملکرد هستهای چگونه بدون تغییر میماند، گرچه یک API بر پایه GraphQL اضافه کردهایم که از همان متدهای دامنه مشابه استفاده میکند. به علاوه آن، یک سری واحد آزمایش ساختهایم که عملکرد صحیح منطق کار ما را بدون توجه به فریموورک API مورد استفاده، تضمین میکنند:
بهبودهای بیشتر
همانطور که پیشتر اشاره شد ، این فقط یک معماری پایه است که بهتر است نیازهای پروژه خود را بر حسب آن بسازید. همچنین نمیخواهم این پست بیش از حد بزرگ شود، پس موارد زیر را بیش از حد با جزئیات توضیح نمیدهم.
- تعریف آداپتورهای API را بهبود دهید: آداپتور REST API ما منطق کار ما را از منابع، درخواستها، پاسخها و دیگر موارد REST جدا میکند. اما یک لایه کاملتر، باید شامل mapping کدهای HTML، پیامهای i18n و دیگر موارد مورد نیاز در یک پروتکل API باشد.
- لایه آداپتور را به دیتابیس خود اضافه کنید: همانطور که فناوری API خود را از کد خود جدا کردیم، باید لایه دیتابیس را نیز جدا کنیم تا برنامهمان از فناوری دیتابیسی که استفاده میکنیم، مستقل شود. به همین ترتیب، باید برای هر فریموورک خارجی که میخواهیم از کد دامنه خود جدا کنیم، یک آداپتور اضافه کنیم.
- موجودیتهای دامنه را تعریف کنید: برخی مدلهای معماری پاک، میگویند که باید موجودیتهای هستهای را در منطق کار خود تعریف کنید و آنها را در یک لایه داخلی چکیدهسازی کنیم. این کار ممکن است به نوع برنامه شما بستگی داشته باشد، اما اگر میخواهید برخی موجودیتهای به خوبی تعریف شده را در کنار منطق برنامه خود داشته باشید، بهتر است آنها را در نظر داشته باشید.
حال که این موارد را یاد گرفتید، میتوانید خود را با ساخت یک سرور NodeJS که آماده تطابق با هر موقعیتی باشد، به چالش بکشید.
دیدگاه و پرسش
در حال دریافت نظرات از سرور، لطفا منتظر بمانید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید