هنوز چند بایت مونده که پیدا کنم...

شش + 1 قدم تا مایکرو فرانت اند با انگولار

6 Steps to Micro Frontend with Angular

اخیراً ما به تیم های زیادی در پیاده سازی برنامه های تک صفحه ای انگولار (Angular SPA) در محیط های مایکروسرویس کمک کردیم. راه های مختلفی برای این کار وجود داره. توی این مقاله، من، نحوه ی اجرا و پیاده سازی یکی از اون ها رو توی 6 مرحله بهتون نشون میدم: یه پوسته داریم که مایکرو فرانت اندها رو بنا به نیاز، بارگذاری می کنه. معمولا در ماکرو-معماری از کامپوننت های وب (Web Component) استفاده نمیکنیم؛ به جای اون، فقط با SPAهای معمولی که در صورت نیاز، بارگذاری و راه اندازی میشن، کار می کنیم، اما برای مایکرو-معماری هنوز از اجزای وب استفاده میشه:


درحالی که این روش، پیاده سازی رو ساده تر می کنه، اما ما همچنان میتونیم برنامه های مختلف رو با استفاده از سایه ی DOM ایزوله تر کنیم، چون انگولار از روز اول تا الآن، از این استاندارد مرتبط با کامپوننت های وب، برای کامپوننت های خودش پشتیبانی کرده. این مثال، کلاینت – a وکلاینت – b رو در پوسته بارگذاری می کنه. همچنین اولی، یه ابزارک (Widget) رو با دومی به اشتراک میذاره:

 مرحله ی هیچ!  اطمینان حاصل کنید که نیازش دارید

اول اطمینان حاصل کنید که این رویکرد، با اهداف معماری شما همخوانی داشته باشه. مایکرو فرانت اندها کلی پیامد دارن که شما باید از اونا اطلاع داشته باشین. در مورد پیامدهای این مدل معماری شاید بعدا مطالبی نوشتم اما از حوصله بحث فعلی خارج هست.

مرحله ی اول: SPA هاتون رو پیاده سازی کنید

مایکرو فرانت اندهاتون رو به عنوان یه برنامه ی معمولی انگولار، پیاده سازی کنین. در معماری مایکروسرویس تقریباً رایج هست که هر قسمتی کد ریپازیتوری(Code Repository) خاص خودشو داشته باشه تا اینکه اونا رو از هم جدا کنیم  و برعکس، من خیلی از مایکرو فرانت اندها رو دیدم که برپایه ی monorepo(مونوریپو) بودن که دلایل تجربی خوبی هم داشتند برای کارشون.

الآن میتونیم در مورد مناسب بودن اصطلاح مایکرو فرانت اند بحث کنیم اما من این کارو نمی کنم؛ چون عملاً کمکی بهمون نمی کنه. چیزی که الآن میشه روش حساب کرد، پیدا کردن یه معماری خوبه که با هدفای شما سازگار باشه و از طرفی شما هم از پیامدهاش باخبر باشین.

اگه بخوایم با مونوریپو کار کنیم، باید اطمینان حاصل کنیم که به هر ترتیبی مایکرو فرانت اندها به همدیگه تنیده نشده باشن. Nrwl’s Nx یه راه حل خیلی عالی برای این داره: این اجازه رو داده تا یه سری محدودیت برای دسترسی اعمال بشه و این محدودیت ها به ما بگن که چه کتابخونه ای میتونه به چه کتابخونه ی دیگه ای دسترسی داشته باشه. همچنین Nx تعیین می کنه که چه قسمتی ازمونوریپوی شما توسط یه تغییر، تحت تأثیر قرار میگیره تا راحت بتونه فقط اون قسمت ها رو دوباره کامپایل و تست کنه.

البته که این تصمیم هم پیامدهایی داره که بهتون گفتم!

برای سهولت مسیردهی (Routing) در مایکرو فرانت اندها، بهتره برای همه ی مسیرها، نام برنامه رو به عنوان پیشوند قرار بدیم؛ در این صورت، نام برنامه ی ما میشه: کلاینت – a
 

