माइक्रो फ्रंटएंड आर्किटेक्चर: फ्रंटएंड विकास का विस्तार

माइक्रो फ्रंटएंड माइक्रोसर्विस के विचार को इंटरफ़ेस की परत तक ले आते हैं। एक अकेले एकाश्म अनुप्रयोग के बजाय फ्रंटएंड को छोटे स्वतंत्र अनुप्रयोगों में बाँटा जाता है, जिन्हें अलग-अलग बनाया, परखा और तैनात किया जाता है। हर माइक्रो फ्रंटएंड किसी एक कारोबारी क्षेत्र का स्वामी होता है और उसे किसी दूसरी तकनीक से, दूसरी टीम द्वारा, दूसरी रिलीज़ रफ़्तार पर बनाया जा सकता है।

यह आर्किटेक्चर उन्हीं दबावों से जन्मा जिनसे सर्वर पर माइक्रोसर्विस आए। जैसे-जैसे वेब अनुप्रयोग जटिल होते गए, एकाश्म फ्रंटएंड सँभालने में भारी, बनाने में धीमे और तैनात करने में जोखिम भरे होते गए। कोड में कहीं भी किया गया एक बदलाव पूरे अनुप्रयोग को दोबारा बनाने और तैनात करने की माँग करता था, जिससे टीमें धीमी पड़तीं और अलग-अलग रफ़्तार से चलने की इच्छुक टोलियों के बीच टकराव पैदा होता।

माइक्रो फ्रंटएंड क्या हैं?

माइक्रो फ्रंटएंड आर्किटेक्चर एक वेब अनुप्रयोग को कार्यात्मक हिस्सों में बाँटता है, जिनमें से हर हिस्से का स्वामी एक स्वायत्त टीम होती है। टीम अपने हिस्से की सभी परतों के लिए उत्तरदायी है: इंटरफ़ेस के अवयव, कारोबारी तर्क, आँकड़ों का लाना और सर्वर से जुड़ाव। उपयोगकर्ता को एक ही सुगठित अनुप्रयोग दिखता है, पर परदे के पीछे वह ब्राउज़र की एक ही खिड़की में चल रहे कई छोटे अनुप्रयोगों से बना होता है।

यह विभाजन क्षेत्र-आधारित अभिकल्प के सिद्धांतों पर चलता है। हर माइक्रो फ्रंटएंड एक परिबद्ध संदर्भ से मेल खाता है: किसी विशेष कारोबारी क्षमता के इर्द-गिर्द खींची गई तार्किक सीमा। भुगतान की प्रक्रिया, उत्पाद सूची, उपयोगकर्ता का ब्योरा और खोज — ये अलग-अलग टीमों के अधीन अलग-अलग माइक्रो फ्रंटएंड हो सकते हैं।

माइक्रो फ्रंटएंड और कोड को मॉड्यूल में बाँट देने के बीच मूल अंतर यह है कि माइक्रो फ्रंटएंड निर्माण और तैनाती में स्वतंत्र होते हैं। हर एक का अपना पाइपलाइन, अपना भंडार, अपने परीक्षण और अपनी रिलीज़ अनुसूची होती है। यही स्वतंत्रता इस आर्किटेक्चर को उसके लाभ देती है — और यही उसकी कठिनाइयाँ भी गढ़ती है।

माइक्रो फ्रंटएंड न तो अवयवों की कोई लाइब्रेरी है, न साझा उपयोगिताओं का ढेर। यह एक आत्मनिर्भर अनुप्रयोग है जो चलते समय किसी बड़े अनुप्रयोग के भीतर जोड़ दिया जाता है। इस आर्किटेक्चर की शक्ति और उसकी कीमत, दोनों को समझने के लिए यह भेद अनिवार्य है।

Webpack 5 के साथ Module Federation

React और Angular की दुनिया में Module Federation सबसे प्रचलित तरीका है। Webpack 5 में आया यह तरीका एक JavaScript अनुप्रयोग को यह सुविधा देता है कि वह चलते समय किसी दूसरे अनुप्रयोग से कोड ले आए। लाया गया कोड मेज़बान अनुप्रयोग के संदर्भ में चलता है और उसके साथ निर्भरताएँ साझा करता है, ताकि React या Vue जैसी लाइब्रेरी दोहराई न जाएँ।

तंत्र यह है कि एक दूरस्थ अनुप्रयोग कुछ चुनिंदा मॉड्यूल बाहर खोलता है और मेज़बान उन्हें आयात करता है। दूरस्थ बताता है कि कौन-से मॉड्यूल उपलब्ध हैं और मेज़बान उन्हें ऐसे बुलाता है मानो वे स्थानीय आयात हों। Webpack अतुल्यकालिक लदान, दोहरी निर्भरताओं को हटाने और निर्माण के समय संस्करणों के निपटारे का काम सँभालता है।

निर्भरताओं का साझा होना सबसे महत्वपूर्ण विशेषता है। जब मेज़बान और दूरस्थ एक ही लाइब्रेरी इस्तेमाल करते हैं, तो Webpack सुनिश्चित करता है कि ब्राउज़र में उसकी केवल एक प्रति जाए। इससे React, Lodash या दूसरी बड़ी लाइब्रेरियों की कई प्रतियों से होने वाली गिरावट टल जाती है। इकलौती प्रति वाला ढंग यह भी पक्का करता है कि साझा स्थिति — कोई Redux भंडार या React संदर्भ — वास्तव में माइक्रो फ्रंटएंडों के बीच साझा हो।

