Photo by Vadim Sherbakov on Unsplash

Micro Frontend

Micro Frontends Birleştirme Yöntemleri (Module Federation)

Micro Frontend farklı farkı birleştirme yöntemleri bulunuyor, bunlardan bir taneside Module Federation yöntemi, bu yazıda Module Federation yönteminin nasıl çalıştığını anlatmaya çalışacağım.

Frontend Development With JS
5 min readMay 29, 2023

--

Micro Frontend Nedir ? Ne işe yarar ?

Bu konuda yaptığım araştırmaları Microfrontend Nedir ve Kullanılan Framework’ler blog yazısında anlatmıştım.

Daha sonra her bu modüllerin bir Shell (Kabuk) yapısı içerisinde birleştirilmesi gerekmektedir. Bu composition için farklı yöntemler bulunmaktadır .

Bu konuda yaptığım araştırmaları Container/Layout içerisinde Micro Frontends Birleştirme Yöntemleri blog yazısında anlattım.

Bu yöntemlerden bir tanesi’de Module Federation yöntemidir. Aslında yapılan oluşan JS bundle ve CSS çıktıları üzerinden birleştirme yöntemidir. Bu yöntem nasıl çalışıyor gelin buna bir bakalım.

Bu kısımda bizim bir Container uygulamamız ve bunun içerisinde birleştirebilmemizi sağlayacak diğer modüllerin ayrı ayrı bundle.js dosyaları olması gerekmektedir .

Yani RemoteEntry üzerinden modüller expose edilecek , ve Container vb.. ihtiyaç duyan modüller bu yapılar üzerinden bu bileşenleri kullanacaklar.

Bundler araçlarının bazıları module federation için pluginler sunar;

Bu da sizin altyapınızda kullandığınız Bundler ile basitçe bu işlemleri yapabilmenize olanak sağlar. Konuyu sırası ile bundler üzerinden basitçe nasıl çalıştığını anlatalım.

Webpack Module Federation

Webpack Configurasyon dosyasında öncelikle ModuleFederationPlugin kullanarak hangi Modülleri dışarı açmak istiyorsak onu belirtiyoruz.

