İçeriğe atla

Sunucu Tarafı Micro-Frontend Kompozisyonu: Tek Sayfa, Birçok Ekip

Sunucu tarafı micro-frontend kompozisyonu, sayfa parçalarının sahipliğini bağımsız ekiplere verir. Zor kısım sahiplik sınırı ve sürümlenmiş tutarlılık sözleşmesidir.

Ayhan Sipahi Ayhan Sipahi

Checkout ya da vitrin gibi tek bir yüksek trafikli sayfa, çoğu zaman bağımsız teslimat yapması gereken birçok ekibin katkısına ihtiyaç duyar. Frontend monolit, bu ekipleri tek bir sürüm trenine bindirir; böylece bir ekibin yavaş derlemesi veya bozuk testi diğer tüm ekiplerin deploy’unu engeller. Sayfayı birleştiren mekanizma kolay kısımdır; asıl zor problem, sahiplik sınırı ile birçok ekibi koordineli bir sürüm olmadan görsel ve davranışsal olarak hizalı tutan sürümlenmiş tutarlılık sözleşmesidir. Çok ekipli, SEO’ya duyarlı ve ilk boyamanın önemli olduğu bir sayfada varsayılan seçenek sunucu tarafı kompozisyondur; istemci tarafı kompozisyon ile iframe’ler daha dar senaryolarda öne geçer.

Module Federation mekaniğine, event-bus bağlantılarına ve çalışma zamanı hata ayıklamasına ihtiyacınız varsa istemci tarafı seriyle başlayın: Micro Frontend Mimari Temelleri ve Uygulama Desenleri. Buradaki odak, sunucu tarafı birleştirme modeli ve onu bir arada tutan sözleşmedir.

Küçük Bir Full-Stack Uygulama Olarak Parça#

Sunucu tarafı kompozisyonda sahiplik birimi parçadır (fragment): kendi sunucu tarafında render edilen HTML’i, hidrasyonu yapan kendi istemci paketi ve kendi durumu olan bağımsız bir uygulama. Bir ekip parçayı baştan sona sahiplenir. Kompozisyon katmanı parçanın içine asla erişmez; yalnızca parçanın render edilmiş işaretlemesini URL üzerinden çeker.

micro-frontends.org topluluk rehberi bunu somutlaştırır. Her ekip kendi sunucusunu çalıştırır ve render edilen çıktıya düz bir URL ile erişilir: /blue-buy?sku=t_porsche isteği, sayfaya yerleştirilmeye hazır, tamamen render edilmiş bir <button> döndürür. Podium aynı biçimi kullanır ve buna “podlet” adını verir: bir podlet sunucusu, “daha sonra tam bir HTML sayfası oluşturmak için bir [layout] sunucusunda kullanılabilecek HTML parçaları üretmekten sorumludur.” Desen framework’ten bağımsızdır ve Express birinci sınıf bir entegrasyon olarak desteklenir.

Node ile minimal bir parça sunucusu şöyle görünür. Kendi React alt ağacını sunucu tarafında render eder ve işaretlemeyi bir URL’de sunar.

// mini-cart parçası: kendi sunucusu, kendi React ağacı
import express from "express";
import { renderToString } from "react-dom/server";
import { MiniCart } from "./MiniCart";

const app = express();

app.get("/fragment/mini-cart", async (req, res) => {
  const cart = await loadCart(req.query.userId as string);
  const html = renderToString(<MiniCart cart={cart} />);

  // Bu parçanın varlıklarının nerede olduğunu kompozisyon katmanına bildir
  res.setHeader("Link", '</assets/mini-cart.js>; rel="fragment-script"');
  res.send(html);
});

app.listen(3002);

Kompozisyon katmanı bu URL’i çeker. Parça kendi render’ını, kendi veri çekme işlemini ve kendi varlıklarını sahiplenir. Sepetin iç yapısına dair hiçbir şey host’a sızmaz.

Kompozisyon Katmanı#