// webpack.config.js - Remote application exposing a module
const { ModuleFederationPlugin } = require("webpack").container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: "checkout",
      filename: "remoteEntry.js",
      exposes: {
        "./CheckoutApp": "./src/bootstrap",
      },
      shared: {
        react: { singleton: true, requiredVersion: "^18.0.0" },
        "react-dom": { singleton: true, requiredVersion: "^18.0.0" },
      },
    }),
  ],
};

// webpack.config.js - Host application consuming the remote
const { ModuleFederationPlugin } = require("webpack").container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: "shell",
      remotes: {
        checkout: "checkout@http://localhost:3001/remoteEntry.js",
      },
      shared: {
        react: { singleton: true, requiredVersion: "^18.0.0" },
        "react-dom": { singleton: true, requiredVersion: "^18.0.0" },
      },
    }),
  ],
};

// Lazy loading the remote module in the host
const CheckoutApp = React.lazy(() => import("checkout/CheckoutApp"));

function Shell() {
  return (
    <Suspense fallback={<Spinner />}>
      <CheckoutApp />
    </Suspense>
  );
}

उपयोगकर्ता जैसे ही ऐसे रास्ते पर पहुँचता है जहाँ भुगतान वाला माइक्रो फ्रंटएंड चाहिए, मेज़बान दूरस्थ प्रवेश फ़ाइल उठा लेता है। यह फ़ाइल Webpack द्वारा अपने आप बनाई गई छोटी-सी JavaScript है: इसमें सभी खुले मॉड्यूलों की सूची और उनके पते होते हैं। जब मेज़बान कोई ख़ास मॉड्यूल माँगता है, तो Webpack उससे जुड़ा टुकड़ा उतारता है और उसे साझा निर्भरताओं के संदर्भ में चलाता है।

एक अहम बात यह है कि मेज़बान और दूरस्थ को साझा निर्भरताओं के संस्करणों पर सहमत होना पड़ता है। यदि दूरस्थ को React 18.2 चाहिए पर मेज़बान के पास केवल React 18.0 है, तो जब तक संस्करण मेल न खाएँ, इकलौती प्रति वाला ढंग विफल हो जाता है। requiredVersion खाना अर्थ-आधारित संस्करण परास स्वीकार करता है, जिससे छूट भी मिलती है और टूट-फूट से बचाव भी रहता है।

iframe आधारित एकीकरण

Module Federation और आधुनिक तरकीबों से पहले iframe ही मूल तरीका था। iframe किसी पृष्ठ में एक पूरी तरह स्वतंत्र HTML दस्तावेज़ धँसा देता है। उस दस्तावेज़ का अपना JavaScript संदर्भ, अपना CSS दायरा और अपना DOM वृक्ष होता है। यही अलगाव इस तरीके की ताक़त भी है और कमज़ोरी भी।

सभी तरकीबों में iframe अलगाव की सबसे मज़बूत गारंटी देता है। CSS के टकराव का ख़तरा नहीं, क्योंकि हरेक का अपना दस्तावेज़ है। JavaScript की भिड़ंत नहीं, क्योंकि हरेक का अपना वैश्विक दायरा है। कोई माइक्रो फ्रंटएंड भूल से भी दूसरे की स्थिति नहीं बदल सकता, क्योंकि DOM वृक्ष पूरी तरह अलग हैं। एक में स्मृति का रिसाव दूसरे को नहीं गिराता।

इन गारंटियों की क़ीमत भारी है। iframe बोझिल होते हैं। हर एक पूरा HTML दस्तावेज़ उठाता है, उसके सारे CSS और JavaScript संसाधनों समेत। ब्राउज़र हरेक को अलग ब्राउज़िंग संदर्भ मानता है, यानी अधिक स्मृति, अधिक जाल-अनुरोध और अधिक चित्रण का काम। यदि आप दस माइक्रो फ्रंटएंड iframe में धँसा दें, तो ब्राउज़र असल में ग्यारह पृष्ठ लादता है।

iframe और उसे समेटे पृष्ठ के बीच बातचीत के लिए postMessage चाहिए, जो अतुल्यकालिक है और केवल क्रमबद्ध किए जा सकने वाले आँकड़ों तक सीमित है। सीमा के आर-पार न फलन भेजे जा सकते हैं, न वर्गों की प्रतियाँ, न DOM के संदर्भ। इसी से iframe उन माइक्रो फ्रंटएंडों के लिए अनुपयुक्त हो जाते हैं जिन्हें घनिष्ठ जुड़ाव चाहिए — जैसे कोई भुगतान फ़ॉर्म जिसे मेज़बान पृष्ठ की टोकरी की स्थिति चाहिए।

सुगम्यता भी दुख पाती है। परदा-पाठक और दूसरी सहायक तकनीकें नत्थी ब्राउज़िंग संदर्भों से प्रायः जूझती हैं। iframe की सीमाओं के आर-पार कुंजीपटल से चलना असंगत रहता है। पृष्ठ के भीतर खोज इन सीमाओं को पार नहीं करती, और इतिहास हर भीतरी संचरण को अलग सत्र मानता है।

iframe मुख्यतः तीसरे पक्ष की सामग्री धँसाने के काम आते हैं, जहाँ अलगाव सर्वोपरि है और जुड़ाव न्यूनतम। किसी सुगठित अनुभव को गढ़ने के लिए वे बुरा चुनाव हैं, जहाँ माइक्रो फ्रंटएंडों को स्थिति साझा करनी है, संचरण मिलाना है और एक जैसी दृश्य सतह बनानी है।