MicroFrontend Webpack Konfigurasyonu

    plugins: [
new ModuleFederationPlugin({
name: "mfeModule1",
filename: "remoteEntry.js",
exposes: {
"./App": "./src/App",
},
shared: [
{
react: { singleton: true },
"react-dom": { singleton: true },
},
],
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],

Bu yöntemde bu işlemi exposes kısmında yapıyoruz. shared altında diğer micro frontend ler ile ortak kullanılacak kütüphanelerin isimleri yazılır. Şimdi gelin uygulama nasıl çalıştığına bakalım.

Aşağıdaki görselde gözükeceği gibi her bir modül network request sonrasında Lazy olarak sisteme yükleniyor.

Microfrontend kodlarını peki dışarıya nasıl sunuyoruz ?

Local ortamda projeleri npm start veya yarn start ile localde başlattığımızda Microfrontend uygulamaları farklı farklı portlarda ayağa kalkar. Örneğin

Prod ortamında projeleri build ettikden sonra AWS S3 → oradan Cloudfront üzerinde bir distributionId oluşturup bunun üzerinden sunabiliriz. (Not: Bu konuda daha detaylı bilgi için React Proje Mimarisi 13 — (Asset Management) içerisindeki CloudFront kısmını inceleyin)

  • MFE2 → ${distributionId}/mfe-app2/remoteEntry.js
  • MFE3 → ${distributionId}/mfe-app3/remoteEntry.js
  • MFE4 → ${distributionId}/mfe-app4/remoteEntry.js

Peki bu kısımlar Micro-Frontend uygulamalarını sunduğumuz kısımlar. Uygulamanın Shell kısmını nasıl handle edeceğiz

Shell (Kabuk) Yapısı

Şimdi geliştirilen ve sunulan micro frontend leri bir kabuk içerisinde birleştirme ordan geleni uygulamaya ekleme kısmına geldik.

Burada istenen Micro frontend kendi yapısına 2 şekilde ekleyebilir;

  • Static
  • Dynamic

Statik Bağlanma

Kabuk hangi micro frontend kullanacak ise bunu webpack module federation plugini içerisinde belirtmesi gerekmektedir.

Aşağıdaki şekilde bileşeni kendi yapısında remotes altında tek tek ekleyeceği url vermesi gerekiyor

module.exports = {
name: 'shell',
remotes: {
remote: 'remote@http://localhost:3002/remoteEntry.js',
},
shared: {
...dependencies,
react: {
singleton: true,
requiredVersion: dependencies['react'],
},
'react-dom': {
singleton: true,
requiredVersion: dependencies['react-dom'],
},
},
};

Tabi bu kısımdan sadece http://localhost:3002/remoteEntry.js üzerinden sunulan(expose) edilen bileşenleri kullanacağız.

Bu durumda kodun içerisindeki herhangi bir yerden MFE2 uygulamasını kullanabiliriz.

MFE2 from import(‘remote/App’));

const myShellPage=(props)=>{
return <MFE2/>
}

Fakat istediğimiz bu şekilde statik bir kullanımdan öte Micro frontend bizim yapımız tarafından şekilde lazy load edilip aşağıdaki şekilde kullanılabilir.

const MFE2 = React.lazy(() => import("remote/App"));

const myShellPage=(props)=>{
return (
<Suspense fallback={"loading..."}>
<MFE2/>
</Suspense>
)
}

Dinamik Bağlanma

Bu yöntemde ise hangi Micro frontend kullanacağımızı Module Federation Plugini içerisinde belirtmiyoruz .

    plugins: [
new ModuleFederationPlugin({
name: "shellApp",
shared: { react: { singleton: true }, "react-dom": { singleton: true } },
}),
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],

Bu bağlanma için elimizde RemoteEntry.js ulaşabilecek URL olması gerekiyor.

Bu Local ortamda veya Prod ortamında farklı URL karşılık geliyor.

  const remoteEntry2 = PRODUCTION
? `${distributionId}/mfe-app2/remoteEntry.js`
: "http://localhost:3002/remoteEntry.js";

Bu PRODUCTION olup olmadığını npm, .env dosyasında vererek ilgili ayarlamaları yapabilirsiniz.

 process.env.REACT_APP_PRODUCTION = true

React bu Env üzerinden RemoteEntry2 URL belirlemiş olacak. Bu URL üzerinden Webpack Module Federation sayfasında bulunan kod ile ilgili bileşeni/app kendi sistemimize yükleyebilirsiniz.

MFEComponent içerisine bakalım.


function MFEComponent=(props)=>{

const { ready, failed } = useDynamicScript({
url: props.system.url,
});


const Component = React.lazy(
loadComponent(props.system.scope, props.system.module)
);

return (
<Suspense fallback={"Loading ..."/>}>
<Component />
</Suspense>
);
}

1.useDynamicScript : uzakta URL olarak verilen bir Dynamic Script’in Host uygulamanın içerisine çekilmesi. Buna şundan dolayı ihtiyaç duyuyoruz. Host Module Federation Plugini içerisinde statik bağlamadık bir şekilde aşağıdaki URL üzerinden Host Dynamic eklememiz gerekiyor

const remoteEntry2 = PRODUCTION
? `${distributionId}/mfe-app2/remoteEntry.js`
: "http://localhost:3002/remoteEntry.js";

<MFEComponent url={remoteEntry2}>

useDynamicScript uzaktaki JS dosyasını Local’e nasıl yüklüyor ? document.createElement

const useDynamicScript = (args) => {
const [ready, setReady] = useState(false);
const [failed, setFailed] = useState(false);

React.useEffect(() => {
if (!args.url) {
return;
}

const element = document.createElement("script");

element.src = args.url;
element.type = "text/javascript";
element.async = true;

setReady(false);
setFailed(false);

element.onload = () => {
console.log(`Dynamic Script Loaded: ${args.url}`);
setReady(true);
};

element.onerror = () => {
console.error(`Dynamic Script Error: ${args.url}`);
setReady(false);
setFailed(true);
};

document.head.appendChild(element);

return () => {
console.log(`Dynamic Script Removed: ${args.url}`);
document.head.removeChild(element);
};
}, [args.url]);

return {
ready,
failed,
};
};

Aslında bu yapılan işlem Cam Jakson örneğinde anlattığımız bundle.js dokümana ekleme işlemi

inspired by the link’s diagram Micro Frontends

2. loadComponents : Burada uzaktan JS yükledik. Bu JS içerisindeki kod ve modül kapsamında ilgili UI bileşenini kendi Host uygulamamızda kullanacağız, bunun için aşağıdaki fonksiyon (scope: “mfeModule1”, module: “./App”) olarak çağırıp içerisinden bileşeni yükleyebiliriz.

function loadComponent(scope, module) {
return async () => {
// Initializes the share scope. This fills it with known provided modules from this build and all remotes
await __webpack_init_sharing__("default");
const container = window[scope]; // or get the container somewhere else
// Initialize the container, it may provide shared modules
await container.init(__webpack_share_scopes__.default);
const factory = await window[scope].get(module);
const Module = factory();
return Module;
};
}

Vite Module Federation

Vite Module Federation’da yukarıda anlattığım konudan farklı bir yapıda değil. İkisi’de benzer şekilde çalışıyor. Vite ile ilgili React örneğine aşağıdaki linkten erişebilirsiniz.

Referanslar

Okumaya Devam Et 😃

Bu yazının devamı veya yazı grubundaki diğer yazılara erişmek için bu linke tıklayabilirsiniz.

--

--