İstek anında bir layout host’u her parçanın işaretlemesini çeker ve yanıtları, tarayıcıya akış halinde gönderilen tek bir dokümana diker. Kanonik, düşük teknolojili mekanizma Server Side Includes’tır; burada host, sayfayı göndermeden önce <!--#include virtual="/blue-buy?sku=t_porsche" --> gibi yer tutucuları çözer. Cam Jackson’ın Martin Fowler makalesi aynı fikri bir Nginx ssi on; direktifiyle gösterir ve sunucu tarafı şablon kompozisyonunu “sıradan” ama meşru bir temel olarak konumlandırır.

Bir şablon host’u ham SSI’dan daha fazla kontrol sağlar. Arşivlenmiş ama iyi belgelenmiş bir OSS projesi olan Tailor (2022’den beri salt-okunur), bu biçim için net bir önceki örnektir: parçalar özel elemanlar olarak tanımlanır ve kompozisyon anında host’un dikkate aldığı öznitelikler taşır. Podium ise aynı fikir üzerine kurulu, aktif olarak bakımı yapılan bir alternatiftir.

<!-- Kompozisyon host'u tarafından çözülen layout şablonu -->
<html>
  <head><title>Checkout</title></head>
  <body>
    <fragment src="https://header.internal/fragment"></fragment>

    <fragment
      src="https://cart.internal/fragment/mini-cart"
      primary
      timeout="2000"
      fallback-src="https://cart.internal/fragment/mini-cart-empty">
    </fragment>

    <fragment
      src="https://reco.internal/fragment/recommended"
      async>
    </fragment>
  </body>
</html>

Öznitelikler operasyonel sözleşmeyi taşır. Tailor’da timeout varsayılan olarak 3000 ms’dir, primary bir parçanın sayfa yanıt kodunu belirlemesini sağlar, async bir parçayı body’nin sonuna erteler ve fallback-src, parça zaman aşımına uğradığında veya hata verdiğinde sunulacak bir URL belirtir. En önemlisi bu son özniteliktir: yavaş bir ekibin tüm sayfayı durdurmasını engelleyen, parça başına devre kesicidir.

İstek anındaki akış bir dağıtım (fan-out) ve bir dikiştir.

Reco fragmentMini-cart fragmentHeader fragmentComposition layerBrowserReco fragmentMini-cart fragmentHeader fragmentComposition layerBrowserpar[Parallel fan-out]GET /checkoutGET /fragmentGET /fragment (timeout 2000, fallback set)GET /fragment (async)header HTMLcart HTML (or fallback on timeout)stream stitched documentreco HTMLappend async fragment

Note

Akışlı bir host, yavaş parçalar çözülmeden önce doküman head’ini ve erken parçaları göndermeye başlayabilir; böylece en yavaş parça ilk byte’ı değil yalnızca kendi bölgesini geciktirir. Pahalı parçaları async ile işaretlemek ve onları ilk boyamanın ötesine ertelemek buradaki ana koldur.

Her Parçanın Sahiplendiği Kesişen Sorumluluklar#

Uluslararasılaştırma, kimlik doğrulama token’ları, gözlemlenebilirlik ve para biçimlendirme her parçanın kendi sorumluluğundadır. Parçaların paylaşılan bir DOM’da çakışmasını önlemek için micro-frontends.org’un “Ekip Önekleri Belirle” kuralı geçerlidir: CSS sınıflarını, özel olayları, local storage anahtarlarını ve çerezleri ekip bazında isim alanına alın. Sınıflarda cart- öneki ve olaylarda cart: öneki, sahipliği okunabilir kılar ve bir ekibin global seçicisinin başka bir ekibin işaretlemesini yeniden biçimlendirmesini önler.

Host’un ince kalmasının nedeni de budur. Host bir parçanın iç yapısı hakkında ne kadar çok şey bilirse, o kadar çok bağımlılık yeniden devreye girer. Host’un işi çekmek, dikmek, zaman aşımı uygulamak ve gerektiğinde fallback’e geçmektir; geri kalan her şey parçayı sahiplenen ekibe aittir.