single-spa और दूसरे संचालन ढाँचे

single-spa एक JavaScript ढाँचा है जो एक ही पृष्ठ पर कई माइक्रो फ्रंटएंडों का संचालन करता है। यह हरेक का जीवनचक्र सँभालता है: उपयोगकर्ता के ज़रूरी रास्ते पर आते ही उसे चढ़ाता है, हटते ही उतारता है, और निष्क्रिय पड़े रहने वालों को स्मृति में रखता है ताकि उन्हें फुर्ती से लौटाया जा सके। यह किसी ढाँचे से बँधा नहीं और React, Angular, Vue, Svelte तथा शुद्ध JavaScript को सँभालता है।

हर माइक्रो फ्रंटएंड single-spa में स्वयं को एक अनुप्रयोग के रूप में दर्ज कराता है, जिसमें जीवनचक्र के फलन होते हैं: bootstrap, mount, unmount और वैकल्पिक रूप से update। single-spa की जड़ रूटिंग सँभालती है और पतों के नमूनों से तय करती है कि कौन-से अनुप्रयोग सक्रिय हैं। जब कोई पता किसी दर्ज अनुप्रयोग के सक्रियता-फलन से मेल खाता है, तो single-spa उसे चढ़ाता है और जो अब सक्रिय नहीं रहे उन्हें उतार देता है।

single-spa सबसे काँटेदार समस्याओं में से एक सुलझाता है: ढाँचों का साथ-साथ रहना। जब दो माइक्रो फ्रंटएंड एक ही ढाँचे के अलग संस्करणों पर, या बिलकुल अलग ढाँचों पर बने हों, तो single-spa उन्हें एक ही पृष्ठ पर बिना टकराव के साथ रहने देता है। वह यह काम जीवनचक्र के जोड़ों में दख़ल देकर और हर अनुप्रयोग की वैश्विक स्थिति को अलग रखकर करता है।

दूसरे तरीक़ों में Piral है, जिसके विस्तार-प्रतिरूप में माइक्रो फ्रंटएंड किसी वितरण सेवा से मॉड्यूल की तरह उतारे जाते हैं, और Open Components है, जिसका झुकाव सर्वर पर संयोजन की ओर है। वेब अवयवों का रास्ता भी है, जिसमें हर माइक्रो फ्रंटएंड को एक विशेष तत्व में लपेटकर ब्राउज़र के संगत अंतरापृष्ठ में दर्ज किया जाता है। वेब अवयव HTML, CSS और JavaScript का सहज परिसीमन देते हैं और HTML खाकों में घोषणात्मक ढंग से जुड़ जाते हैं।

चुनाव आपकी तकनीकी थाती, टीमों की बनावट और निष्पादन की माँगों पर निर्भर है। जो पहले से Webpack पर हैं उनके लिए Module Federation सबसे अच्छा है। अलग-अलग ढाँचों वाली मिली-जुली आर्किटेक्चर में single-spa समझ में आता है। वेब अवयव उन संगठनों के लिए ठीक हैं जो ढाँचे से स्वतंत्र सीमाएँ लागू करना चाहते हैं। iframe केवल तीसरे पक्ष की सामग्री धँसाने भर के लिए उपयुक्त हैं।

साझा अवयव लाइब्रेरी और अभिकल्प प्रणालियाँ

एक बार-बार उठने वाली चिंता है दृश्य संगति का खो जाना। यदि हर टीम अपना इंटरफ़ेस अलग-थलग बनाए, तो उपयोगकर्ता अनुप्रयोग में घूमते हुए अलग-अलग बटन, अलग टाइपफ़ेस और अलग सज्जा पाएगा। इसका उत्तर है एक साझा अवयव लाइब्रेरी या अभिकल्प प्रणाली, जिसे सभी माइक्रो फ्रंटएंड बरतें।

साझा लाइब्रेरी में प्रायः प्रस्तुति के अवयव होते हैं: बटन, निवेश खाने, संवाद खिड़कियाँ और संचरण के तत्व। ये विशुद्ध दृश्य होते हैं और इनमें कारोबारी तर्क नहीं होता। ये शैली के भेदों, नामपट्टियों और घटना-संचालकों के लिए गुण लेते हैं, पर आँकड़े नहीं लाते, स्थिति नहीं सँभालते और क्षेत्र-विशेष का व्यवहार लागू नहीं करते। यही अलगाव लाइब्रेरी को स्थिर रखता है और उसे टीमों की रफ़्तार का अवरोध बनने से रोकता है।

इसके संस्करण सँभालने में अनुशासन चाहिए। npm संकुल के रूप में प्रकाशित होने पर हर माइक्रो फ्रंटएंड एक संस्करण पर टिक जाता है और अपनी रफ़्तार से उन्नयन करता है। इससे स्वायत्तता मिलती है, पर संस्करणों का बहाव भी हो सकता है, जिसमें वही अवयव अलग-अलग माइक्रो फ्रंटएंडों में अलग दिखता है। लाइब्रेरी पर दृश्य प्रतिगमन परीक्षणों की जमात इन फ़र्क़ों को उत्पादन से पहले पकड़ लेती है।

दूसरा विकल्प है अभिकल्प प्रणाली को चलने-के-समय की निर्भरता के रूप में परोसना। लाइब्रेरी एक आत्मनिर्भर अनुप्रयोग की तरह प्रकाशित होती है और हर माइक्रो फ्रंटएंड चालू होते समय उसे उतार लेता है। तब सभी हमेशा हर अवयव का नवीनतम संस्करण बरतते हैं और बहाव मिट जाता है। बदले में प्रणाली का उन्नयन अब एक तैनाती माँगता है, और कोई भी टूट सारे माइक्रो फ्रंटएंडों पर एक साथ गिरती है।