مرحله ی دوم: از ابزارک های اشتراکی را معرفی کنید

ابزارک هایی که میخواین اون ها رو به عنوان اجزای وب/المان سفارشی به اشتراک بذارین، معرفی کنین. توجه داشته باشید که از دیدگاه مایکروسرویس ها، تا حد امکان، نباید بین مایکرو فرانت اندها، کدی به اشتراک بذارین؛ چون این کار باعث چفت شدگی یا به هم تنیده شدن میشه که دقیقاً همون چیزیه که ما میخوایم در این سبک معماری ازش دوری کنیم.

مرحله ی سوم: برنامه های تک صفحه ای (SPA) تون رو کامپایل کنید

Webpack(وب پک) و در نتیجه CLI انگولار، از یه آرایه ی سراسری برای ثبت بسته ها (bundle) استفاده می کنن. این موضوع باعث میشه تا قسمت های (تنبل/Lazy) مختلف برنامه ی شما بتونن همدیگه رو  پیدا کنن. اگه ما بخوایم چند SPA رو توی یه صفحه بارگذاری کنیم، با هم درمورد این آرایه رقابت می کنن، اوضاع رو به هم ریخته می کنن و خلاصه از کار می افتن.

برای این معضل، ما دوتا راه حل داریم:

  1. هرچیزی رو توی یه بسته بدیم؛ که در این صورت اصلاً نیازی به این آرایه ی سراسری نداریم.
  2. اسم آرایه های سراسری رو عوض کنیم.
خب اینجا من از راه حل اول استفاده می کنم؛ چون طبق تعریفی که هممون میدونیم، مایکرو فرانت اند خیلی کوچیکه و فقط یه دسته بسته (bundle) داره که در صورت تقاضا، بارگذاری رو آسون تر می کنه. همچنین میتونیم کتابخونه هایی مثل RxJS و یا خودِ انگولار رو بینشون به اشتراک بذاریم. برای رسیدن به این هدف، شما میتونین از ngx-build-plus رو با سویچ --single-bundle استفاده کنین .
کار این سوئیچ اینه که همه ی کدهاتون رو در یه بسته جا بده. با این حال، اسکریپت ها، استایل ها و پلی فیل ها، هنوز در دسته بندی خاص خودشون قرار میگیرن. دلیلشم اینه که احتمالش زیاده که پوسته بخواد این ها رو بارگذاری کنه و نیازی به بارگذاری دوباره اون توسط شما نیست.

(پلی فیل / polyfill ها، سرویس هایی هستن که در اون، کدهای خاص یه مرورگر، به کدهای قابل اجرا در مرورگر متقاضی تبدیل میشه.)

خب حالا اگه شما راه حل دوم رو ترجیح دادین، میتونین از ngx-build-plus برای اثرگذاری روی پیکربندی وب پک استفاده کنین. با استفاده از تنظیمات output.jsonpFunction، میتونین برای اون آرایه ای که بهش اشاره کردیم، اسم تعیین کنین.

مرحله ی چهارم: یه پوسته بسازید و بسته های نرم افزاری درخواستی رو اونجا بارگذاری کنید

بارگذاری بسته های مورد نیاز خیلی راحته. تنها چیزی که شما نیاز دارین، بعضی کدهای خالص  JavaScript هست که به صورت پویا یه تگ script و یه تگ برای المان اصلی برنامه ایجاد می کنن:
 
البته میتونین اینو در یه بخشنامه (Directive) هم جا بدین.

همچنین شما باید یه سری کدها رو هم برای نشون دادن یا پنهان کردن بارگذاری های درخواستی توی مایکرو فرانت اند، داشته باشین.



مرحله ی پنجم: ارتباط بین مایکرو فرانت اندها

به طور کلی، ما باید ارتباط بین مایکرو فرانت اندها رو به حداقل برسونیم؛ چون باعث به هم تنیده شدن مایکروفرانت اندها میشود.