Tutarlılık Sözleşmesi#

Mimarinin gerçek ekiplerle temas ettiğinde ayakta kalıp kalmayacağını asıl belirleyen kısım budur. Bağımsız render, yukarıdaki parça modeliyle çözülür. Onunla çözülmeyen şey görsel ve davranışsal tutarlılıktır: bir şey onları bir arada tutmadıkça her ekibin butonları, boşlukları, odak halkaları ve para biçimlendirmesi birbirinden uzaklaşır. O “şey” tutarlılık sözleşmesidir ve iki yarısı vardır.

İlk yarı sürümlenmiş paylaşılan bir tasarım sistemidir. Tutarlılık sürümlenmiş paylaşılan paketlerden oluşan bir kayıttan (registry) gelir: bir UI bileşen kütüphanesi ile bir tasarım-token paketi, her biri semver üzerinden tüketilir; böylece ekipler kendi temposunda yükseltme yapar. Cam Jackson bunu doğrudan destekler. Paylaşılan bileşen kütüphaneleri “kodun yeniden kullanımıyla azaltılmış efor ve görsel tutarlılık” sağlar ve “yaşayan bir stil rehberi” görevi görür. Aynı zamanda bir Foundation Platform’u çok erken kurmaya karşı uyarır; bunun yerine “ekiplerin kendi bileşenlerini oluşturmasına izin verin … bu bir miktar tekrara yol açsa bile” der, kanıtlanmış olanları ise daha sonra paylaşılan kütüphaneye taşıyın. Paylaşılacak ilk en iyi adaylar basit görsel ilkellerdir: ikonlar, etiketler, butonlar.

İkinci yarı kompozisyon protokolünün kendisidir: parça URL biçimi, öznitelik kuralları (timeout, primary, async, fallback-src) ve isim alanı kuralları. Bu, host ile her parça arasındaki arayüzdür ve yeni bir parçanın host değişmeden sayfaya katılmasını sağlayan şeydir.

own upgrade cadence

own upgrade cadence

own upgrade cadence

Shared registry: UI lib + design tokens (semver)

Header fragment: ui@^2.3

Mini-cart fragment: ui@^2.4

Reco fragment: ui@^2.1

Burada adı konması gereken gerçek bir gerilim var. micro-frontends.org “Ekip Kodunu İzole Et: tüm ekipler aynı framework’ü kullansa bile çalışma zamanını paylaşma ve paylaşılan duruma ya da global değişkenlere güvenme” der. Paylaşılan bir UI kütüphanesi ise tanımı gereği paylaşılan koddur. Her iki konum da farklı katmanlarda doğrudur. Çözüm nettir: semver üzerinden tüketilen derleme zamanı paketlerini paylaşın, ancak parçalar arasında asla bir çalışma zamanı singleton’u veya global durum paylaşmayın. Token’lar ve bileşenler her ekibin sabitlediği sürümlenmiş npm paketleri olarak gönderilir; tarayıcıda canlı bir paylaşılan nesneye dönüşmezler.

Bunu yanlış yapmanın bedeli tam olarak Cam Jackson’ın uyardığı şeydir. Ortak bağımlılıkları dışsallaştırmak “bir miktar derleme zamanı bağımlılığını yeniden devreye sokar … örtük bir sözleşme … hepimiz bu tam sürümleri kullanmalıyız … Kırıcı bir değişiklik olursa … büyük bir koordineli yükseltme çabası ve tek seferlik bir lockstep sürüm olayı. Bu, kaçınmaya çalıştığımız her şeydir …” Sunucu tarafı kompozisyonu çalışır kılan mühendislik disiplini sözleşme yönetimidir: semver disiplini, yalnızca ekleyen (additive) token değişiklikleri ve ekiplerin lockstep yerine kendi takvimlerinde yükseltme yapmasına yetecek kadar uzun kullanımdan kaldırma pencereleri.