अभिकल्प प्रतीक न्यूनतम साझा आधार के रूप में

अभिकल्प प्रतीक — रंगों, अवकाश, अक्षरांकन और छायाओं के नामित मान — पूरी लाइब्रेरी का हल्का विकल्प हैं। हर माइक्रो फ्रंटएंड अपने अवयव ख़ुद बनाता है पर प्रतीकों का वही समूह बरतता है। इससे कार्यान्वयन की छूट मिलती है और दृश्य संगति भी बची रहती है। प्रतीकों को CSS के विशेष गुणों के रूप में बाँटा जा सकता है, जिन्हें दबाना आसान है और जिनके लिए कोई निर्माण उपकरण नहीं चाहिए।

माइक्रो फ्रंटएंडों के बीच संवाद

माइक्रो फ्रंटएंडों को आपस में बात करनी पड़ती है। भुगतान वाले को जानना है कि उपयोगकर्ता ने सूची से टोकरी में क्या डाला। शीर्षिका वाले को अपना निशान बदलना है जब सूचना वाले के पास नई सूचना आए। प्रवेश वाले को बाक़ी सबको बताना है कि उपयोगकर्ता भीतर आया या बाहर गया।

सबसे सरल तंत्र है साझा घटना-वाहिनी। हर माइक्रो फ्रंटएंड एक वैश्विक माध्यम में घटनाएँ प्रकाशित करता है और दूसरों की घटनाओं का ग्राहक बनता है। वाहिनी प्रायः एक घटना-उत्सर्जक के रूप में बनाई जाती है, जो window वस्तु से जुड़ी होती है या खोल द्वारा भीतर डाली जाती है। घटनाएँ एक प्रकार और एक भार लिए चलती हैं; ग्राहक प्रकार के हिसाब से छानते हैं।

// Shared event bus - shell application
// Each micro frontend receives this bus as part of its initialization.

type BusEvent = {
  type: string;
  payload: unknown;
};

type Listener = (event: BusEvent) => void;

class EventBus {
  private listeners: Map<string, Listener[]> = new Map();

  on(type: string, listener: Listener): () => void {
    if (!this.listeners.has(type)) {
      this.listeners.set(type, []);
    }
    this.listeners.get(type)!.push(listener);
    return () => this.off(type, listener);
  }

  off(type: string, listener: Listener): void {
    const listeners = this.listeners.get(type);
    if (listeners) {
      this.listeners.set(
        type,
        listeners.filter((l) => l !== listener)
      );
    }
  }

  emit(type: string, payload: unknown): void {
    this.listeners.get(type)?.forEach((listener) => {
      listener({ type, payload });
    });
  }
}

// Usage in a micro frontend
export function init(bus: EventBus) {
  bus.on("item:added-to-cart", (event) => {
    updateCartBadge(event.payload.quantity);
  });

  bus.emit("user:logged-in", { userId: "abc-123", name: "Jane" });
}

अधिक जटिल ज़रूरतों के लिए साझा स्थिति-भंडार प्रायः घटना-वाहिनी से बेहतर रहता है। खोल में रखा कोई Redux या Zustand भंडार हर माइक्रो फ्रंटएंड में डाला जा सकता है। हरेक उसमें से वह स्थिति पढ़ता है जो क्षेत्रों के आर-पार जाती है, और अपने क्षेत्र की स्थिति अपने स्थानीय भंडार में लिखता है। जुड़ाव ढीला बना रहता है और फिर भी साझा आँकड़ों के लिए एक ही सच्चाई का स्रोत रहता है: वर्तमान उपयोगकर्ता, टोकरी की सामग्री, सक्रिय संचरण।

संवाद की परत को स्पष्ट रूप से गढ़ना और लिखित करना पड़ता है। टीमों को घटनाओं के नाम, भार के प्रारूप और साझा तथा स्थानीय स्थिति के बीच की सीमा पर सहमत होना होगा। इस सहमति के बिना माइक्रो फ्रंटएंड एक-दूसरे की भीतरी स्थिति पर अघोषित निर्भरताएँ बना लेते हैं, और एक वितरित एकाश्म खड़ा हो जाता है — माइक्रो फ्रंटएंडों की सारी जटिलता के साथ और उनके एक भी लाभ के बिना।

रूटिंग की रणनीतियाँ

रूटिंग को दो सवालों का जवाब देना है: वर्तमान रास्ता किस माइक्रो फ्रंटएंड का है, और उनके बीच संचरण कैसे होता है? जवाब इस पर निर्भर हैं कि आप खोल में केंद्रीकृत रूटर बरतते हैं, वितरित तरीक़ा अपनाते हैं, या मिली-जुली रणनीति।

केंद्रीकृत रूटिंग में खोल अनुप्रयोग पहले स्तर के रूटर का स्वामी होता है। वह रास्तों का नक़्शा तय करता है, ठहराता है कि किस पते के नमूने पर कौन-सा माइक्रो फ्रंटएंड चढ़ाना है, और उसे प्राचल सौंपता है। हर माइक्रो फ्रंटएंड के पास अपने क्षेत्र के भीतर घूमने के लिए अपना भीतरी रूटर होता है। खोल क्षेत्रों के बीच के पड़ावों को सँभालता है; माइक्रो फ्रंटएंड भीतरी रास्तों को।