برای برقراری این ارتباط، گزینه های مختلفی داریم. در اینجا، من از اونی که کمتر مزاحمت و دردسر ایجاد میکنه صحبت می کنم: استفاده از رشته پرس و جو(Query String). که چندتا مزیت داره:

  1. مهم نیست که مایکرو فرانت اندها رو با چه ترتیبی بارگذاری می کنین، چون بعد از بارگذاری، خودشون میتونن پارامترهای فعلی رو از url بگیرن.
  2. امکان ایجاد پیوند عمقی یا Deep Linking رو به ما میده.
  3. دقیقا مدلی هست که وب باید کار کنه!
  4. خیلی راحت پیاده سازی میشه.
برای تنظیم پارامتر UR با روتر انگولار، فقط نیازه که یه متد رو صدا بزنیم: 


 گزینه ی merge این اطمینان رو بهمون میده که پارامترهای فعلی در URL از  دست نرن. اگه از قبل یه پارامتر با ID مشابه هم داشته باشیم، روتر اون رو بازنویسی می کنه.(یعنی مقدارش میشه مقدار جدیدی که روتر بهش میده)

همچنین روتر انگولار میتونه در گوش دادن به تغییرات پارامترهای URL هم به ما کمک کنه:

البته یه سری جایگزین هایی هم داره:
  1. اگه مایکرو فرانت اندهاتون رو در کامپوننت های وب قرار بدین، میتونین از ویژگی ها و رویدادهاشون برای ارتباط برقرار کردن با پوسته استفاده کنین.
  2. پوسته میتونه در namepace (فضای نامی) سراسری، یه "گذرگاه پیام(Message Bus)" قرار بده.



  3. الآن دیگه هم پوسته و هم مایکرو فرانت اند میتونن این گذرگاه پیام رو دنبال کنن و برای رویدادهای موردعلاقه ی خودشون، گوش به زنگ باشن. همچنین هردوشون میتونن رویدادهایی رو منتشر کنن.
     
  4. از رویدادهای سفارشی که توسط مرورگر فراهم میشه، استفاده کنین:
     

مرحله ی ششم: کتابخونه ها رو بین مایکرو فرانت اندها به اشتراک بذارید

الآن که ما چندتا مایکرو فرانت اند خودگردان (Self-Contained) داریم، هرکدوم از اون ها وابستگی های خاص خودشون رو دارن مثل خود انگولار یا RxJS. از دیدگاه مایکروسرویس، این خیلی هم عالیه چون هر تیم توسعه ای که پشت هر مایکرو فرانت اند هست، این اجازه رو داره که هر کتابخونه با هر نسخه و ورژنی که میخواد رو انتخاب کنه؛ همچنین با تصمیم خودشون میتونن زمان به روز رسانی رو مشخص کنن.

درحالی که از دیدگاه راندمان و زمان بارگذاری، موقعیت بدیه چون موجب تکثیر کدیکسان در دسته ها یا همون باندل های متفاوت میشه. برای مثال، ما میتونیم به جایی برسیم که چندین نسخه ی متفاوت از انگولار در باندل های متفاوت داشته باشیم:
خوشبختانه برای این مسئله هم راه حلی هست...درسته! وب پک های خارجی.

اکسترنال ها(Externals) به ما این امکان رو میدن تا کتابخونه های معمول رو به اشتراک بذاریم. به همین دلیل این ها فقط بارگذاری میشن تا بشه از طریق namespace سراسری به اونها رجوع کنیم. در مورد بیشتر کتابخونه ها، ما میتونیم از باندل UMD استفاده کنی که دقیقاً همین کارو انجام میده و حتی یه سری کار بیشتر هم میکنه. بعد از اون، ما باید به وب پک دستور بدیم که همه ی مایکرو فرانت اندها رو با هم دسته بندی نکنه، بلکه به جای اون به namespace سراسری ارجاعش بدیم: