From below of long thin identical blue cables connected to small round electrical connectors

Ordu merkezli 95 mağazalı bir perakende zinciri (gıda + temizlik), POS verisi 4 farklı sistem (mağaza tipi başına ayrı), e-ticaret Shopify, CRM Dynamics, ERP SAP B1 — analytics için her seferinde ayrı ayrı çekip Excel’de birleştirme yapıyordu. 6 ayda Microsoft Fabric’e geçildi. Bu yazı OneLake-tabanlı mimarinin sahadaki teknik notlarını paylaşıyor.

Eski Mimari (SaaS Salatası)

Sistem Veri Erişim
POS Tipi A (40 mağaza) Günlük satış, stok SFTP CSV
POS Tipi B (35 mağaza) Günlük satış, stok API REST
POS Tipi C (20 mağaza) Günlük satış, stok SQL Server export
E-ticaret Shopify Online sipariş, müşteri API GraphQL
Dynamics CRM Müşteri profil, kampanya OData
SAP B1 Stok, satınalma, finans HANA SQL

Sonuç: aylık satış raporu hazırlamak ~~3 gün manuel work, hata oranı yüksek, gerçek zamanlı görüş yok.

Microsoft Fabric Mimarisi

[Veri Kaynakları]
  POS A (CSV) ─┐
  POS B (API) ─┤
  POS C (SQL) ─┼──► [Dataflow Gen2 (Fabric)]
  Shopify     ─┤             ↓
  Dynamics    ─┤      [OneLake Lakehouse - Bronze]
  SAP B1      ─┘             ↓
                       [Notebook (Spark) - Silver]
                              ↓
                       [Notebook - Gold (curated)]
                              ↓
                       [Semantic Model]
                              ↓
                  [Power BI Direct Lake - 95 dashboard]

Fabric Capacity

F-SKU seçimi: F32 (32 capacity unit, ~$5K/ay) — başlangıç. Pause özelliği var (gece kapatılırsa fatura azalır).

Lakehouse Katmanları (Bronze/Silver/Gold)

Bronze: Ham Veri

# Notebook: bronze-layer-pos-a.ipynb
from pyspark.sql.functions import current_timestamp, lit

# Lakehouse'a Bronze tablosu yaz
df_pos_a_raw = spark.read.csv(
    "abfss://retail@onelake.dfs.fabric.microsoft.com/Files/landing/pos_a/2026/04/25/*.csv",
    header=True,
    inferSchema=True
)

df_bronze = df_pos_a_raw 
    .withColumn("ingested_at", current_timestamp()) 
    .withColumn("source_system", lit("POS_A"))

df_bronze.write.format("delta") 
    .mode("append") 
    .saveAsTable("bronze.pos_a_sales_raw")

Silver: Temizlenmiş + Birleştirilmiş

# Notebook: silver-layer-sales-unified.ipynb
from pyspark.sql.functions import col, regexp_replace, to_date

# 3 POS sistem birleşik schema
sales_a = spark.table("bronze.pos_a_sales_raw").select(
    col("magaza_id").alias("store_id"),
    col("urun_kod").alias("product_code"),
    to_date(col("satis_tarih"), "dd.MM.yyyy").alias("sale_date"),
    col("adet").cast("integer").alias("quantity"),
    col("tutar").cast("decimal(18,2)").alias("amount_try"),
    lit("POS_A").alias("source")
)

sales_b = spark.table("bronze.pos_b_sales_raw").select(...)  # benzer mapping
sales_c = spark.table("bronze.pos_c_sales_raw").select(...)

unified = sales_a.unionByName(sales_b).unionByName(sales_c)

# Stok master ile join (ürün kategorisi enrich)
products = spark.table("silver.dim_product")
sales_enriched = unified.join(products, "product_code", "left")

sales_enriched.write.format("delta") 
    .mode("overwrite") 
    .saveAsTable("silver.fact_sales")

Gold: Aggregate + Business-Ready