वितरित रूटिंग में रास्ते ख़ुद माइक्रो फ्रंटएंडों के होते हैं। खोल अब भी पते और माइक्रो फ्रंटएंड के बीच का समूचा मिलान सँभालता है, पर हर एक अपने उप-रास्ते और भीतरी संचरण ख़ुद सँभालता है। खोल में रखी एक संचरण लाइब्रेरी इतिहास के बदलावों को मिलाती है और पक्का करती है कि पता-पट्टी पूरे अनुप्रयोग की स्थिति दिखाए, केवल सक्रिय माइक्रो फ्रंटएंड की नहीं।

घटना-आधारित रूटिंग में वाहिनी संचरण को मिलाती है। जब किसी माइक्रो फ्रंटएंड को ऐसे रास्ते पर जाना हो जो दूसरे का है, तो वह एक संचरण-घटना उत्सर्जित करता है। खोल उसे सुनता है और बदलाव कर देता है, जिससे गंतव्य चढ़ जाता है। इस तरह रूटिंग के मामले माइक्रो फ्रंटएंडों से बाहर निकल जाते हैं और तर्क खोल में सिमट आता है।

  • केंद्रीकृत रूटिंग: खोल रास्तों के नक़्शे का स्वामी है और माइक्रो फ्रंटएंड केवल भीतरी संचरण सँभालते हैं। छोटी टीमों और सरल सीमाओं के लिए उपयुक्त।
  • वितरित रूटिंग: हर माइक्रो फ्रंटएंड अपने उप-रास्ते सँभालता है। बड़ी टीमों और भली-भाँति अलग क्षेत्रों के लिए उपयुक्त।
  • घटना-आधारित रूटिंग: संचरण एक साझा वाहिनी से मिलाया जाता है। अलग-अलग ढाँचों वाली मिली-जुली आर्किटेक्चर के लिए उपयुक्त।
  • मिश्रित रूटिंग: खोल पहले स्तर के क्षेत्र-रास्ते सँभालता है और माइक्रो फ्रंटएंड उप-रास्ते। अधिकतर उत्पादन अनुप्रयोगों के लिए उपयुक्त।

तैनाती और संस्करण

तैनाती की स्वतंत्रता ही माइक्रो फ्रंटएंड अपनाने की मुख्य प्रेरणा है। हर टीम को अपना हिस्सा बिना दूसरों से तालमेल किए और पूरे की स्थिरता जोखिम में डाले बिना तैनात कर पाना चाहिए। इसके लिए पाइपलाइन, आश्रय की योजना और संस्करण-विधान को सोच-समझकर गढ़ना पड़ता है।

सबसे सरल प्रतिरूप है अलग-अलग आश्रय। हर माइक्रो फ्रंटएंड अपने पते या अपने भंडारण में तैनात होता है। खोल उसे वहीं से चलते समय उतारता है। यह प्रतिरूप सबसे अधिक स्वतंत्रता देता है, क्योंकि हर टीम अपनी अधोसंरचना, अपना पाइपलाइन और अपनी वापसी की रणनीति नियंत्रित करती है। किसी माइक्रो फ्रंटएंड के उन्नयन पर खोल को दोबारा तैनात करने की ज़रूरत नहीं, क्योंकि वह गतिशील ढंग से नवीनतम संस्करण उठा लेता है।

अलग-अलग आश्रय एक नई कठिनाई लाता है: पिछली अनुकूलता बनाए रखने के लिए तैनातियों का तालमेल। यदि भुगतान वाला माइक्रो फ्रंटएंड खोल से गुणों का कोई ख़ास प्रारूप अपेक्षित रखता है और खोल की अगली तैनाती उसे बदल दे, तो भुगतान वाला तब तक टूटा रहेगा जब तक उसका भी उन्नयन न हो। उपाय यह है कि एकीकरण के इक़रारनामे का — यानी प्रायः खोल द्वारा सौंपे जाने वाले गुणों का — संस्करण रखा जाए और टूटों को अर्थ-आधारित संस्करण से चिह्नित किया जाए।

यहाँ क्रमिक तैनाती और उपयोगकर्ताओं के छोटे हिस्से पर परख आसान हो जाती है, क्योंकि हर माइक्रो फ्रंटएंड अपने आप तैनात होता है। कोई टीम नया संस्करण पाँच प्रतिशत उपयोगकर्ताओं के लिए खोल सकती है, त्रुटियों की दर और निष्पादन के सूचक देख सकती है, और सब ठीक रहा तो धीरे-धीरे फैला सकती है। कोई गड़बड़ी दिखे तो वह केवल अपना माइक्रो फ्रंटएंड वापस लेती है, बाक़ी को छुए बिना।

खोल से संस्करण टाँक देना आपात द्वार है। यदि किसी तैनाती ने ऐसी टूट ला दी जो परीक्षणों से बच निकली, तो खोल पिछला संस्करण टाँककर स्थिरता लौटा सकता है जब तक टीम उसे सुलझाए। यह तंत्र प्रायः कोई विन्यास फ़ाइल या परिवेश चर होता है, जो हर माइक्रो फ्रंटएंड को पहले से तैनात किसी संस्करण से जोड़ता है।

एक भंडार या अलग-अलग भंडार

भंडारों की बनावट फ्रंटएंड समुदाय में विवादित विषय है। एक ही भंडार के पक्ष के तर्क और अलग-अलग भंडारों के पक्ष के तर्क, दोनों में दम है, और सही चुनाव टीमों के आकार, संगठन की परिपक्वता और उपकरणों की पसंद पर निर्भर करता है।