Sunucu, İstemci ve Iframe Karşılaştırması#

Üç yaklaşım aynı özellikleri farklı şekilde takas eder: HTML’in tarayıcıya nasıl ulaştığı, sayfa içi rota geçişlerinin nasıl davrandığı ve bir parçanın sayfanın geri kalanından ne kadar izole olduğu.

FaktörSunucu tarafı kompozisyonİstemci tarafı (Module Federation / single-spa)Iframe
SEO / ilk boyamaGüçlü (HTML render edilmiş gelir)SSR ile eşlenmedikçe zayıfZayıf
Tarayıcı içi rota geçişleriVarsayılan olarak tam yeniden yüklemeGüçlü (SPA kabuğu)Zayıf
Stil / çalışma zamanı izolasyonuManuel (önekler, kapsamlı CSS)Manuel (paylaşılan singleton’lar sapma riski)Güçlü (yerel)
Ekip bağımlılığıDüşük (URL + öznitelik sözleşmesi)Derleme / çalışma zamanı sürüm sözleşmesiEn düşük
Hata yayılma alanıParça başına timeout + fallback-srcPaylaşılan bağımlılık kırılması yayılabilirSınırlı
Derin sayfa entegrasyonuİyiEn iyiZayıf (history, duyarlı yerleşim)

İstemci tarafı geçişi mevcut seri tarafından iyi karşılanır. Module Federation 2.0, Webpack 5’in yerleşik sürümünün üzerine bir Federation Runtime, bir Manifest, dinamik tip ipuçları ve bir çalışma zamanı eklenti sistemi ekler; büyük ölçekli tarayıcı içi kompozisyon için konumlanmıştır. single-spa, uygulamaları tarayıcıda bir root-config ile import map üzerinden birleştirir, modülleri bir orgName ile isim alanına alır ve Layout Engine’i üzerinden isteğe bağlı olarak SSR ekleyebilir. Her ikisi de sayfa oturum açma gerektiren bir uygulama kabuğu olduğunda doğru araçtır.

Yes

No

Yes

No

Yes

No

Multi-team page to compose

SEO / first paint critical?

Untrusted or legacy content?

In-browser route transitions dominate?

Server-side composition (default)

Client-side (Module Federation / single-spa)

Iframe isolation

Trade-off’lar#

Sunucu tarafı kompozisyon güçlü yanlarının bedelini öder. En önemli maliyet gecikme bağımlılığıdır: micro-frontends.org’un ifadesiyle, “en yavaş parça tüm sayfanın yanıt süresini belirler.” Hafifletmeler sözleşmenin zaten sağladıklarıdır: parça yanıtlarını önbelleğe alın, parça başına bir timeout ile bir fallback-src ayarlayın ve pahalı parçaları async ile işaretleyerek ilk boyamanın ötesine erteleyin ve istemci tarafında yüklenmelerini sağlayın. Ayrıca her parça kompozisyon host’unun yanında kendi sunucusu olduğu için daha fazla altyapı işletirsiniz.

İstemci tarafı kompozisyon bunu farklı bir maliyetle takas eder. Her micro-frontend kendi React kopyasını gönderebilir; bu yüzden Cam Jackson’ın deyimiyle “müşteriler React’i n kez indirir.” Paylaşılan bağımlılıkları dışsallaştırmak yükü düzeltir ama yukarıda anlatılan derleme zamanı bağımlılığını ve lockstep yükseltme riskini yeniden devreye sokar. Iframe’ler en güçlü izolasyonu yerel olarak verir ama yönlendirme ve history, duyarlı yerleşim ve derin entegrasyon zordur; Cam Jackson onları kullanma konusundaki çekincenin kısmen estetik, kısmen haklı olduğunu belirtir.

Sözleşmeyi Atlamak#

