NX Monorepo, Micro-frontend, Build & Performans
Turkcell.com.tr ve Superonline.net gibi birden fazla büyük ürünü aynı ekipler geliştirdiğinde ortaya çıkan "kod paylaşımı" ve "performans" problemlerine verilen cevaplar.
NX Monorepo — neden tek repo?
Çoklu ürün (turkcell.com.tr, superonline.net) aynı buton/form/API çağrısı mantığını tekrar tekrar yazmasın diye, hepsi tek bir repo altında, paylaşılan libs/ ile geliştirilir. NX bu yapıyı yönetmek için: bağımlılık grafiği çıkarır, sadece değişen projeleri build/test eder (affected), kod paylaşımını kütüphane sınırlarıyla zorunlu kılar.
apps/
web-turkcell/ ← turkcell.com.tr
web-superonline/ ← superonline.net
libs/
ui-components/ ← paylaşılan buton, kart, form elemanları
data-access-billing/ ← fatura/ödeme API çağrıları
utils-formatting/ ← para birimi, tarih formatlama vb.
# Yeni bir paylaşılan kütüphane üret
nx generate @nx/react:library ui-components
# Sadece SON COMMIT'te değişen projeleri test et (tüm repoyu değil)
nx affected:test
# Proje bağımlılık grafiğini görselleştir
nx graphMicro-frontend — ne zaman gerekir?
Farklı ekipler aynı sayfanın farklı parçalarını (ör. header turkcell ekibinden, ödeme akışı ayrı bir ekipten) bağımsız deploy edilebilir parçalar halinde geliştirsin diye kullanılır. En yaygın teknik: Webpack Module Federation — bir app, başka bir app'in derlenmiş bir parçasını runtime'da "ödünç alır".
// webpack.config.js — superonline app, turkcell app'ten "header" alıyor
new ModuleFederationPlugin({
name: "superonline",
remotes: {
turkcell: "turkcell@https://turkcell.com.tr/remoteEntry.js",
},
shared: ["react", "react-dom"], // aynı React kopyasını paylaş
});Webpack vs Vite
| Webpack | Vite | |
|---|---|---|
| Dev sunucu | Tüm bundle'ı önceden derler → büyük projede yavaş başlar | Native ESM kullanır, dosyayı tarayıcı ister istemez derler → anında açılır |
| Prod build | Kendi bundler'ı | Rollup kullanır (dev'de esbuild) |
| Olgunluk / plugin | Çok olgun, dev ekosistemi devasa (özellikle NX/enterprise) | Daha genç ama hızla büyüyor, çoğu Webpack plugin'inin eşdeğeri var |
Performans optimizasyonu: lazy loading, code splitting, memoization
Turkcell ilanında ayrı ayrı sayılan bu üç kavram aslında birbirini tamamlar: ilk ikisi "gerekmeyen kodu indirme", üçüncüsü "gerekmeyen render'ı yapma" problemini çözer.
// Lazy loading + code splitting — bu parça sadece ihtiyaç anında indirilir
import { lazy, Suspense } from "react";
const FaturaOdemeSihirbazi = lazy(() => import("./FaturaOdemeSihirbazi"));
function OdemeSayfasi() {
return (
<Suspense fallback={<Yukleniyor />}>
<FaturaOdemeSihirbazi />
</Suspense>
);
}
// Memoization — gereksiz yeniden hesaplama/render'ı engeller
const ToplamTutar = memo(function ToplamTutar({ items }: { items: Item[] }) {
const toplam = useMemo(() => items.reduce((s, i) => s + i.price, 0), [items]);
return <span>{toplam} ₺</span>;
});React Profiler / DevTools ile hangi component'in ne sıklıkla ve neden yeniden render olduğunu görürsün — "neden yavaş" sorusuna tahminle değil, ölçümle cevap verirsin.
Lighthouse ve Core Web Vitals
| Metrik | Ne ölçer | İyi eşik |
|---|---|---|
| LCP (Largest Contentful Paint) | En büyük içerik ne zaman göründü | < 2.5s |
| INP (Interaction to Next Paint) | Tıklamadan ekranın tepki vermesine kadar geçen süre | < 200ms |
| CLS (Cumulative Layout Shift) | Sayfa yüklenirken içeriğin ne kadar "zıpladığı" | < 0.1 |
Egzersizler
1) Bir sayfanın LCP değeri 4.2s çıkıyor. İlk 3 şüphelin ne olurdu?
defer/async olmaması. 3) Yavaş sunucu yanıt süresi (TTFB) — SSR/API gecikmesi. Lighthouse raporundaki "Opportunities" bölümü genelde bunu doğrudan söyler.2) useMemo/useCallback her yere eklemek her zaman performansı artırır mı?
useMemo kendi başına bir bellek + karşılaştırma maliyeti taşır. Ucuz hesaplamalarda veya nadiren render olan component'lerde gereksiz karmaşıklık ekler. Doğru kullanım: React Profiler'da gerçekten pahalı olduğu ÖLÇÜLEN hesaplamalarda veya React.memo'lu bir child'a prop referansının her render'da değişmemesi gerektiği durumlarda.