एक ही भंडार सारे माइक्रो फ्रंटएंड एक जगह, फ़ोल्डरों में सजाकर रखता है। हर एक की अपनी package.json, अपना निर्माण विन्यास और अपनी निर्देशिका होती है। यह तरीक़ा निर्भरताएँ सँभालने, निर्माण चलाने और माइक्रो फ्रंटएंडों के बीच कामों का तालमेल बिठाने के लिए Turborepo, Nx, Lerna या pnpm के कार्यक्षेत्रों जैसे औज़ार बरतता है।

  • औज़ारों का साझा विन्यास सरल है: सबके लिए एक ESLint, एक TypeScript, एक Prettier।
  • आर-पार की पुनर्रचना आसान हो जाती है, क्योंकि सबका कोड एक ही जगह है।
  • निर्भरताओं का प्रबंधन केंद्रीकृत है, जिससे संस्करणों के बेमेल का जोखिम घटता है।
  • जब कोई बदलाव कई क्षेत्रों को छूता है, तो कई माइक्रो फ्रंटएंडों में फैली अविभाज्य प्रतिबद्धताएँ संभव हो जाती हैं।

कई भंडारों वाला तरीक़ा हर माइक्रो फ्रंटएंड को उसके अपने भंडार में रखता है, अपने पाइपलाइन और अपने विन्यास के साथ। टीमें अपनी तकनीकी थाती और रिलीज़ की प्रक्रिया पर पूरी स्वायत्तता रखती हैं। जो टीम React से Preact पर जाना चाहे, वह बिना तालमेल के जा सकती है, बशर्ते उसका माइक्रो फ्रंटएंड खोल में ठीक से दिखता रहे।

  • टीमों को अपने काम-काज और औज़ारों पर पूरी स्वायत्तता मिलती है।
  • भंडार छोटा बना रहता है, जिससे प्रतिलिपि फुर्तीली और पाइपलाइन कुशल रहते हैं।
  • एक-भंडार वाले विशेष औज़ार नहीं चाहिए: सामान्य Git प्रवाह और npm पंजिकाएँ काफ़ी हैं।
  • एक माइक्रो फ्रंटएंड की टूट दूसरे का निर्माण नहीं गिराती।

उद्योग का रुझान एक ही भंडार की ओर झुका है, ख़ासकर वहाँ जहाँ अभिकल्प प्रणाली या उपयोगिताओं का समूह साझा होता है। कई भंडारों के बीच बदलावों का तालमेल बिठाने की क़ीमत प्रायः स्वायत्तता के लाभ पर भारी पड़ती है, विशेषकर उन टीमों में जो पहले से पास-पास हैं और मिलती-जुलती तकनीकें बरतती हैं। फिर भी अलग-अलग ढाँचों पर काम करती बिखरी टीमों वाले संगठनों के लिए अलग भंडार ही सबसे अच्छा चुनाव रहता है।

निष्पादन के विचार

एकाश्म अनुप्रयोग की तुलना में माइक्रो फ्रंटएंड निष्पादन की एक क़ीमत लेकर आते हैं। हर एक अपना JavaScript बंडल, अपना CSS और संभवतः अपने ढाँचे का चालन-परिवेश उतारता है। ब्राउज़र को अधिक कोड उतारना, बाँचना और चलाना पड़ता है। भेजे जाने वाले बाइट बढ़ते हैं और पहली बार में पृष्ठ के काम लायक होने तक का समय लंबा हो जाता है।

Module Federation का निर्भरता-साझाकरण इस क़ीमत को कम करता है, क्योंकि वह किसी लाइब्रेरी को एक ही बार उतरने देता है। पर अनुप्रयोग का कोड — हर माइक्रो फ्रंटएंड के अवयव, उपयोगिताएँ और कारोबारी तर्क — साझा नहीं होता। यदि उपयोगकर्ता एक सत्र में तीन अलग माइक्रो फ्रंटएंडों से गुज़रे, तो वह तीनों का कोड उतारता है, भले उसने केवल एक से काम लिया हो।

हर माइक्रो फ्रंटएंड के भीतर कोड का विभाजन अनिवार्य है। हरेक को अपने रास्ते, अपने भारी अवयव और तीसरे पक्ष की लाइब्रेरियाँ आलस्य से उतारनी चाहिए। किसी प्रशासन-पटल वाले माइक्रो फ्रंटएंड में यदि आलेखों की लाइब्रेरी है, तो उसे तब तक नहीं उतरना चाहिए जब तक उपयोगकर्ता ऐसे पृष्ठ पर न पहुँचे जहाँ आलेख बनता है। यह वही चलन है जो एकाश्म अनुप्रयोगों में होता है, हर सीमा के भीतर लगाया गया।

पूर्व-लदान एक मूल्यवान सुधार है। जब उपयोगकर्ता भुगतान वाले माइक्रो फ्रंटएंड के किसी रास्ते पर आता है, तो खोल अंदाज़ लगा सकता है कि आगे आदेश की पुष्टि होगी और पृष्ठभूमि में उसके संसाधन लाना शुरू कर सकता है। यह रणनीति संचरण के नमूनों के बारे में अटकलों पर नहीं, बल्कि बरताव के असली आँकड़ों पर टिकी होनी चाहिए।

चित्रण के निर्णायक पथ की रक्षा ज़रूरी है। खोल को उतनी ही फुर्ती से आना चाहिए जितनी फुर्ती से कोई एकाश्म अनुप्रयोग, यानी उसे हल्का होना चाहिए: थोड़ा JavaScript, थोड़ा CSS, थोड़ी निर्भरताएँ। पहला चित्रण उसी के ज़िम्मे है, और धीमे माइक्रो फ्रंटएंडों को उसे टालना नहीं चाहिए। हरेक को अतुल्यकालिक ढंग से उतारा जाए और तैयार होने तक लदान की अवस्था दिखाई जाए।

