امروز یک قالب جدید برای Vertical Microfrontends (VMFE) روی پلتفرم Cloudflare معرفی شده که اجازه می‌دهد چندین Cloudflare Worker مستقل را زیر یک دامنه واحد نگه دارید. با این روش، تیم‌ها می‌توانند کاملاً ایزوله کار کنند — مثلاً تیم بازاریابی، مستندسازی و داشبورد هر کدام به‌صورت جداگانه deploy کنند — ولی در عمل برای کاربر یک اپلیکیشن یکتا و یک‌پارچه نمایش داده شود. 🚀

بیشتر معماری‌های microfrontend به صورت “horizontal” هستند؛ یعنی اجزای مختلف یک صفحه از سرویس‌های مختلف فراخوانی می‌شوند. در مقابل، Vertical microfrontends اپلیکیشن را بر اساس مسیرهای URL تقسیم می‌کنند. در این مدل تیمی که مسیر /blog را دارد، نه فقط یک کامپوننت بلکه کل استک آن مسیر — از فریم‌ورک و کتابخانه‌ها تا CI/CD — را کنترل می‌کند. داشتن مالکیت کامل بر یک path به تیم‌ها این امکان را می‌دهد که با اطمینان و سرعت بیشتر منتشر کنند.

وقتی سازمان رشد می‌کند، هر تیم ممکن است نیاز به فریم‌ورک یا ابزار متفاوتی داشته باشد: مثلاً سایت بازاریابی ممکن است بهتر باشد با Astro ساخته شود و یک داشبورد با React. یا ممکن است یک monolith داشته باشید که چندین تیم در آن با هم کار می‌کنند و آپدیت‌ها مرتب بهم برمی‌گردند چون یک تیم باعث regression شده. سوال این است: چطور جزئیات پیاده‌سازی را از دید کاربر پنهان نگه داریم و در عین حال به تیم‌ها استقلال کامل بدهیم تا تجربه‌ای یکنواخت ارائه شود؟ پاسخ می‌تواند Vertical microfrontends باشد.

چیست این Vertical microfrontends؟ یک الگوی معماری است که در آن یک تیم مستقل، یک برش از اپلیکیشن را از رابط کاربری تا CI/CD مالک است. این برش‌ها معمولاً با مسیرهای دامنه مشخص می‌شوند و می‌توان هر Worker را به یک path مرتبط کرد:

/ = Marketing
/docs = Documentation
/blog = Blog
/dash = Dashboard

می‌توانیم حتی ریزتر هم شویم و زیرمسیرها را جدا کنیم، مثلاً داخل داشبورد هر محصول می‌تواند یک codebase جدا داشته باشد:

/dash/product-a = WorkerA
/dash/product-b = WorkerB

هر مسیر بالا می‌تواند پروژه فرانت‌اند کاملاً مستقل خودش را داشته باشد، بدون کد مشترک. این یعنی ownership از ابتدا تا انتها برای تیم‌ها فراهم می‌شود. اما چالش بعدی این است که این پروژه‌های جدا را طوری به هم وصل کنیم که برای کاربر یک تجربه متحد و یکپارچه به نظر برسد.

ما در Cloudflare خودمان این درد را تجربه کرده‌ایم: داشبورد شرکت از محصولات مختلفی تشکیل شده که هر کدام تیم خودش را دارد. کاربران وقتی بین بخش‌ها می‌روند نباید متوجه شوند که در واقع وارد پروژه یا repository جدا شده‌اند. اینجاست که تکنیک‌هایی مثل CSS View Transitions و Speculation Rules به کمک می‌آیند.

اتحاد بصری (Visually unified experiences) با چند خط CSS قابل دستیابی است. نکته مهم این است که جزئیات پیاده‌سازی را به کاربر نریزیم؛ اگر تجربه محسوس از هم گسیخته باشد، اشتباه بزرگی است. اجازه دهید ببینیم view transitions و preloading چه نقشی دارند.

View transitions وقتی مفیدند که بخواهیم بین دو صفحه متمایز با حس روان و نرم جابه‌جا شویم. با تعریف DOM elementهایی که باید تا رندر صفحه بعدی روی صفحه بمانند و تعریف چگونگی انیمیشن تغییرات، می‌توانیم دو Worker کاملاً جدا را طوری نمایش دهیم که انگار یک اپلیکیشن واحد هستند.

اگر بخواهیم عناصر مثل ناوبری بین دو صفحه حفظ شوند و به جای صفحه سفید واسط، حس یکپارچگی بدهیم می‌توانیم از CSS View Transitions استفاده کنیم. نمونه‌ای از آن:

@supports (view-transition-name: none) {
  ::view-transition-old(root), ::view-transition-new(root) {
    animation-duration: 0.3s;
    animation-timing-function: ease-in-out;
  }
  nav {
    view-transition-name: navigation;
  }
}

