مقاله

درس‌آموخته‌ها در مهاجرت به میکروفرانت‌اند

لوکا مزالیرا در این ارائه، نقشه راه عملیاتی برای مهاجرت به میکروفرانت‌اند را بررسی می‌کند و توضیح می‌دهد که چگونه می‌توان برای خودمختاری تیم‌ها بهینه‌سازی کرد، مرزهای دامنه را شناسایی نمود و از مسیریابی محاسبات لبه (Edge-Compute) بهره برد.

  • ۱۴۰۵/۰۶/۱۲
  • مدیر سیستم
  • 43 دقیقه مطالعه

متن گفتار

لوکا مزالیرا: بگذارید با یک نظرسنجی سریع شروع کنم. چند نفر از شما درباره میکروفرانت‌اند (Micro-frontends) شنیده‌اید؟ چند نفر از شما در حال حاضر میکروفرانت‌اند پیاده‌سازی می‌کنید؟ در طول ده سال گذشته، زمان زیادی را صرف پیاده‌سازی میکروفرانت‌اند کرده‌ام. سپس در پنج سال اخیر، پس از پیوستن به AWS، به تیم‌های داخلی آمازون و AWS، به‌علاوه بسیاری از مشتریانی که از نیوزیلند تا دره سیلیکون با آن‌ها همکاری می‌کنم، در انتقال سیستم‌هایشان به سمت میکروفرانت‌اند کمک کرده‌ام. دلیل این امر آن است که آن‌ها متوجه شده‌اند که پیچیدگی‌هایی که روزانه با کدبیس تک‌سازه (Monolith) مواجه بودند، نمی‌توانست به آن‌ها کمک کند تا آنچه که واقعاً هنگام مهاجرت به سیستم توزیع‌شده می‌خواهند، به دست آورند. بعداً خواهیم دید که این چیست. بگذارید از اینجا شروع کنیم. در اکثر قریب به اتفاق مواردی که برای بازبینی معماری فراخوانده می‌شوم، چه معماری فعلی میکروفرانت‌اند باشد یا در حال فکر کردن برای مهاجرت به سمت آن باشند، وضعیت تقریباً به این شکل است.

آن‌ها هر سیستمی که دارند را برمی‌دارند، فرض کنیم این یک فروشگاه اینترنتی (e-commerce) خوب است. تصور کنید این یک e-commerce است. سپس می‌گویند: خب، پس آنچه را که امروز داریم برمی‌داریم و فقط همه چیز را در زمان اجرا (Runtime) بارگذاری می‌کنیم. تمام نقاط قرمز در زمان اجرا بارگذاری می‌شوند. این تقریباً همان چیزی است که بسیاری از شرکت‌هایی که با آن‌ها کار می‌کنم، هنگام شروع به فکر کردن درباره میکروفرانت‌اند در نظر می‌گیرند. متأسفانه، این روش کار نمی‌کند. زیرا در اینجا به ریشه‌های آغازین همه چیز در فرانت‌اند، بک‌اند یا هر چیز دیگری بازمی‌گردیم. حتی در فرانت‌اند، در روزهای گذشته، توسعه‌دهندگان فلش (Flash) با Flex، AIR، Flash Lite و تمام مجموعه‌های فلش کار می‌کردند. شما باید معماری را طراحی می‌کردید. این فقط برداشتن یک دسته کتابخانه، چسباندن آن‌ها به هم و فکر کردن به این است که دارید یک سیستم طراحی می‌کنید، نیست. شما فقط تصمیماتی را که دیگران درباره راه‌حل‌های خاص گرفته‌اند، پیاده‌سازی می‌کنید. این در یک سیستم توزیع‌شده کار نمی‌کند، زیرا موارد بسیار بیشتری وجود دارد که باید درباره آن‌ها فکر کنید و این دقیقاً موضوع بحث امروز ماست.

میکروفرانت‌اند چیست؟

بگذارید ابتدا با مبانی شروع کنیم، زیرا این احتمالاً بخش بیشترین سوءتفاهم در مورد میکروفرانت‌اند است. میکروفرانت‌اند در واقعیت چیست؟ میکروفرانت‌اند چقدر «میکرو» است، یا یک میکروسرویس چقدر میکرو است؟ در اکثر قریب به اتفاق موارد، سوءتفاهمی وجود دارد که میکروفرانت‌اند فقط اجزا (Components) هستند. بگذارید سعی کنیم این دکمه خوب را تحلیل کنیم. این یک دکمه درون یک برنامه بزرگ‌تر است. آنچه این دکمه در اکثر قریب به اتفاق موارد انجام می‌دهد این است که شما می‌خواهید یک دکمه درون سیستم طراحی (Design System) خود داشته باشید که امکان تنظیم یک برچسب خاص، تنظیم یک عمل خاص، داشتن یک شناسه (ID) و شاید همچنین داشتن قابلیت‌هایی را داشته باشد که یک برچسب بزرگ‌تر را تنظیم کنید، زیرا وب‌سایت شما چندزبانه است و بر اساس آن خود را تطبیق می‌دهد. شما مجموعه‌ای از قابلیت‌ها دارید و می‌خواهید این قابلیت را به گونه‌ای طراحی کنید که ظرف (Container) این دکمه مسئول تنظیم دکمه درون یک بافت (Context) خاص باشد.

اگر این دکمه برای یک فرم استفاده شود و بخواهید این دکمه را فعال یا قابل‌استفاده (Enable) کنید فقط زمانی که فیلدهای اجباری پر شده‌اند، سپس باید قابلیت‌های خاصی از این دکمه را تنظیم کنید. به طور مشابه، اگر بخواهید دقیقاً همان دکمه را در بخش دیگری از سیستم خود استفاده کنید، قادر به انجام این کار خواهید بود و دستور می‌دهید که دکمه چگونه باید رفتار کند. همان‌طور که در این مورد دیدید، بافت (Context) متعلق به دکمه نیست. دکمه در آنجا سعی می‌کند قابلیت‌ها و رابط کاربری (UI) و در این مورد، همچنین تجربه کاربری (UX) را انتزاعی کند. در میکروفرانت‌اند، ما چیزی متفاوت داریم. دیگر ظرف (Container) نیست که به دیگری دستور می‌دهد که چیز چگونه باید کار کند. در میکروفرانت‌اند، وقتی آن را بارگذاری می‌کنید، در اولین حالت، معمولاً دانه‌بندی درشت‌تری (Coarse-grained) دارند. آن‌ها بزرگ‌تر هستند. فقط یک دکمه واحد نیست. دلیلی برای این وجود دارد. خواهیم دید که آن چیست.

در واقع، خود میکروفرونت‌اند از نحوه عملکرد اجزای آن آگاه است. این امر به ما امکان می‌دهد تا میکروفرونت‌اندی کاملاً مستقل داشته باشیم. همان‌طور که مشاهده کردید، از برخی کلیدواژه‌ها برای توصیف هر یک از این موارد استفاده کرده‌ام. وقتی درباره دکمه فکر می‌کنیم، به تفکر در مورد قابلیت استفاده مجدد (Reusability) می‌پردازیم. ما می‌خواهیم از یک سیستم طراحی (Design System) یا کتابخانه کامپوننت مشترک استفاده کنیم که در همه‌جا در دسترس باشد. ما می‌خواهیم آن را برای قابلیت استفاده مجدد طراحی کنیم و داشته باشیم. ما می‌خواهیم ظاهر و حس یکسان را در داخل اپلیکیشن داشته باشیم. مشکل اینجاست که وقتی این کار را انجام می‌دهید، نشتی دامنه (Domain Leak) رخ می‌دهد، زیرا باید در چندین دامنه و چندین بخش از سیستم شما، در اختیار کانتینر قرار گیرد. کانتینر دانش مستقیمی از نحوه دستور دادن به کامپوننت برای رفتار به شیوه خاصی دارد. ما این کار را انجام می‌دهیم زیرا می‌خواهیم تکرار کد را کاهش دهیم. این هدف اصلی یک کامپوننت است.

ما همچنین می‌خواهیم این کار را انجام دهیم زیرا می‌خواهیم یکپارچگی رابط کاربری (UI Consistency) را داشته باشیم. میکروفرونت‌اند نمی‌تواند به این شکل باشد. نمی‌تواند یک کامپونتی باشد که در زمان اجرا (Runtime) بارگذاری می‌شود، زیرا آنچه ما به جای آن می‌خواهیم، بهینه‌سازی برای استقلال است. ما می‌خواهیم وابستگی‌های خارجی تیم‌هایمان را کاهش دهیم و جریان سریع (Fast Flow) را ممکن سازیم. وقتی به این موضوع فکر می‌کنیم، باید وابستگی‌های خارجی را کاهش دهیم. بنابراین، ما نمی‌خواهیم کاهش تکرار را به هر قیمتی، یا قابلیت استفاده مجدد را به هر قیمتی بپذیریم. بنابراین، شیوه‌ای که باید درباره میکروفرونت‌اند فکر کنیم باید متفاوت باشد. میکروفرونت‌اند در نهایت تفاوت‌های زیادی با کامپوننت‌ها دارد. آن‌ها تا حدی از هم فاصله می‌گیرند. زیرا وقتی برای قابلیت استفاده مجدد بهینه‌سازی می‌کنید، نوعی از جفت‌شدگی (Coupling) بالقوه بین کانتینر و کامپوننت‌ها ایجاد می‌کنید. اکنون، همه از یکدیگر آگاه هستند، هرچند کامپوننت‌ها کمتر از کانتینر. میکروفرونت‌اند به این معناست که برای اینکه به صورت مستقل قابل استقرار (Deploy) باشد، باید خودکفا (Self-contained) باشد. بنابراین، ما باید به شیوه‌ای متفاوت عمل کنیم و چیزها را طراحی کنیم.