निष्पादन के बजट केवल पूरे अनुप्रयोग के लिए नहीं, हर माइक्रो फ्रंटएंड के लिए तय होने चाहिए। हर टीम को अपने बंडल का अधिकतम आकार, काम लायक होने तक का अधिकतम समय और स्मृति की अधिकतम छाप पता होनी चाहिए। इनसे आगे निकलने पर पाइपलाइन गिरनी चाहिए, जैसा किसी एकाश्म अनुप्रयोग में होता।

माइक्रो फ्रंटएंडों का परीक्षण

इस आर्किटेक्चर का परीक्षण तीन स्तरों पर होता है: हर माइक्रो फ्रंटएंड अलग-थलग, उनके बीच का एकीकरण, और संयुक्त अनुप्रयोग समग्र रूप में। हर स्तर के अपने औज़ार, अपने लक्ष्य और अपने उत्तरदायी होते हैं।

हर माइक्रो फ्रंटएंड के भीतर इकाई और अवयव परीक्षण वही नमूने अपनाते हैं जो किसी भी फ्रंटएंड अनुप्रयोग में। हर टीम अपना परखती है और परीक्षण उसके अपने पाइपलाइन में चलते हैं। अंतर बस इतना है कि यह मानकर परखना बुद्धिमानी है कि खोल और दूसरे माइक्रो फ्रंटएंड भरोसेमंद नहीं: एहतियातन जाँचना कि खोल के अप्रत्याशित गुण भेजने पर या अपेक्षित घटना न आने पर आपका माइक्रो फ्रंटएंड शालीनता से टिका रहे।

एकीकरण सबसे कठिन स्तर है। परीक्षण को खोल और कई माइक्रो फ्रंटएंड एक साथ उतारने, सीमाओं के आर-पार की अंतःक्रियाएँ दोहराने और संयुक्त व्यवहार की शुद्धता जाँचने की ज़रूरत है। यहाँ Cypress और Playwright अव्वल हैं, क्योंकि वे असली ब्राउज़र में चलते हैं और पूरे अनुप्रयोग को चलाते हैं।

एक कारगर रणनीति है परीक्षण-परिवेश में उत्पादन जैसी तैनाती रखना: हर माइक्रो फ्रंटएंड अपने पते पर और खोल उन्हें उतारने के लिए सधा हुआ। परीक्षणों की जमात इसी तैनाती पर चलती है और सीमाओं के आर-पार जाती असली यात्राएँ तय करती है। इससे वे गड़बड़ियाँ उभरती हैं जो अलग-थलग परीक्षणों में नहीं दिखतीं: समय-मेल की विफलताएँ, जब एक माइक्रो फ्रंटएंड चालू ही हो रहा हो और दूसरा उससे बात करने लगे, या शैली के प्रतिगमन, जब एक में CSS का बदलाव दूसरे की सज्जा खिसका दे।

दृश्य प्रतिगमन परीक्षण यहाँ ख़ास तौर पर महत्वपूर्ण हैं। साझा अभिकल्प प्रणाली की अपनी जमात होनी चाहिए। हर माइक्रो फ्रंटएंड की अपने पृष्ठों के लिए अपनी। और संयुक्त अनुप्रयोग की निर्णायक यात्राओं के लिए अपनी। Percy, Chromatic या Loki पाइपलाइन में जुड़ जाते हैं और फ़र्क़ अपने आप पकड़ लेते हैं।

माइक्रो फ्रंटएंड कब न बरतें

माइक्रो फ्रंटएंड हर परियोजना के लिए सही आर्किटेक्चर नहीं हैं। वे जो जटिलता लाते हैं — संचालन की क़ीमत, निष्पादन की क़ीमत, तालमेल की ज़रूरत और छानबीन की कठिनाई — उसे केवल कुछ ख़ास संगठनात्मक ज़रूरतें ही जायज़ ठहराती हैं। जिस परियोजना को इनकी ज़रूरत नहीं, वहाँ इन्हें लगाना टकराव पैदा करता है और विकास को बिना किसी प्रतिफल के धीमा करता है।

एक ही अनुप्रयोग बनाती छोटी टीम को इनकी ज़रूरत नहीं। यह आर्किटेक्चर उन संगठनों के लिए सोची गई है जहाँ कई टीमें हैं और हरेक का अपना अलग क्षेत्र है। यदि आपका पूरा फ्रंटएंड तीन-चार लोग सँभाल सकते हैं, तो अच्छी तरह सजे मॉड्यूलों वाला एकाश्म अनुप्रयोग अधिक उत्पादक, बनाने में तेज़ और सँभालने में आसान होगा।

कसकर जुड़े क्षेत्रों वाला अनुप्रयोग बुरा उम्मीदवार है। यदि हर सुविधा बाक़ी सबसे उलझी हो, यदि सूची, टोकरी और भुगतान इतने गुँथे हों कि साफ़-साफ़ अलग न हों, तो उन्हें बाँटना आपको संवाद की जटिल परतें बनाने पर मजबूर करेगा, जो उसी सीधी पहुँच की नक़ल करेंगी जो एकाश्म में थी। यही वितरित एकाश्म वाला कु-नमूना है।