با این کار، ناوبری مشترک بین مسیرهای مختلف به صورت یک عنصر واحد نمایش داده می‌شود و جابه‌جایی‌ها نرم می‌شوند. البته در برخی موارد که بخش‌ها کاملاً متفاوت‌اند (مثلاً marketing vs docs vs dashboard)، تفاوت در ظاهر قابل قبول است؛ اما اگر تقسیم‌بندی عمودی داخل یک تجربه واحد باشد (مثل محصولات مختلف داخل داشبورد)، کاربر نباید متوجه جدا بودن repositoryها شود.

پیش‌بارگذاری (Preloading) — برای حس آنی بودن مشابه SPA — از Speculation Rules بهره می‌برد؛ این API برای بهبود عملکرد ناوبری‌های آینده طراحی شده و فعلاً در Chrome/Edge/Opera پشتیبانی می‌شود و در Firefox/Safari پشتیبانی کامل ندارد، پس باید با graceful degradation طراحی کرد. نمونه rule برای prefetch مسیرهای مرتبط:

{
  "prefetch": [
    {
      "urls": ["https://product-a.com", "https://product-b.com"],
      "requires": ["anonymous-client-ip-when-cross-origin"],
      "referrer_policy": "no-referrer"
    }
  ]
}

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

بین View Transitions و Speculation Rules می‌توان مخازن کد کاملاً جدا را طوری به هم دوخت که کاربر حس کند همه چیز از یک SPA واحد سرو می‌شود — چیز واقعا جالبی است! 😄

حالا نیاز داریم که چند اپلیکیشن را میزبانی کنیم و درخواست‌ها را به‌صورت جریان‌وار مسیریابی کنیم. یک Cloudflare Worker به‌عنوان Router می‌تواند نقطه منطقی‌ای در edge باشد که درخواست‌ها را بررسی و به Worker مربوطه هدایت (یا forward) کند. این اجازه می‌دهد یک دامنه را به Router Worker مپ کنیم و بقیه کارها خودکار انجام شود.

Service bindings در Cloudflare Workers مکانیزمی هستند که به یک Worker اجازه می‌دهد بدون رفتن به URL عمومی، به Worker دیگر دسترسی پیدا کند. به عبارت دیگر Router Worker می‌تواند مستقیماً Worker مربوط به هر مسیر را صدا بزند یا درخواست را به آن forward کند. این همان چیزی است که برش‌های عمودی را با هم “دوخت” می‌کند.

برای اینکه Router بداند کدام Workers را می‌تواند صدا بزند، باید فایل wrangler آن را با bindings به‌روز کنیم. نمونه‌ای از تعریف:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "router",
  "main": "./src/router.js",
  "services": [
    { "binding": "HOME", "service": "worker_marketing" },
    { "binding": "DOCS", "service": "worker_docs" },
    { "binding": "DASH", "service": "worker_dash" }
  ]
}

با این کار Router اجازه دارد به این سه Worker داخلی درخواست بفرستد. در src/router.js معمولاً بر اساس path تصمیم می‌گیرید که کدام binding را فراخوانی یا به کدام Worker proxy کنید. این مدل ساده و کم‌پیکربندی است و در عمل «zero-config request routing» را برای شما فراهم می‌آورد.

نکات عملی و نگرانی‌های معمول که باید مد نظر SRE/DevOps باشند:

– احراز هویت و session: باید روش مطمئنی برای پاس‌کردن کوکی‌ها/توکن‌ها بین Router و Workers و یا استفاده از shared auth layer داشته باشید.
– Caching و CDN: هر برش ممکن است استراتژی caching متفاوتی نیاز داشته باشد؛ هماهنگ کردن Cache-Control و Purge برای محتواهای مشترک ضروری است.
– Observability: لاگ‌ها، distributed tracing و metrics باید طوری طراحی شوند که بتوانید به سادگی عملکرد هر برش را زیر نظر بگیرید و SLOها را جداگانه تعریف کنید.
– Rollback و CI/CD: هر تیم pipeline خودش را داشته باشد اما لازم است روی rollout و rollback هماهنگی وجود داشته باشد تا regressions سراسری ایجاد نکند.
– استایل و UX: نگهداری design tokens یا یک CDN برای theme/shared CSS می‌تواند کمک کند تجربه کاربر یکپارچه بماند، حتی با repositoryهای جدا.

جمع‌بندی: Vertical microfrontends با ترکیب Cloudflare Workers، Service bindings، CSS View Transitions و Speculation Rules یک راه‌حل عملی و مقیاس‌پذیر برای تقسیم ownership بین تیم‌ها و در عین حال حفظ تجربه کاربری یکپارچه فراهم می‌کنند. این الگو مخصوصاً برای سازمان‌هایی که می‌خواهند استک‌های متفاوت و استقلال تیمی داشته باشند اما تجربه کاربری‌شان متحد بماند، بسیار مناسب است. اگر خواستید می‌توانم یک مثال عملی از Router Worker و نحوه forward کردن requestها بنویسم و نکات پیاده‌سازی (auth headers، response streaming، error handling) را هم دقیق‌تر توضیح بدهم. 🙂