# Notebook: gold-layer-monthly-store-summary.ipynb

monthly_summary = spark.sql("""
    SELECT 
        s.store_id,
        st.store_name,
        st.region,
        DATE_TRUNC('month', s.sale_date) AS month,
        p.category,
        COUNT(DISTINCT s.transaction_id) AS transaction_count,
        SUM(s.quantity) AS total_quantity,
        SUM(s.amount_try) AS total_revenue_try,
        SUM(s.amount_try) / COUNT(DISTINCT s.transaction_id) AS avg_basket
    FROM silver.fact_sales s
    JOIN silver.dim_store st ON s.store_id = st.store_id
    JOIN silver.dim_product p ON s.product_code = p.product_code
    WHERE s.sale_date >= DATE_SUB(CURRENT_DATE(), 730)
    GROUP BY s.store_id, st.store_name, st.region,
             DATE_TRUNC('month', s.sale_date), p.category
""")

monthly_summary.write.format("delta") 
    .mode("overwrite") 
    .partitionBy("month") 
    .saveAsTable("gold.monthly_store_category_summary")

Pipeline Orchestration

Data Pipeline (Fabric):
  - Trigger: günlük 03:00 (POS gün kapanışı sonrası)
  - Activity 1: Copy CSV → Bronze (POS A)
  - Activity 2: API call → Bronze (POS B)
  - Activity 3: SQL extract → Bronze (POS C)
  - Activity 4: Notebook silver-layer-sales-unified
  - Activity 5: Notebook gold-layer-monthly-summary
  - Activity 6: Refresh semantic model
  - Activity 7: Email summary report
  - On failure: Teams webhook alert

Direct Lake Mode (Power BI)

Mode Veri kopyası Refresh Performans
Import Var (Power BI’a kopyalanır) Manuel/scheduled Hızlı, ama refresh sınırı
DirectQuery Yok Real-time query Yavaş büyük veri
Direct Lake Yok (OneLake’i direkt okur) Anlık (Delta files) Hızlı + güncel

Direct Lake = Fabric’e özel, OneLake’teki Delta tablosunu Power BI semantic model olarak kullanır. Performance Import’a yakın, freshness DirectQuery gibi.

Üretilen Power BI Dashboard’lar

  • CEO daily KPI (95 mağaza top-line)
  • Mağaza müdürü performans (kendi mağazası vs benzer)
  • Kategori müdürü stok devir hızı + sezonluk trend
  • E-ticaret funnel + cart abandonment
  • Müşteri segmentasyon (RFM analizi)
  • Promosyon ROI

Sonuçlar (6 Ay)

Metrik Önce Sonra
Aylık rapor hazırlama süresi ~~3 gün manuel ~~5 dk (otomatik refresh)
Veri freshness ~~aylık ~~günlük (gece batch)
Mağaza müdürü self-service raporlama Yok %80 müdür kendi raporunu çekiyor
Out-of-stock cevap süresi ~~3-4 gün ~~ertesi gün
Promosyon ROI doğruluk %~~60 (kabaca) %~~95 (item-level attribution)

Sahada Düşülen Üç Tuzak

  1. F-SKU’yu over-provision etmek: F64 → F32’ye düşürülünce performans aynı, fatura yarı. Önce baseline + monitor + scale.
  2. Bronze/Silver/Gold ayrımını skip etmek: Tüm transformation tek notebook’ta = bakım kabusu. Medallion architecture şart.
  3. Capacity’i 7/24 açık tutmak: Production capacity gece pause edilebilir (batch hariç). %~~30-40 fatura tasarrufu.

CloudSpark olarak Microsoft Fabric mimarisi, OneLake + Lakehouse tasarımı, medallion architecture, Spark notebook geliştirme, Direct Lake semantic model ve Power BI dashboard’lar için danışmanlık veriyoruz.

🇹🇷 Türkçe🇬🇧 English🇩🇪 Deutsch🇫🇷 Français🇸🇦 العربية🇷🇺 Русский🇪🇸 Español