संचालन की परिपक्वता के बिना संगठन को कष्ट होगा। इस आर्किटेक्चर के लिए संचालन के अच्छे चलन, स्वचालित परीक्षण, निगरानी और घटनाओं पर प्रतिक्रिया चाहिए। हर टीम को अपने बल पर तैनात कर पाना और फुर्ती से वापस लौट पाना चाहिए। यदि ये बुनियादें नहीं हैं, तो बेहतर है पहले एकाश्म अनुप्रयोग के दायरे में इन्हीं पर निवेश किया जाए।

निष्पादन के लिहाज़ से निर्णायक अनुप्रयोग, जिनमें लदान के समय की कड़ी शर्तें हैं, शायद यह क़ीमत न झेल पाएँ। यदि हर मिलीसेकंड मायने रखता हो — जैसे किसी वास्तविक-समय व्यापार पटल में या किसी वीडियो अंतरापृष्ठ में — तो अतिरिक्त जाल-अनुरोध और JavaScript बाँचने का बढ़ा समय आपको बजट से बाहर कर सकते हैं।

  • आपकी फ्रंटएंड टीम में चार से कम लोग हैं।
  • अनुप्रयोग में साफ़ तौर पर अलग दिखने वाली पाँच से कम क्षेत्र-सीमाएँ हैं।
  • टीम को माइक्रोसर्विस या वितरित प्रणालियों का अनुभव नहीं है।
  • संगठन में स्वचालित सतत एकीकरण और निगरानी नहीं है।
  • अनुप्रयोग पर निष्पादन के कड़े बजट हैं, जिन्हें निभाना पहले ही कठिन है।
  • सुविधाएँ कसकर जुड़ी हैं और क्षेत्रों में साफ़-साफ़ नहीं बँटतीं।

माइक्रो फ्रंटएंड एक संगठनात्मक निर्णय हैं जो तकनीकी आर्किटेक्चर का रूप धरकर सामने आता है। यदि आपको वह संगठनात्मक स्वतंत्रता नहीं चाहिए जो वे देते हैं — अलग टीमें, अलग रफ़्तार, अलग तकनीकी चुनाव — तो तकनीकी क़ीमत चुकाना सार्थक नहीं।

निष्कर्ष

उन संगठनों के लिए माइक्रो फ्रंटएंड एक सशक्त नमूना हैं जिन्हें कई टीमों के बीच फ्रंटएंड विकास फैलाना है। यह आर्किटेक्चर असली लाभ देती है: स्वतंत्र तैनाती, टीमों की स्वायत्तता, तकनीकी छूट और अलग-अलग रफ़्तार पर रिलीज़ की गुंजाइश। सही समस्या पर लगाई जाए तो ये लाभ ठोस और मापे जा सकने योग्य होते हैं।

सफलता की कुंजी है इन्हें पहले संगठनात्मक हल और उसके बाद तकनीकी हल मानना। यह आर्किटेक्चर टीमों को स्वतंत्र बनाने के लिए है, सुघड़ ढंग से खुले-खुले कोड गढ़ने के लिए नहीं। हर तकनीकी निर्णय — Module Federation या iframe, एक भंडार या कई, घटना-वाहिनी या साझा भंडार — का निशाना यह होना चाहिए कि टीमों की रफ़्तार बढ़े और सुगठित अनुभव भी न टूटे।

छोटे से शुरू करें। एक ऐसे माइक्रो फ्रंटएंड से चलें जिसकी क्षेत्र-सीमा साफ़ हो और जिसे अपने बल पर चलाने को कोई टीम उत्सुक हो। दिखाएँ कि पाइपलाइन चलता है, कि निष्पादन टिका रहता है, कि टीम तेज़ी से देती है। फिर धीरे-धीरे फैलाएँ। यह सब-या-कुछ नहीं वाला मामला नहीं है: मिला-जुला तरीक़ा — अन्यथा एकाश्म अनुप्रयोग के भीतर एक-दो माइक्रो फ्रंटएंड — प्रायः सबसे अच्छी शुरुआत होती है।

सबसे आम चूक है समय से पहले अमूर्तन। टीमें अपनी असली ज़रूरतें समझने से पहले ही संचालन की विस्तृत परतें, साझा स्थिति की प्रणालियाँ और आर-पार फैली अधोसंरचना गढ़ बैठती हैं। सबसे सरल संभव एकीकरण से शुरू करें — एक खोल जो माइक्रो फ्रंटएंडों को script चिह्न या Module Federation से उतारे — और जटिलता तभी जोड़ें जब सरल का न चलना सिद्ध हो जाए। अमूर्तन की सही मात्रा असली उपयोग में उजागर होती है, पहले से किए गए अभिकल्प में नहीं।

Module Federation ने माइक्रो फ्रंटएंडों को पहले से कहीं अधिक सुलभ बना दिया है, पर बुनियादी सवाल अब भी संगठनात्मक ही हैं। क्या आपकी टीमों को स्वतंत्र रूप से तैनात करना है? क्या आपका संगठन संचालन की जटिलता झेल सकता है? क्या आपके क्षेत्र साफ़-साफ़ अलग होते हैं? जिनका उत्तर हाँ है, उन्हें ये कायाकल्प करने वाले लगेंगे। जिनका उत्तर नहीं है, उन्हें ये खिजाने वाले लगेंगे। आर्किटेक्चर स्वयं तटस्थ है: उसका मूल्य पूरी तरह उस संदर्भ पर निर्भर है जिसमें उसे लगाया जाता है।

See what your own repository can account for.

Thirty minutes on a repository you choose, including the part the record cannot attribute.