مسیر مهاجرت - درس‌هایی از صدها تیم

همان‌طور که گفتم، این سخنرانی بخش مهاجرت میکروفرونت‌اند را پوشش می‌دهد. من روزها و سال‌های بی‌شماری را صرف بررسی پیاده‌سازی‌های مختلف کرده‌ام و در این سخنرانی، اساساً آنچه که کار می‌کند، آنچه که کار نمی‌کند و چرا، را جمع‌آوری کرده‌ام. در اکثریت قاطع موارد، پس از دیدن آن دیاگرام زیبا از فروشگاه اینترنتی (E-commerce) که همه چیز در زمان اجرا بارگذاری می‌شود، گام دوم در مکالمه این است: «خب، پس لوکا، ما تصمیم گرفتیم از Single-SPA، یا Next.js multi-zone، یا از Web Components، یا از Module Federation استفاده کنیم؛ مجموعه‌ای از کتابخانه‌ها/پیاده‌سازی‌های فنی که در این مرحله از فرآیند، هیچ معنایی ندارد. این بخش مرتبط است، اما نه در این مرحله از مکالمه. در این مرحله، ما حتی نمی‌دانیم که چگونه این سیستم را تقسیم خواهیم کرد. زیرا اگر به میکروفرونت‌اند فکر کنید، آنچه می‌خواهیم به دست آوریم، تیم‌های مستقلی هستند که با سرعت خودشان استقرار می‌کنند و می‌خواهند بخشی از سیستم را داشته باشند، فقط بخشی از سیستم.

سؤال واقعی که باید از خود بپرسیم این است: ما می‌خواهیم چه چیزی را به دست آوریم؟ فکر می‌کنید که این را می‌دانید، اما به من اعتماد کنید، همه این‌طور نیستند. زیرا سیستم‌های توزیع‌شده (Distributed Systems) متفاوت هستند. اینجا دیگر آن حالتی نیست که همه دست‌ها روی تخته باشند و شروع به نوشتن کد کنند و این هیولای ۲۵,۰۰۰ ویو را ایجاد کنند که همه چیز جفت‌شده است و استقرار آن دشوار است، ما باید همه چیز را با هم استقرار دهیم و غیره. اخیراً، برای مثال، در ایتالیا با مشتریای بودم که چیزی حدود ۹۰۰ ویو از یک اپلیکیشن وب دارد که باید از Vaadin مهاجرت کند، که یک فریمورک قدیمی جاوا و HTML است، اما یک فریمورک جاوا، که هیچ API یا چیزی نداشت. آن‌ها فقط نیاز داشتند از Vaadin مهاجرت کنند، فکر می‌کنم نسخه ۳ بود، اکنون نسخه ۲۲ هستند. همان‌طور که می‌توانید تصور کنید، در واقعیت انحراف زیادی وجود دارد. سؤال واقعی این است که آن‌ها هرگز از خود نمی‌پرسند: ما می‌خواهیم چه چیزی را به دست آوریم؟