Micro-frontend’leri bir render teknolojisi seçimi olarak görmek temel hatadır. Sayfa, ekipler bölündüğü için bölünür; tasarım, organizasyon sınırıdır. Team Topologies aynı fikri, bir platformu X-as-a-Service etkileşim modu üzerinden tüketen akış-hizalı (stream-aligned) ekipler olarak çerçeveler ve kompozisyon katmanı tam olarak o platform hizmetidir.

Diğer tekrarlayan başarısızlıkların tümü sözleşmeyi atlamaya işaret eder:

  • Paylaşılan tasarım sistemi olmaması ya da bir Foundation Platform’u çok erken kurmak. İlki görsel sapmaya, ikincisi gereksiz yenilemeye yol açar. Baştan tek bir sistem dayatmak yerine kanıtlanmış ilkelleri daha sonra paylaşılan kütüphaneye taşıyın.
  • Parça başına zaman aşımı veya fallback olmadan eşzamanlı dağıtım. O zaman yavaş bir ekip tüm sayfayı durdurur. Her parçanın bir timeout ve bir fallback-src’ye ihtiyacı vardır.
  • Parçalar arasında global durum veya kopyalanmış framework örnekleri paylaşmak. Bu, micro-frontend’lere geçerken kaçındığınız çalışma zamanı bağımlılığını geri getirir. Ekip kodunu izole edin ve ekip önekleriyle isim alanına alın.
  • Paylaşılan UI kütüphanesi sürümünü “herkes birlikte yükseltir” olarak görmek. Bu, kaçmaya çalıştığınız lockstep sürümdür. Sözleşme, semver aralıkları ve kullanımdan kaldırma pencereleri aracılığıyla bağımsız yükseltme temposuna izin vermelidir.

Kapanış#

Varsayılan, sayfa çok ekipli, SEO’ya duyarlı ve ilk boyaması kritik kaldığı sürece geçerlidir. Sayfa, tarayıcı içi rota geçişlerinin ilk boyamadan daha önemli olduğu, kimlik doğrulamalı bir uygulama kabuğu haline geldiğinde Module Federation veya single-spa ile istemci tarafı kompozisyona geçin. İçerik güvenilmez ya da paylaşılan bir DOM’a güvenemeyeceğiniz eski (legacy) kod olduğunda bir iframe kullanın.

Kaynaklar#

İlgili yazılar

Nub vs Vite+: JS Yığınının Zıt Uçları İçin İki Rust Araç Zinciri

Nub ve Vite+, 2026'nın oxc tabanlı iki Rust araç zinciri; rakip gibi görünüp öyle değiller. Hangi ikilinin hangi repoda yeri olduğuna dair net bir kural.

javascript · typescript · vite +3

Shift-Left Security: İnceleme Kuyruğu Darboğazını Kaldırmak

Yüksek performanslı ekipler güvenlik incelemesini lead-time darboğazına çevirmiyor: shift-left otomasyon, risk-tabanlı kapılar, hazır yol ve bağımlılık ritmi.

ci-cd · devops · security +3

Frontend Platformu için Golden Path: İskelet, Paylaşılan Paketler ve Görev CLI'si

Bir frontend platform ekibi doğru yolu nasıl en kolay yol haline getirir: golden-path iskeleti, sürümlü paylaşılan paketler ve ekip kaymasını gideren bir görev CLI'si.

platform-engineering · developer-experience · build-tools +1

Sunum Servisi için Hexagonal Mimari: BFF Karşılaştırması

Bir UI parçasının arkasındaki ince sunum servisi yapışkan koda dönüşür. Port-ve-adaptör, çekirdeği somut hiçbir şeye bağımlı bırakmayarak bunu sürdürülebilir tutar.

architecture · nodejs · typescript +3

Kimlik Doğrulama vs Yetkilendirme: Temelleri ve İzin Sistemleri Neden Bozulur

Authentication ve authorization farkı, yaygın izin sistemi tuzakları, fail-closed prensibi ve her izin sisteminin karşılaması gereken hedefler.

typescript · nextjs · authorization +2