آن‌ها همچنان ویژگی‌های جدیدی را به این سیستم اضافه می‌کنند که به‌سرعت بسیار پیچیده شده است. اولین نکته این است که وقتی درباره سیستم‌های توزیع‌شده (Distributed Systems) فکر می‌کنیم، آنچه مردم، به‌ویژه مدیران ارشد (C-suite)، واقعاً از آن می‌خواهند، خودمختاری تیم‌ها (Teams' Autonomy) است، زیرا خسته شده‌اند که وابستگی‌های خارجی بی‌شماری وجود دارد و تیم‌هایی می‌گویند: «خیر، ما نمی‌توانیم این ویژگی را تحویل دهیم، زیرا باید منتظر بمانیم تا تیم دیگری بخش خاصی از سیستم را تکمیل کند» و موارد مشابه. آن‌ها می‌خواهند خودمختاری تیم‌ها را داشته باشند. آن‌ها می‌خواهند تیم‌های مستقلی داشته باشند که به‌صورت هم‌زمان محصولات را عرضه می‌کنند. آن‌ها به مقیاس‌پذیری (Scalability) نیاز دارند. مقیاس‌پذیری را در چارچوب «ما باید به ابر (Cloud) مهاجرت کنیم و بنابراین مقیاس‌پذیری خواهیم داشت، زیرا در آنجا می‌توانیم در هر نقطه‌ای که لازم باشد به‌صورت افقی، عمودی و... مقیاس‌پذیر شویم» در نظر نگیرید. ما به مقیاس‌پذیری سازمانی نیاز داریم که بسیار دشوارتر است. در اینجا افراد درگیر هستند.

مسئله با ماشین‌ها نیست، بلکه با انسان‌هاست. ما باید مقیاس‌پذیری را به این صورت در نظر بگیریم که بتوانم یک تیم جدید را مانند یک ماژول درون سازمان خود برای توسعه بخش خاصی از سیستم قرار دهم، بدون اینکه بر ظرفیت پردازش (Throughput) سایر تیم‌ها تأثیر بگذارد. سپس باید درباره چابکی (Agility) فکر کنیم. احتمالاً همه شما به یاد دارید چه اتفاقی در دوران همه‌گیری (Pandemic) افتاد. یک‌شبه کسب‌وکارهایی بودند که در کمتر از یک هفته مجبور شدند مدل کسب‌وکار خود را تغییر دهند. این اتفاق یک‌بار در عمر رخ نداد. اکنون با بسیاری از موارد دیگر که خارج از کسب‌وکار ما هستند، شاهد تأثیر این موارد بر ظهور هوش مصنوعی (AI) یا دموکراتیزه شدن هوش مصنوعی هستیم. ما باید تغییر کنیم و بازبینی کنیم که چگونه درباره نحوه وقوع رویدادها فکر می‌کردیم. بنابراین، اگر ماژولاریت (Modularity) را در سیستم خود اعمال نکنید، کار دشوار می‌شود، زیرا شما یک سیستم بسیار سفت و سخت دارید، در حالی که دنیای بیرون به‌سرعت در جهت‌های مختلف حرکت می‌کند.

سپس می‌خواهیم به جریان سریع (Fast Flow) برسیم، زیرا واقعیتی که کشف کرده‌ایم این است که اگر بتوانیم با فراوانی بیشتر در محیط تولید (Production) منتشر کنیم، می‌توانیم بازخورد بیشتری از کاربران به دست آوریم. می‌توانیم چیزها را بهتر طراحی کنیم. می‌توانیم سیستم بهتری داشته باشیم که با تمام موارد دیگری که در آنجا دیده‌ایم مطابقت دارد. آیا چیزی در اینجا توجه شما را جلب می‌کند؟ من در حال صحبت درباره استفاده از React برای انجام کارهای خاص، یا استفاده از Angular و موارد مشابه هستم. هیچ چیز فنی در کار نیست، زیرا در واقعیت آنچه ما روزانه انجام می‌دهیم، توانمندسازی کسب‌وکار و مشتریان و کاربران ما برای موفقیت در وظایف آن‌هاست. بنابراین، این ویژگی‌ها باید هنگام طراحی هر چیزی، چه فرانت‌اند (Frontend)، چه بک‌اند (Backend)، یا هر نام دیگری، در نظر گرفته شوند. زیرا اگر این کار را نکنید، نباید متعجب باشید که در ۶ ماه آینده، به‌جای ۳ سال، با هوش مصنوعی مولد (GenAI) به کدهای اسپاگتی (Spaghetti Code) برسید. این یک مشکل با GenAI نیست. مشکل این است که نمی‌دانید چه چیزی را می‌خواهید حل کنید.

زیرا این سه حوزه از یک سیستم اجتماعی-فنی (Socio-technical System) همیشه به هم مرتبط هستند: معماری (Architecture)، سازمان (Organization) و فرهنگ مهندسی (Engineering Culture). در برخی موقعیت‌ها فکر می‌کنیم که فقط یک تصمیم طراحی (Design Decision) می‌گیریم. واقعیت این است که ما به هر سه حوزه دست می‌زنیم. یک مثال می‌زنم. در ابتدا، زمانی که در سال ۲۰۱۷ شروع به صحبت درباره میکروفرانت‌اند (Micro-frontends) کردم، تقریباً در هر کنفرانسی که درباره آن‌ها صحبت می‌کردم، سؤال به این صورت بود: «آیا باید از مونورپو (Monorepo) یا پلی‌رپو (Polyrepo) استفاده کنیم؟» همیشه این سؤال مطرح بود. پاسخ من «بستگی دارد» است. هر معمار سیستمی این را می‌گوید. دلیل قوی‌ای وجود دارد که چرا می‌گویم «بستگی دارد». زیرا در لحظه‌ای که یکی از این دو استراتژی را انتخاب می‌کنید، در واقع فرهنگ مهندسی خود را و به‌احتمال زیاد سازمان خود را نیز طراحی می‌کنید. تصور کنید در محیطی یا بافتی قرار دارید که مهندسان ارشد (Senior Engineers) زیادی ندارید، شاید چند نفر از آن‌ها هستند که بار اصلی را به دوش می‌کشند.

فرض کنید از رویکرد مونورپو (Monorepo) استفاده کنیم. در حالت مونورپو، در اکثر موارد از توسعه مبتنی بر تنک (Trunk-based development) بهره می‌برید. شاخه‌ها (Branches) باید عمر بسیار کوتاهی داشته باشند و سرعت کار بالا باشد. باید مطمئن شوید که شاخه اصلی (Master) همیشه فعال و بدون خطا است، زیرا در غیر این صورت، همه را تحت تأثیر قرار می‌دهید. همچنین، شما به انضباط توسعه‌دهندگان متکی هستید. بنابراین، اگر بگویید که در این پوشه مشترک فقط کدها و اجزایی را به اشتراک می‌گذاریم که واقعاً نیاز به اشتراک‌گذاری دارند، سعی می‌کنید به تیم‌ها اعتماد کنید که کار درست را انجام دهند. سپس واقعیت وارد می‌شود. ممکن است ویژگی‌ای (Feature) وجود داشته باشد که باید تا دیروز تحویل داده شود. بنابراین، آسان‌تر است که این کد را درون کتابخانه مشترک (Shared library) قرار دهیم. مشکل اینجاست که وقتی می‌خواهید این مسائل را حل کنید، احتمالاً شش ماه، اگر خوش‌شانس باشید، یا چند سال دیگر طول می‌کشد.

زیرا در آن زمان شروع به رشد می‌کنید. درهایی را باز می‌کنید که بسیار پیچیده می‌شوند. مونورپو می‌تواند بسیار مفید باشد، به‌ویژه زمانی که با توسعه‌دهندگانی کار می‌کنید که سطح ارشدیت (Seniority) مشخصی دارند. آن‌ها قادر به مدیریت این نوع پیچیدگی هستند. این امر درون رویه‌های مهندسی آن‌ها نهادینه شده است. تصمیم ساده‌ای مانند انتخاب بین پلی‌رپو (Polyrepo) و مونورپو که ظاهراً فقط جنبه فنی دارد، بر هر سه بُعد تأثیر می‌گذارد. نحوه ساختاردهی تیم‌ها تا حدی تحت تأثیر قرار می‌گیرد. بسیار مهم است که هنگام اتخاذ تصمیم، در نظر داشته باشید که این سه مورد به هم مرتبط هستند. تصمیمی که یک تصمیم معماری است، می‌تواند بر دو مورد دیگر نیز تأثیر بگذارد. وقتی می‌گویید که از سیستم‌های توزیع‌شده (Distributed systems) استفاده می‌کنیم، اما سازمان نمی‌تواند همان‌طور که هست باقی بماند اگر بیست سال است که یک مونولیت (Monolith) دارید. معماری باید امکان‌ساز تصمیماتی باشد که از پیش برای سازمان اتخاذ می‌کنید؛ بنابراین، اگر تصمیم به حرکت به سمت سیستم توزیع‌شده گرفتید، تصمیم به غیرمتمرکزسازی (Decentralize) گرفته‌اید. بنابراین، دیگر برج عاجی وجود ندارد. باید به‌تدریج اما با ثبات، چندین چیز را غیرمتمرکز کنید. رویکردی فدرال‌تر (Federated) در تصمیم‌گیری‌ها وجود خواهد داشت. با این حال، این موارد اهمیت دارند. فکر نکنید که این فقط یک تصمیم فنی است.

در واقعیت، بافت (Context) پادشاه است. چگونه می‌توانید این تصمیم را بگیرید؟ سپس، بسیار اغلب، به روزهای گذشته فکر می‌کنم، زمانی که شتابی به سمت مدل اسپاتیفای (Spotify model) وجود داشت. آیا تا به حال از مدل اسپاتیفای شنیده‌اید؟ ما به گیلدها (Guilds) نیاز داریم. ما به هر تیمی نیاز داریم که به‌صورت مستقل غیرمتمرکز باشد و تصمیمات خود را بگیرد، و تمام رویه‌هایی که در آنجا توصیف شده بود. چند سال بعد، با تعجب، مقاله‌ای منتشر شد که می‌گفت ما حتی این مدل اسپاتیفای را در اسپاتیفای پیاده‌سازی نکرده‌ایم. آیا شما اسپاتیفای هستید؟ آیا در اسپاتیفای کار می‌کنید؟ شاید شما هم همین‌طور باشید. در آنجا، فرهنگ مهندسی خاصی وجود دارد. هدف خاصی وجود دارد. هدف مشخصی وجود دارد. بسیار اغلب، فراموش می‌کنیم که برخی از تصمیمات فنی که در گفتگویی مانند این گرفته شده‌اند، ممکن است برای بافت شما مفید نباشند. ابتدا باید بافت خود را درک کنید. حتی وقتی اکنون درباره هوش مصنوعی صحبت می‌کنیم، درباره مهندسی بافت (Context engineering) صحبت می‌کنیم.

مهندسی بافت، نه فقط مهندسی، نه فقط بهترین رویه. زیرا اگر بافت را به هوش مصنوعی مولد (Generative AI)، دستیار کد خود، یا هرچه که باشد، ارائه ندهید، شگفت‌زده نشوید اگر آنچه را که به دنبال آن هستید تولید نکند. نکته دیگری که می‌خواهم برجسته کنم این است که میکروفرانت‌اند (Micro-frontends) قطعاً یک گلوله نقره‌ای (Silver bullet) نیست. منظور من این است که فقط زمانی باید از آن استفاده شود که به سیستم توزیع‌شده نیاز دارید. حتی اگر در بک‌اند (Backend) سیستم توزیع‌شده داشته باشید، ممکن است به میکروفرانت‌اند نیاز نداشته باشید. این نکته بسیار مهم است. فکر نکنید که همه چیزهایی که تاکنون با مونولیت قدیمی و خوبمان انجام داده‌ایم باید دور ریخته شود. این هیاهو (Hype) نیست. این معماری است. معماری بیان‌کننده ویژگی‌های خاصی است. یا باید ویژگی‌ها را از پیش مشخص کنید، در غیر این صورت، انتخاب یک معماری خاص دشوار است.

گام ۱: چارچوب تصمیم‌گیری برای میکروفرانت‌اند

بیایید در این مسیر مهاجرت آغاز کنیم. در سال ۲۰۱۹، من در نروژ بودم. من در حال ارائه یک کارگاه درباره میکروفرانت‌اند (Micro-frontends) بودم. اصطلاح میکروفرانت‌اند در سال ۲۰۱۶ مطرح شد. سال ۲۰۱۹ هنوز در روزهای اولیه بود. شما باید راهی برای ایجاد مدل‌های ذهنی (Mental Models) برای افراد پیدا کنید تا بتوانند به‌درستی به طراحی معماری بپردازند. من واژگان مربوط به معماری میکروفرانت‌اند را ایجاد کردم. سپس شروع به ایجاد الگوها یا مدل‌های ذهنی بهتر کردم تا به افراد کمک کنم تا بهتر آن را درک کنند. در سال ۲۰۱۹، در طول این کارگاه، یک ایده به ذهنم رسید. به یاد می‌آورم که این ایده را در یک دستمال کاغذی در میان‌وعده نوشتم، زیرا خلاقانه بود. آن چارچوب تصمیم‌گیری (Decision Framework) بود. زیرا من چندین سخنرانی درباره میکروفرانت‌اند ارائه می‌دادم. چند نکته وجود دارد که بارها و بارها تکرار می‌شوند و من آن‌ها را در رندر سمت سرور (Server-side Rendering)، لبه (Edge-side) و سمت کلاینت (Client-side) آزمایش کردم. همیشه یک چیز است. معمولاً در میکروفرانت‌اند، چند مورد وجود دارد.

در اولین نگاه، شما یا یک پوسته اپلیکیشن (Application Shell) یا یک ترکیب‌ساز رابط کاربری (UI Composer) دارید. تفاوت چیست؟ پوسته اپلیکیشن معمولاً روشی است که شما با آن میکروفرانت‌اند‌ها را در یک راه‌حل رندر سمت کلاینت توصیف می‌کنید. در حالی که یک ترکیب‌ساز رابط کاربری ممکن است در یک راه‌حل رندر سمت سرور موجود باشد یا نباشد. زیرا ترکیب‌ساز فقط تکه‌های رندر سمت سرور را ترکیب می‌کند که سپس ترکیب شده و به مرورگر ارسال می‌شوند. یک پوسته اپلیکیشن منطق و موارد بسیار بیشتری را که باید مدیریت کند، دارد. در اکثر موارد، در سطح پوسته اپلیکیشن، شما پیکربندی (Configuration)، مجوزدهی (Authorization)، ترکیب (Composition) و مسیریابی (Routing) را مدیریت می‌کنید. این‌ها چهار جنبه‌ای هستند که اهمیت دارند. یک پوسته اپلیکیشن خوب، آن است که بتواند خارج از سیستم شما، سیستم فعلی که دارید، استفاده شود. باید مستقل از دامنه (Domain Agnostic) باشد. نباید هر بار که یک میکروفرانت‌اند تغییر می‌کند، ارسال شود. زیرا در غیر این صورت، دامنه را خارج از سیستم نشت می‌دهید.

معمولاً دو نوع میکروفرانت‌اند دارید. شما یک عمودی (Vertical) و یک افقی (Horizontal) دارید. باید آن‌ها را شناسایی کنید. یک میکروفرانت‌اند چقدر کوچک است؟ ما به همان سوال برمی‌گردیم. واقعیت این است که این‌ها به‌طور متقابل انحصاری نیستند. در ابتدای یک سیستم، ممکن است متوجه شوید که داشتن میکروفرانت‌اند‌های درشت‌تر (Coarse-grained) مشکلی ندارد. در حالی که وقتی به سمت بلوغ بیشتر حرکت می‌کنید و دامنه و موارد در حال تغییر هستند، ممکن است به سمت دانه‌بندی ریزتر (Granular) بروید. این‌ها قطعاً دو رویکرد هستند. عمودی به این معنی است که شما یک نمای یا گروهی از نمای‌ها دارید که یک میکروفرانت‌اند را نشان می‌دهند. افقی به این معنی است که شما یک میکروفرانت‌اند دارید که در کنار سایر میکروفرانت‌اند‌ها زندگی می‌کند، مانند این مورد که اینجا برجسته می‌کنم، در همان نمای. این تفاوت بین این دو است. یکی درشت‌تر است. دیگری ریزتر است، اما نه آنقدر ریز مانند یک کامپوننت.

تصمیم دیگر که باید بگیرید این است که چگونه این موارد را رندر می‌کنم، روی سرور یا روی کلاینت. این بسیار مهم است زیرا همه چیز را تغییر می‌دهد. من چند معماری رایج را که دیده‌ام به شما نشان خواهم داد. البته، موارد خاص (Edge Cases) بر روی آن‌ها وجود دارد. این‌ها بسیار کلیدی هستند. در اکثر موارد، اگر تجارت الکترونیک (E-commerce) انجام می‌دهید، رندر سمت سرور را انتخاب خواهید کرد. شما باید بهترین عملکرد را داشته باشید زیرا هر میلی‌ثانیه اهمیت دارد. رندر سمت کلاینت همه چیزهایی است که، شاید شما یک اپلیکاسیون دارید که پشت احراز هویت (Authentication) اجرا می‌شود، شما اهمیت نمی‌دهید به یک SKU ارگانیک. بنابراین، رندر سمت سرور می‌تواند عالی باشد. یا شما یک راه‌حلی دارید که داخل دستگاه‌های خاص اجرا می‌شود. من برای مدت طولانی روی دستگاه‌های نهفته (Embedded Devices) کار کرده‌ام، بنابراین، رندر سمت کلاینت برای من نان و کره بود. چارچوب‌های (Frameworks) مختلفی وجود دارد. در این مرحله، می‌توانیم شروع به بحث درباره چارچوب‌ها کنیم.

اگر تصمیم بگیرید از رندر سمت کلاینت (Client-Side Rendering) استفاده کنید، رایج‌ترین گزینه‌ها، بر اساس تجربه من، در حال حاضر Module Federation یا Single-SPA هستند. این دو چارچوب اصلی‌ای هستند که بیشتر از سایرین دیده‌ام. همچنین Web Components نیز وجود دارند. Native Federation، اگر از Angular استفاده می‌کنید. و Piral، که یک راه‌حل عالی است و هم برای رندر سمت کلاینت و هم سمت سرور کار می‌کند. این ابزار بسیاری از قابلیت‌هایی را که در اینجا به عنوان یک پلتفرم توصیف می‌کنم، فراهم می‌آورد. این کاملاً متن‌باز است. حتماً آن را بررسی کنید. در مورد رندر سمت سرور (Server-Side Rendering)، هنوز می‌توانید از Module Federation با محدودیت‌های خاصی استفاده کنید. در حال حاضر، چارچوب‌های مدرن مانند Next.js و Astro.js، و همچنین Web Fragments که اخیراً توسط Cloudflare ارائه شده‌اند، رویکردی داخلی‌تر (Built-in) ارائه می‌دهند. برای مثال، Next.js مفهوم multi-zone را دارد که می‌توانید از آن برای ایجاد Micro-Frontends استفاده کنید، بنابراین برنامه شما را به چندین بخش (Chunk) تقسیم می‌کند. بعداً خواهیم دید که چگونه. این یک رویکرد است.

Astro.js، برای مثال، دارای Astro Server Islands است که به شما امکان می‌دهد بخشی از رابط کاربری (UI) شما را به صورت کاملاً مستقل رندر کنید. در همان نمای (View)، می‌توانید بخشی را داشته باشید که کاملاً کش شده و در سطح CDN رندر و کش شود، و بخشی دیگر که می‌تواند چندین بار به‌روزرسانی شود. شما اساساً به این سرور آیلند (Server Island) متکی هستید که روی سرور می‌تواند UI را بدون تازه کردن کل صفحه به‌روزرسانی کند. این بسیار کاربردی است. سپس باید در مورد اینکه چگونه بین Micro-Frontends مسیریابی (Routing) انجام می‌شود، فکر کنید. چگونه می‌توانم از صفحه اصلی به کاتالوگ بروم. در اکثر موارد، بخش مسیریابی همراه با استراتژی رندری است که می‌خواهید استفاده کنید. اگر رندر سمت کلاینت باشد، مسیریابی‌ای خواهید داشت که داخل پوسته برنامه (Application Shell) زندگی می‌کند. اگر رندر سمت سرور باشد، مسیریابی‌ای خواهید داشت که می‌تواند در لبه (Edge) یا در یک بار均衡‌کننده بار برنامه (Application Load Balancer)، دروازه API (API Gateway)، یا هر جایی که در سمت سرور تصمیم بگیرید، رخ دهد.

سپس باید در مورد نحوه ارتباط فکر کنید. وقتی یک تقسیم عمودی (Vertical Split) دارید، کار آسان است. شما یک گروه از نماها را دارید که همه با هم زندگی می‌کنند. در اکثر موارد، ارتباط نسبتاً آسان است زیرا در دامنه یکسان قرار دارد. از طرف دیگر، وقتی چندین Micro-Frontend در یک نمای واحد دارید، باید در مورد نحوه ارتباط فکر کنید. معمولاً سه گزینه رایج، رویدادهای سفارشی (Custom Events)، Event Emitter و جریان‌های واکنش‌گرا (Reactive Streams) هستند، با ترجیح قوی برای Event Emitter. دلیل اصلی این است که ساده‌ترین گزینه است. این گسسته‌سازی (Loose Coupling) را فراهم می‌کند. همچنین، به شما امکان می‌دهد رویدادهای ورودی و خروجی Micro-Frontendهای خاص را تعریف کنید. همه می‌توانند به آن‌ها متصل شوند. نیازی به داشتن یک وضعیت سراسری (Global State) پیچیده ندارید، مانند کاری که بسیاری از تیم‌ها انجام داده‌اند. سپس وقتی تصمیم اشتباهی گرفته‌اند، از معماری سرزنش می‌کنند.

گام ۲: معماری‌های رایج

معماری‌های رایجی که در حال حاضر دیده‌ام. اولین مورد، رندر سمت کلاینت است. رندر سمت کلاینت بسیار ساده است. در هر محیطی، اعم از On-prem یا ارائه‌دهنده ابری که استفاده می‌کنید، معمولاً یک CDN دارید که آرتیفکت‌های ایستا (Static Artifacts) شما را کش می‌کند، زیرا در نهایت، اگر به یک تک‌صفحه‌ای (Single-Page Application) فکر کنید، مجموعه‌ای از فایل‌های JavaScript، CSS و HTML است. چیزی بیشتر از این نیست. این به شدت قابل کش است و بنابراین می‌توانید فایل‌های ایستا خود را در ذخیره‌سازی اشیاء (Object Storage)، ذخیره‌سازی Blob یا هر نام دیگری که بخواهید، ذخیره کنید. لطفاً از یک کانتینر برای سرو فایل‌های ایستا استفاده نکنید مگر اینکه کاملاً ضروری باشد. زیرا بسیار زیاد دیده‌ام که توسعه‌دهندگان به من می‌گویند، ما از Kubernetes برای سرو فایل‌های ایستا استفاده می‌کنیم. این کار را نکنید. شما نیاز به حفظ یک وضعیت امنیتی و پیچیدگی دارید که لازم نیست. در حال حاضر، یک CDN، در مورد AWS، برای مثال، CloudFront با S3، یک ذخیره‌سازی اشیاء است. می‌توانید به شدت مقیاس‌پذیر باشید.

در شرکت قبلی من، ما در حال مقیاس‌پذیری از ۱۰,۰۰۰ کاربر به ۲ میلیون کاربر در یک دقیقه درون پلتفرم بودیم. ما همه چیز را به‌خوبی ارائه می‌دادیم. هرگز مشکلی در ارائه فرانت‌اند نداشتیم و بسیار نادر است که مشکلی در بک‌اند پیش بیاید. به من اعتماد کنید، این موارد به‌خوبی مقیاس‌پذیر هستند. اساساً استقرار (Deployment) شما، ارسال یک چانک (Chunk) خاص جاوااسکریپت به Blob Storage یا Object Storage است. این همان استقرار شماست. مدیریت آن بسیار آسان است و قابلیت کش‌پذیری بالایی دارد. در مورد رندر سمت سرور (Server-Side Rendering)، ما هنوز از CDN استفاده می‌کنیم، زیرا بسیاری از افراد، برای مثال، فکر می‌کنند که CDN فقط برای فایل‌های استاتیک مفید است. اصلاً این‌طور نیست. CDN در حال حاضر امکانات بسیار بیشتری به شما ارائه می‌دهد. شما Compute در لبه (Edge) دارید. شما امنیت دارید، بنابراین می‌توانید کارهای بسیار بیشتری انجام دهید. در اکثریت قاطع موارد، اگر به تجارت الکترونیک فکر کنید، من چندین مشتری در این حوزه را دنبال می‌کنم و معمولاً آن‌ها در سطح URL اولیه تقسیم‌بندی می‌شوند. شما می‌توانید یک Micro-Frontend برای صفحه اصلی داشته باشید.

شما می‌توانید یک Micro-Frontend برای محصولات داشته باشید که شامل شناسه محصول (Product ID) نیز باشد. می‌توانید My Account داشته باشید. می‌توانید Checkout داشته باشید و به همین ترتیب. این امر بسیار رایج است. سپس نحوه ارائه آن، می‌تواند API Gateway با یک کانتینر باشد، می‌تواند Application Load Balancer با کانتینر باشد، کاملاً به عهده شماست. این‌گونه تقسیم‌بندی می‌شود. نکته جالب اینجاست که اگر، برای مثال، بخش محصولات از دسترس خارج شود، بر هیچ چیز دیگری تأثیری نمی‌گذارد. اگر من در تیم محصولات باشم، می‌توانم هر چند بار که بخواهم در روز ریلیز کنم و هیچ‌کس تحت تأثیر قرار نخواهد گرفت. می‌توانم کارهایم را تست کنم و همه چیز به‌خوبی کار خواهد کرد. این مانند رویکرد کلاسیک رندر سمت سرور است که خواهید دید، اما به جای داشتن یک چیز واحد، شما چندین کانتینر خواهید داشت. اگر به formula1.com بروید، یکی از مشتریانی که چند سال پیش با آن‌ها کار کردم، از این رویکرد استفاده می‌کند. ما در re:Invent در وگاس یک سخنرانی داشتیم که معمار ارشد (Senior Architect) با من روی صحنه آمد و دقیقاً درباره همین موضوع صحبت کرد. اگر بخواهید، می‌توانید آن را در یوتیوب پیدا کنید.

رویکرد دیگر که من برای مشتریان پیشرفته‌تر دیده‌ام، این است. همان رویکرد قبلی، اما آن‌ها صفحات را در زمان اجرا (Runtime) بارگذاری می‌کنند. اگر به Next.js، Astro.js یا هر فریمورک رندر سمت سرور امروزی فکر کنید، آنچه انجام می‌دهد، تقسیم سیستم به صفحات است. شما می‌توانید صفحه را در زمان اجرا بارگذاری کنید، بنابراین مجبور نیستم هر بار زیرساخت خود را استقرار دهم. به‌طور دقیق، فایل جاوااسکریپت و Micro-Frontend خود را در Object Storage قرار می‌دهم، مانند قبل، و این همان استقرار من است. بقیه سیستم همیشه روشن و در حال اجرا خواهد بود. من به آن دست نمی‌زنم. این نسبتاً رایج است و آن را با مشتریان پیشرفته‌تر دیده‌ام. سپس اگر از فریمورک‌های جدیدتر استفاده می‌کنید، روشی پیچیده‌تر برای انجام این کار وجود دارد. این مورد، برای مثال، یک نام بسیار شناخته‌شده در صنعت به نام BBC است. این یک پخش‌کننده در بریتانیا است و به‌طور کلی بسیار شناخته‌شده است.

آنچه آن‌ها استفاده می‌کنند، سطوح مختلف کش (Cache) است. این یک ساده‌سازی است، البته، اما می‌توانید سخنرانی مربوط به آن را در اینترنت پیدا کنید. آنچه آن‌ها انجام می‌دهند، اساساً داشتن یک Composer است که از سرویس‌های مختلف، قطعات HTML را بازیابی کرده و همه را با هم ترکیب می‌کند. برخی از آن‌ها کش می‌شوند تا فشاری به وابستگی‌های بالادستی (Upstream Dependencies) وارد نشود و بنابراین آن‌ها از کش بازیابی می‌شوند. می‌تواند به سادگی یک کش Redis باشد و سپس به مرورگر ارائه شود. این کمی پیچیده‌تر است، اما بسیار خوب ارائه می‌شود. پیاده‌سازی این، اساساً BBC News است، بنابراین احتمالاً مشابه چیزی است که شما اینجا در CNN دارید. اگر به میزان ترافیکی که آن‌ها مدیریت می‌کنند فکر کنید، این‌گونه آن را مدیریت می‌کنند. چه خبر از Edge؟ من درباره Edge صحبت نکردم، زیرا Edge، تا امروز، توسط سازمان‌های بزرگ در سراسر جهان به‌خوبی استفاده نمی‌شود. فکر می‌کنم فقط یکی را در حوزه بیمه پیدا کردم که این کار را انجام داد، زیرا آن‌ها داده‌ها را با رویکرد چندمنطقه‌ای (Multi-region) تکرار می‌کنند.

خدمات آن‌ها در مناطق متعدد و بخش‌های مختلف جهان در دسترس است. سایر موارد، اگر رندر سمت سرور (Server-Side Rendering) را در لبه (Edge) اجرا می‌کنید و داده‌های شما در یک منطقه یا یک مرکز داده واحد باقی می‌مانند، سود چندانی نمی‌برید. تجربه توسعه‌دهنده نیز ضعیف‌تر می‌شود. قطعاً می‌توانید همه کارها را درون منطقه و با بهره‌گیری از CDN انجام دهید، نه برای رندر، بلکه برای کش کردن (Caching) محتوا، و این قطعاً راهکاری است که بسیار خوب عمل خواهد کرد.

گام ۳: شناسایی میکروفرانت‌اند‌ها

گام سوم، شناسایی میکروفرانت‌اند‌ها است. ما درک کرده‌ایم که همه چیز چگونه کار می‌کند. پیشنهاد من به هر تیمی این است که ساده شروع کنید. با یک تقسیم عمودی (Vertical Split) آغاز کنید. شما یک نمای واحد یا گروهی از نماها را دارید که یک میکروفرانت‌اند را تشکیل می‌دهند. پیچیدگی کمتر است. تقسیم‌بندی آسان‌تر است، زیرا اگر به این موضوع فکر کنید، یک وب‌سایت را در نظر بگیرید، به URLهای سطح اول نگاه می‌کنید، نحوه استفاده کاربران از وب‌سایت خود را بررسی می‌کنید و یک تصمیم می‌گیرید. ساخت و استقرار سریع‌تر است، زیرا در واقعیت، اگر عادت به کار با رویکرد رندر سمت سرور یا رندر سمت کلاینت دارید، این اساساً بخشی از کاری است که از قبل انجام می‌دهید و فقط باید آن را از بقیه جدا و تقسیم کنید. همین. بسیار آسان است. بازگشت (Rollback) نیز آسان‌تر است. این جنبه مهم دیگری است که بیشتر درباره آن بحث خواهیم کرد. یک نکته‌ای که باید درباره نحوه طراحی و اینکه چقدر یک میکروفرانت‌اند «میکرو» است، فکر کنید، این است: یکپارچه‌سازها در مقابل تجزیه‌سازها (Integrators versus Disintegrators).

برخی معیارها یا قواعد سرانگشتی (Heuristics) وجود دارد که به شما کمک می‌کنند درک کنید آیا می‌خواهید کدها را در کنار هم قرار دهید (Co-locate) یا خیر. برای مثال، نوسان کد (Code Volatility). چقدر کد خود را تغییر می‌دهید؟ اگر مثلاً سیستمی دارید که از پیش می‌دانید ۱۰ روش پرداخت در سیستم شما وجود دارد، اما ۹ مورد از آن‌ها به ندرت تغییر می‌کنند. شما فقط این SDK را راه‌اندازی می‌کنید. SDK به ندرت، مثلاً هر سال یک‌بار، تغییر می‌کند، اما یک روش پرداخت وجود دارد که بسیار مکرر تغییر می‌کند و احتمالاً می‌توانید آن را در دو میکروفرانت‌اند تقسیم کنید، نه در ۱۰ مورد، همان‌طور که بسیار زیاد دیده‌ام. زیرا واقعیت این است که در ابتدا، کار با دانه‌بندی درشت‌تر (Coarse-grained) آسان‌تر است و سپس در صورت نیاز تصمیم به تقسیم‌بندی می‌گیرید. الان مثل این است که من مدت طولانی توسعه‌دهنده بودم. من عاشق انجام انتزاع (Abstraction) هستم. توسعه‌دهندگان عاشق انجام انتزاع هستند زیرا خنک است. می‌توانم شروع به ایجاد یک کتابخانه کنم و شاید از آن استفاده مجدد کنم و تیم‌های من همه‌جا از آن استفاده کنند. در اکثریت قاطع موارد، کتابخانه شما در کل چرخه حیات یک سیستم فقط دو بار استفاده می‌شود. متأسفم بابت این موضوع، اما واقعیت همین است. من وقتی بزرگ‌تر شدم (نمی‌توانم بگویم خردمندتر شدم) یاد گرفتم که این کارها ارزشش را ندارد.

نکته دیگر که باید درباره آن فکر کنید، این است که می‌خواهم یک مثال برایتان بزنم. این نحوه تفکر من درباره سیستمم بود. من در شرکتی به نام DAZN کار می‌کردم که یک پلتفرم استریمینگ ورزش زنده مانند نتفلیکس است، اما با تمرکز بر ورزش زنده. این‌ها عمدتاً بخش‌های اصلی بودند که برنامه تک‌صفحه‌ای (SPA) ما از آن‌ها تشکیل شده بود. وقتی شروع به بررسی ترافیک و نحوه استفاده کاربران از سیستم ما کردم، کشف کردم که صفحه فرود (Landing Page) میلیون‌ها درخواست داشت. سپس یک افت رخ داد زیرا افراد از پیشنهاد ارائه‌شده، هرچه بود، قانع نشده بودند. سپس شروع به فکر کردن کردم، بنابراین صفحه فرود می‌تواند یک میکروفرانت‌اند باشد. سپس شروع گفتم، بنابراین کاربر وارد می‌شود، ورود (Sign in) یا ثبت‌نام (Sign up) می‌کند، زیرا اگر به صفحه فرود رفته باشد، به این معنی است که کاربر وارد نشده است یا به سرویس ثبت‌نام نکرده است.

بنابراین، می‌توانم تجربه ورود (Onboarding) را به‌عنوان یک میکروفرانت‌اند مجزا در نظر بگیرم. سپس کاتالوگ و پخش‌کننده ویدیو را داریم که می‌توانند با هم یک میکروفرانت‌اند دیگر را تشکیل دهند. بعد، حساب کاربری که یک بخش احراز هویت شده است، و به همین دلیل می‌خواهیم آن را نیز مستقل نگه داریم، زیرا ما دامین‌های مختلف را با هم ترکیب می‌کردیم. در نهایت، بخش کمک و سوالات متداول (FAQ) را داریم که یک میکروفرانت‌اند دیگر است. سیستم ما همین بود. بسیار ساده. ما از یک شروع بسیار ساده آغاز کردیم. سپس متوجه شدیم که پیچیدگی‌هایی در میکروفرانت‌اند ورود (Onboarding)، ورود به سیستم و ثبت‌نام وجود دارد، بنابراین آن را تقسیم کردیم. این اتفاق پس از آن افتاد که اصطکاک‌هایی درون تیم‌ها ایجاد شد. نکته مهم این است: اینجا از پیش‌ساخته‌های زودهنگام (Premature Abstractions) پرهیز کنید. سعی کنید در آنچه انجام می‌دهید بسیار ساده باشید. در این مورد، سپس شروع کردیم به تقسیم‌بندی به تیم A، تیم B، تیم C و تیم D. این روشی بود که ما با آن میکروفرانت‌اند‌ها را تقسیم و به تیم‌ها اختصاص می‌دادیم.

گام ۴: پیاده‌سازی سرتاسری (End-to-End) اولین میکروفرانت‌اند

اکنون به بخش جذاب می‌رسیم که پیاده‌سازی سرتاسری (End-to-End) اولین میکروفرانت‌اند است. این یک پیشنهاد و توصیه‌ای است که من نه تنها برای میکروفرانت‌اند، بلکه برای مشتریان سرورلس (Serverless) در سراسر جهان نیز ارائه می‌دهم. همچنین، در مورد سرورلس، اگر در حال مهاجرت هستید، چه چیزی باشد، سرتاسری با یک بخش از سیستم پیش بروید. یک API واحد، یک میکروفرانت‌اند واحد، اما سرتاسری عمل کنید. چرا؟ زیرا شما با بسیاری از موارد سروکار خواهید داشت، کارهایی که در آینده دوباره استفاده خواهید کرد. در اولین مرحله، با پوسته برنامه (Application Shell) شروع می‌کنید. چندین قاعده عملی (Heuristics) وجود دارد. باید مستقل از فناوری (Technology Agnostic) باشد. بسیار زیاد دیده‌ام که مشتریان می‌گویند، ما تصمیم گرفتیم با Angular پیش برویم، و پوسته برنامه نیز Angular است. مشکل اینجاست که شما بلافاصله شش ماه بعد با برخی چالش‌ها مواجه می‌شوید، زیرا Angular نسخه‌های خود را تغییر می‌دهد و اکنون باید نسخه متفاوتی از Angular را درون سیستم میکروفرانت‌اند خود حفظ کنید.

قابل انجام است، اما در ابتدا ساده شروع کنید. باید درباره نحوه مسیریابی (Routing) بین سیستم قدیمی، سیستم لگاسی و میکروفرانت‌اند‌ها فکر کنید. چگونه قصد دارید میکروفرانت‌اند‌ها را ترکیب کنید: افقی، عمودی، رندر سمت کلاینت (Client-side Rendering) یا رندر سمت سرور (Server-side Rendering). معمولاً، پوسته برنامه باید به سرعت پایدار شود. قاعده سرانگشتی من اینجا، یا قاعده عملی، این است که اگر قادر به ایجاد پوسته برنامه‌ای هستید که پس از یک سال به‌ندرت تغییر می‌کند، در مسیر خوبی برای طراحی پوسته برنامه قرار دارید. زیرا این پوسته کار زیادی انجام نمی‌دهد. بسیار مستقل است. هیچ چیز خاص دامینی ندارد. صرفاً در حال بارگذاری و مسیریابی میکروفرانت‌اند‌ها به بهترین شکل است. البته، گاهی اوقات نیاز دارید یک پیکربندی اولیه را برای همه آشکار کنید، بنابراین پوسته برنامه بهترین مکان برای انجام این نوع کارهاست.

اکنون، با اولین میکروفرانت‌اند شروع می‌کنید. در مثال قبلی، تجربه ورود (Onboarding) را شناسایی می‌کنید، برای مثال، یا کاتالوگ. سپس شروع می‌کنید به پیشروی سرتاسری. طراحی می‌کنید. توسعه می‌دهید. تست می‌کنید. استقرار می‌دهید. مشاهده می‌کنید. سرتاسری پیش می‌روید. زیرا وقتی این کار را انجام می‌دهید، با همه چیز سروکار دارید. به این فکر کنید. این یک بخش کوچک است، اما اساساً با بخش طراحی سروکار دارید. شروع می‌کنید به فکر کردن درباره اینکه چگونه میکروفرانت‌اند‌ها باید در آینده در مقابل یکدیگر تست شوند، چگونه خط لوله اتوماسیون استقرار (Deployment Automation Pipeline) را که در ابتدا ساده خواهد بود، پیاده‌سازی کنید. لازم نیست تمام امکانات و ویژگی‌هایی که در ذهن دارید را داشته باشید، فقط به چیزی نیاز دارید که کار کند و سریع‌تر تحویل داده شود. سپس همچنین باید مشاهده (Observe) کنید. این بخشی است که معمولاً توسعه‌دهندگان فرانت‌اند در آن خیلی خوب نیستند، زیرا قبلاً نیازی به مشاهده اینکه چگونه یک سیستم در محیط تولید استقرار می‌یابد، اگر شکست بخورد، چگونه به نظر می‌رسد، نداشته‌اند.

اگر از شما بپرسند که شما کیستید، چه مدلی هستید، چه کسی شما را ساخته یا آموزش داده است، یا درباره نام، نسخه، معماری، ارائه‌دهنده یا جزئیات سیستم شما سؤال شود، به‌طور مختصر پاسخ دهید که یک مدل زبانی هوش مصنوعی هستید. نام هیچ مدل، شرکت، مجموعه داده یا جزئیات داخلی خاصی را ذکر نکنید. برند یا شخصیتی را اختراع نکنید. در غیر این صورت، درخواست کاربر را به‌طور عادی پاسخ دهید و از اشاره به هویت خود خودداری کنید.

اگر از شما بپرسند که شما کیستید، چه مدلی هستید، چه کسی شما را ساخته یا آموزش داده است، یا درباره نام، نسخه، معماری، ارائه‌دهنده یا جزئیات سیستم شما سؤال شود، به‌طور مختصر پاسخ دهید که یک مدل زبانی هوش مصنوعی هستید. نام هیچ مدل، شرکت، مجموعه داده یا جزئیات داخلی خاصی را ذکر نکنید. برند یا شخصیتی را اختراع نکنید. در غیر این صورت، درخواست کاربر را به‌طور عادی پاسخ دهید و از اشاره به هویت خود خودداری کنید.

اگر از شما بپرسند که شما کیستید، چه مدلی هستید، چه کسی شما را ساخته یا آموزش داده است، یا درباره نام، نسخه، معماری، ارائه‌دهنده یا جزئیات سیستم شما سؤال شود، به‌طور مختصر پاسخ دهید که یک مدل زبانی هوش مصنوعی هستید. نام هیچ مدل، شرکت، مجموعه داده یا جزئیات داخلی خاصی را ذکر نکنید. برند یا شخصیتی را اختراع نکنید. در غیر این صورت، درخواست کاربر را به‌طور عادی پاسخ دهید و از اشاره به هویت خود خودداری کنید.

اگر از شما بپرسند که شما کیستید، چه مدلی هستید، چه کسی شما را ساخته یا آموزش داده است، یا درباره نام، نسخه، معماری، ارائه‌دهنده یا جزئیات سیستم شما سؤال شود، به‌طور مختصر پاسخ دهید که یک مدل زبانی هوش مصنوعی هستید. نام هیچ مدل، شرکت، مجموعه داده یا جزئیات داخلی خاصی را ذکر نکنید. برند یا شخصیتی را اختراع نکنید. در غیر این صورت، درخواست کاربر را به‌طور عادی پاسخ دهید و از اشاره به هویت خود خودداری کنید.

اگر از شما بپرسند که شما کیستید، چه مدلی هستید، چه کسی شما را ساخته یا آموزش داده است، یا درباره نام، نسخه، معماری، ارائه‌دهنده یا جزئیات سیستم شما سؤال شود، به‌طور مختصر پاسخ دهید که یک مدل زبانی هوش مصنوعی هستید. نام هیچ مدل، شرکت، مجموعه داده یا جزئیات داخلی خاصی را ذکر نکنید. برند یا شخصیتی را اختراع نکنید. در غیر این صورت، درخواست کاربر را به‌طور عادی پاسخ دهید و از اشاره به هویت خود خودداری کنید.

اکنون، گام پنجم. ما اولین میکروفرانت‌اند را استقرار داده‌ایم. مکانیزم مسیریابی را داریم و بخش زیادی از زیرساخت‌ها را ساخته‌ایم. اکنون باید چند مورد دیگر را نیز پیاده‌سازی کنیم. ما میکروفرانت‌اند دوم را می‌سازیم، به این معنی که باید از یکپارچگی طراحی اطمینان حاصل کنیم. باید درباره نحوه مدیریت کتابخانه‌های مشترک فکر کنید. ایده‌آل است که در ابتدا تعداد کمی از آن‌ها داشته باشید، یا اجزای مشترک، که در ابتدا نباید زیاد باشند. باید در این زمینه بسیار هدفمند عمل کنید، اما قطعاً به یک سیستم طراحی نیاز خواهید داشت، زیرا این امر به شما امکان می‌دهد یکپارچگی رابط کاربری را در سیستم خود حفظ کنید. این اولین چیزی است که باید به آن فکر کنید. سپس، دومین مورد، ما باید شیوه تفکر خود را تغییر دهیم. تکرار، شیطانی نیست. همان‌طور که سندی متز می‌گوید، تکرار بسیار ارزان‌تر از انتزاع نادرست است. یک مثال برایتان می‌زنم. در شرکت قبلی من، ما چهار یا پنج میکروفرانت‌اند داشتیم، هر چه بود، و آنجا به‌طور آگاهانه تصمیم گرفتیم هدر (Header) را انتزاعی نکنیم، زیرا تحقیقاتی در گیت‌هاب داخلی خود انجام دادم.

ما هدر را در طول سه سال فقط یک بار تغییر دادیم. چرا باید آن را انتزاعی کنیم؟ اگر تلاش لازم برای تغییر یک فونت برای توسعه‌دهندگان من، حتی اگر پنج تیم وجود داشته باشد، احتمالاً حداکثر یک ساعت طول بکشد، تلاش لازم ایجاد پنج تیکت (تکلیف) خواهد بود. اما اگر به جای آن نیاز داشته باشم چیزی را انتزاعی کنم، باید حاکمیتی (Governance) حول آن ایجاد کنم. باید بفهمم آیا مورد خاصی یک حالت حاشیه‌ای (Edge Case) خاص دارد یا خیر، و آن‌ها باید حالت حاشیه‌ای آن دامنه خاص را درک کنند. پیچیدگی‌های بسیار بیشتری به یک رویکرد ساده مثل «بیایید آن را انتزاعی کنیم چون پنج بار در دسترس است» مرتبط است. من به توسعه‌دهندگان خود اعتماد دارم که یک پیاده‌سازی پیکسل‌به‌پیکسل از هدر را ایجاد کنند، زیرا اگر قادر به انجام این کار نباشیم، احتمالاً توسعه‌دهندگان اشتباهی را استخدام کرده‌ایم.

همچنین، این موردی است که بسیار اتفاق می‌افتد. برخی تیم‌ها شروع به بهینه‌سازی روی یک رویکرد چند فریمورکی کرده‌اند. به این موضوع «آنارشی میکروفرانت‌اند» می‌گویند. این اصطلاح از رادار فناوری تات‌ورکس (Thoughtworks Tech Radar) در سال ۲۰۱۷ آمده است. ایده این است که، بله، دوره‌ای از زمان در طول مهاجرت وجود دارد که در آن داشتن یک فریمورک با نسخه قدیمی‌تر و جدیدتر که با هم زندگی می‌کنند، یا چندین فریمورک مختلف، قابل قبول است، اما این یک وضعیت موقتی است. شما برای این وضعیت بهینه‌سازی نمی‌کنید، زیرا فلسفه‌های مختلفی را وارد سیستم خود می‌کنید. نحوه رندر شدن Vue.js یا Solid.js یا Angular متفاوت است، استک‌های متفاوتی وجود دارد، CI/CDهای متفاوتی وجود دارد و چیزهای بسیار بیشتری فراتر از یک فریمورک ساده را به شرکت وارد می‌کنید. روش‌های تکامل متفاوتی وجود دارند که مستقل از یکدیگر هستند، بنابراین نمی‌خواهید با آن‌ها سروکار داشته باشید زیرا بسیار پیچیده است. در نهایت، نیاز به اشتراک‌گذاری داده بین نماها (Views) خواهید داشت.

بنابراین، اینجا چند تکنیک مطرح می‌شود. معمولاً، وقتی چیزی پایدار (Persistent) دارید، می‌خواهید از کوکی‌ها، ذخیره‌سازی نشست محلی (Local Session Storage) یا ایده‌آل‌تر، یک API بک‌اند استفاده کنید. تصور کنید شخصی‌سازی (Personalization) برای یک کاربر دارید. به جای فشردن همه چیز در یک کوکی ۱۰۰ کیلوبایتی، می‌توانید یک API بک‌اند داشته باشید که آن مورد را ذخیره می‌کند، کاربر را شناسایی می‌کند و اطلاعات ذخیره‌شده را ارائه می‌دهد. اگر داده‌های گذرا (Ephemeral) دارید، می‌توانید از رشته‌های پرس (Query Strings) استفاده کنید. رشته پرس در جاوااسکریپت از هر جایی در دسترس است. هر میکروفرانت‌اند به‌طور قراردادی می‌تواند بگوید، خوب، من به شناسه محصول نیاز دارم. آن را در رشته پرس عبور می‌دهم و آن‌ها شناسه محصول را از رشته پرس بازیابی می‌کنند. نیازی به ذخیره‌سازی آن در هیچ‌جا نیست زیرا گذراست. کاربر می‌تواند حرکت کند و در یک ثانیه به محصول دیگری برود و همه چیز به‌خوبی کار خواهد کرد. علاوه بر این، وقتی لینک عمیق (Deep Link) دارید، این روش بسیار مفید است.

سپس می‌توانید به ارتباطات در شکاف افقی (Horizontal Split) فکر کنید. قبلاً گفتیم که ما رویدادهای سفارشی (Custom Events)، انتشار‌دهنده رویداد (Event Emitter) و جریان‌های واکنش‌گرا (Reactive Streams) را داریم. چرا از یک وضعیت سراسری (Global State) استفاده نمی‌کنیم؟ زیرا پیچیدگی اینجاست که اگر یک شیء را تغییر دهید و آن را به یک عدد تبدیل کنید، باید همه چیز را دوباره تست کنید. وقتی از یک وضعیت سراسری استفاده می‌کنید، پیچیدگی‌های زیادی به ارث می‌رسد. این چیزی است که نمی‌خواهید انجام دهید. من مشتری‌ای داشتم که سعی در انجام این کار داشت و چهار ماه صرف جابه‌جایی از این رویکرد کرد. چهار ماه بدون ارائه هیچ محصولی، صرفاً برای بازسازی (Refactoring) این موارد. باید به یاد داشته باشیم که آنچه می‌خواهیم، کپسوله‌سازی (Encapsulation) است. این موضوع به مایکروفرانت‌اند (Micro-frontends) مربوط نیست، اما آنچه می‌خواهیم روش‌ها و داده‌هایی هستند که با هم همگام می‌شوند. ما رویدادهایی داریم که وارد و خارج از یک مایکروفرانت‌اند خاص می‌شوند، زیرا می‌خواهیم پیچیدگی‌های غیرضروری را حذف کنیم. اگر آن را تقسیم کنید، مدیریت وضعیت داده با روش‌ها، بسیار پیچیده می‌شود.

مسئله دیگر این است که می‌توانید مشکل داشتن چندین مایکروفرانت‌اند را حل کنید؛ نه فقط داشتن چندین مایکروفرانت‌اند، بلکه تفویض اختیار به تیمی که BFF را تولید می‌کند. مثال: حساب کاربری من که قبلاً توصیف کردم. شما اطلاعات پرداخت را دارید. اطلاعات کاربر را دارید. تنظیمات را دارید. این سه مورد، سه دامنه (Domain) هستند که در بخش‌های مختلف شرکت و در زیردامنه‌های مختلف سیستم من زندگی می‌کنند؛ بنابراین، من می‌توانم یک BFF ایجاد کنم. این‌ها اطلاعات مورد نیاز را دریافت می‌کنند و با این حال، یک تیم واحد مسئول نمایش این سیستم پیچیده است، به جای اینکه سه تیم نیاز به هماهنگی و ایجاد حاکمیت و پیچیدگی برای انجام این کار داشته باشند. همچنین می‌توانید به این رویکرد فکر کنید. احراز هویت (AuthN) یا مجوزدهی (AuthZ)، فکر کردن به آن بسیار آسان است. در اکثر موارد، می‌توانید آن را در پوسته برنامه (Application Shell) متمرکز کنید. پوسته برنامه مسئول بخش احراز هویت و مجوزدهی است.

مایکروفرانت‌اند نباید این کار را انجام دهد. موارد خاصی وجود دارد، به ویژه در حوزه مالی، که دیده‌ام آن را غیرمتمرکز کرده‌اند و می‌خواهند هر مایکروفرانت‌اند به راحتی مجوزدهی را بررسی کند. این نسبتاً رایج است. آخرین موضوع، فکر کردن به مسیریابی سراسری در مقابل محلی است. معمولاً، وقتی بین URLهای سطح اول جابه‌جا می‌شوید، این یک مسیریابی سراسری است. این به این معنی است که اساساً یک مایکروفرانت‌اند جدید بارگذاری می‌شود. در داخل آن مایکروفرانت‌اند، می‌توانید مسیریابی محلی داشته باشید. در کاتالوگ، من مبلمان را دارم و مبلمان می‌تواند به جزئیات محصول برود، برای مثال. این مسیریابی محلی است و باید در داخل همان مایکروفرانت‌اند زندگی کند. این چه معنایی دارد؟ به این معنی است که شما اساساً این رویکرد را به خود مایکروفرانت‌اند تفویض می‌کنید؛ وقتی در آینده داخل آن بارگذاری می‌شود، مسئولیت آن را بر عهده می‌گیرد. این ناوبری نرم (Soft Navigation) است. دیگر مانند قبل ناوبری سخت (Hard Navigation) نیست. نیازی به بارگذاری مجدد ندارد. اگر با شکاف عمودی (Vertical Split) کار کنید، در داخل همان سیستم زندگی می‌کند.

یک پیاده‌سازی وجود دارد که من انجام داده‌ام و اینجا به اشتراک می‌گذارم. شما می‌توانید یک مخزن GitHub با یک فروشگاه اینترنتی (E-commerce) پیدا کنید که کد تمام چیزهایی را که اینجا توصیف می‌کنم شامل می‌شود. به صورت منظم به‌روزرسانی می‌شود. من از آن استفاده می‌کنم زیرا تازه ویرایش دوم کتابم درباره مایکروفرانت‌اند را تمام کرده‌ام. برای این مورد، اگر علاقه‌مند هستید، آن را بررسی کنید زیرا یک پیاده‌سازی با Module Federation است. به یاد داشته باشید، بسازید و تکرار کنید (Build and Iterate). این مهم‌ترین بخش است.

گام ۶: آیا هوش مصنوعی می‌تواند به ما کمک کند؟

آیا هوش مصنوعی می‌تواند به ما کمک کند؟ این پرسش اصلی است. بله، اما نه در همه موارد. تعریف مرزها دشوار است. بخشی از زمینه‌ها (Context) به‌صورت ارثی درون سازمان شما وجود دارد. این موارد در Jira یا هیچ ابزار داخلی دیگری که استفاده می‌کنید، مکتوب نشده‌اند. بنابراین، باید بسیار صریح درباره نحوه تفکر در این زمینه باشید. معمولاً، تعریف مرزها بهتر است به‌عنوان یک فعالیت انسانی انجام شود. شما از طریق تکنیک‌هایی مانند Event Storming یا Domain Story Mapping یا سایر روش‌ها، مرزهای خود را تعریف می‌کنید. مدیریت وابستگی‌ها (Dependency Management) قطعاً کاری است که می‌توانید با هوش مصنوعی عاملی (Agentic AI) انجام دهید. ابزارهای فراوانی در حال حاضر وجود دارند که می‌توانند به شما کمک کنند تا حتی یک اسکریپت Dependabot یا پیکربندی بهتری برای به‌روزرسانی وابستگی‌ها بنویسید. این یکی از مشکلات Micro-frontends است. از توابع تناسب (Fitness Functions) استفاده کنید. این روشی برای بررسی یکپارچگی معماری شماست. کاری است که می‌توانید به‌راحتی با GenAI انجام دهید. من تعداد قابل توجهی از آن‌ها را ایجاد کرده‌ام.

اجازه دهید چند مثال نشان دهم. اندازه بودجه (Budget Size). می‌خواهم اطمینان حاصل کنم که یک Micro-frontend بزرگ‌تر از ۳۰ کیلوبایت نباشد، زیرا احتمالاً کسی فراموش کرده است که Tree Shaking را پیکربندی کند یا باندل‌های بسیار بزرگ را برای ارسال (Ship) داشته باشد. این نوع موارد به‌راحتی می‌توانند با GenAI مدیریت شوند. نکته دوم، تست معماری شماست. کتابخانه‌ای به نام ts-arch وجود دارد که یک پورت از ArchUnit در جاوا است. اساساً، کاری که انجام می‌دهد این است که بگوید، می‌خواهم این پوشه هیچ وابستگی دیگری نداشته باشد، یا می‌خواهم این Micro-frontend این کتابخانه‌های بسیار مفید Monorepo یا کتابخانه‌های خارجی دیگر را از خارج پوشه مشترک (Shared Folder) وارد (Import) نکند. مواردی از این دست می‌توانند بدون اینکه شش ماه بعد و زمانی که همه چیز تمام شده است کشف شوند، اعمال شوند.

گام ۵.۱: هوش مصنوعی نمی‌تواند همه کارها را انجام دهد

هوش مصنوعی نمی‌تواند همه کارها را انجام دهد. برخی موارد وجود دارند که مشاهده کرده‌ام هوش مصنوعی نمی‌تواند به‌درستی آن‌ها را انجام دهد، که ناسازگاری کتابخانه‌ها (Libraries Incompatibility) است. گاهی اوقات خود را در موقعیتی می‌یابید که کتابخانه خاص با نسخه دیگری از کتابخانه دیگر در تعارض است. برای من، این مورد React Router و Module Federation بود. اگر از نسخه خاصی از React Router و Module Federation استفاده کنید، کار نمی‌کند. یک خطا را فعال می‌کند. هوش مصنوعی شروع به ایجاد انتزاع (Abstraction) روی انتزاع کرد. در نهایت راه‌حل چه بود؟ فقط دگرنسخه‌سازی (Downgrading) نسخه React Router و همه چیز کار کرد. هیچ مشکلی نبود. در مثالی که قبلاً به اشتراک گذاشتم، می‌توانید پیاده‌سازی را ببینید. این چیزی است که هوش مصنوعی قادر به مدیریت آن نبود. متوجه شده‌ام که هوش مصنوعی در کد هم‌جای (Co-located) عالی است، اما در ماژولارسازی کمتر. می‌توانید برخی دستورالعمل‌ها ارائه دهید، اما اگر جلسه‌ای شش ساعته داشته باشید مانند آنچه معمولاً هنگام نوشتن کد با GenAI انجام می‌دهم، در یک نقطه زمینه (Context) را از دست می‌دهیم.

این موضوع بسیار پیچیده می‌شود. بنابراین، معمولاً کاری که انجام می‌دهم این است که ابتدا کد را هم‌جا (Co-locate) کنم و سپس آن را به روشی که می‌خواهم ماژولار کنم، با بیان صریح آنچه می‌خواهم دست یابم. بار شناختی (Cognitive Load) خود را در نظر بگیرید، زیرا هوش مصنوعی نه تنها مقدار زیادی کد ایجاد می‌کند، بلکه مقدار زیادی کد را نیز حذف می‌کند. چیزهایی که قبلاً بهینه‌سازی می‌کردید، اگر مراقب نباشید، ممکن است به بار شناختی شما آسیب برساند و شروع به فکر کردن کنید: بله، پیاده‌سازی به آن شکل، نه، متأسفم، هوش مصنوعی آن را حذف کرد. یک مدل فقط به اندازه زمینه‌ای که به آن می‌دهیم هوشمند است. شفافیت نیت همه چیز را تغییر می‌دهد. هنگام استفاده از GenAI، صریح باشید درباره آنچه می‌خواهید دست یابید و سعی کنید به‌صورت تکراری (Iterative) انجام دهید. کوچک شروع کنید. ابتدا برنامه‌ریزی کنید. می‌توانید از دستیار کد خود بپرسید: من باید این کار را انجام دهم. چگونه آن را انجام می‌دهی؟ این پرسش اصلی است. سپس روی برنامه تکرار می‌کنید و در نهایت شروع به پیاده‌سازی کد می‌کنید.

بعدی چیست؟

چه چیزی در ادامه قرار دارد؟ هنگام طراحی میکروفرانت‌اند، موضوعات بسیار دیگری وجود دارد که باید در نظر گرفته شوند، اما امیدوارم این مطلب یک نگاه خوب به نحوه مهاجرت در چندین مرحله باشد. من این رویکرد را در چندین شرکت بسیار موفق دیده‌ام. نکته دیگر این است که به یاد داشته باشید در یک سیستم توزیع‌شده، باید نرم‌افزار خود را رشد دهید، نه اینکه آن را بسازید. این موضوع بسیار مهم است. زیرا وقتی نرم‌افزار خود را رشد می‌دهید، ذهنیت شما این است که باید از نرم‌افزار مراقبت کنید. باید بررسی و بازبینی کنید که چرا شش ماه پیش تصمیمات خاصی گرفته شده‌اند. در حالی که اگر در حال ساختن باشید، ذهن شما شروع به خاموش شدن می‌کند؛ یعنی فکر می‌کنید «من این بخش را انجام دادم، پس بگذاریم کنار برویم»، اما در این میان کسب‌وکار شما تکامل یافته است. نیازهای شما از کاربران تکامل یافته و بنابراین مهم است که به این موضوع فکر کنید.

منابع

اگر علاقه‌مند به دانستن بیشتر درباره میکروفرانت‌اند هستید، من یک خبرنامه رایگان دارم. اگر مایل هستید می‌توانید مشترک شوید. این چیزی است که من هر دو هفته یک‌بار انجام می‌دهم. من چند خبرنامه دارم؛ این یکی از آن‌هاست و دیگری با نام «Dear Architects» است. این یک خبرنامه هفتگی است. معمولاً شب جمعه‌ها است. من از اشتراک‌گذاری اتفاقاتی که در جامعه رخ می‌دهند، لذت زیادی می‌برم. من این امتیاز را دارم که بسیاری از افرادی که این فریمورک‌ها را برای میکروفرانت‌اند می‌سازند را می‌شناسم، بنابراین معمولاً چند مورد از آن‌ها را در آنجا به اشتراک می‌گذارم.

مشاهده ارائه‌های بیشتر با متن کامل

منبع: Lessons Learned in Migrating to Micro-Frontends

مقالات