
Kurumsal Katalog
İzmir merkezli bir LED aydınlatma imalatçısı için katalog sitesi ve yönetim paneli. Sepet, ödeme ve üyelik bilerek kapsam dışı: sitenin tek işi ziyaretçiyi WhatsApp veya telefon görüşmesine çevirmek. Ürün sayfaları veritabanından okunup derleme anında statik üretiliyor.
İşletme saf B2B imalat yapıyor: elektrikçi ya da taahhüt firması ölçüyü veriyor, üretim yapılıyor, ürün elektrikçinin kendi müşterisine gidiyor. Son tüketiciye doğrudan satış yok. Böyle bir işte varsayılan çözüm — "kurumsal siteye e-ticaret ekleyelim" — yalnız gereksiz değil, zararlı: satmayan bir akışa stok, ödeme, iade ve fiyat listesi yükü getiriyor ve rakamı herkese açık hâle getirdiği için ticari zemini de bozuyor. Gerçek problem satış yapmak değil, güven vermek ve konuşmayı başlatmak. İkinci mesele zamanlama: alan adı elde ama içerik hazır değilken sitenin bir şey göstermesi gerekiyordu.
Üç ziyaretçi tipi, tek dönüşüm hedefi. Elektrikçi ve elektrik taahhüt firması: tekrarlı sipariş veriyor, katalogda ürün tipini ve teknik bilgiyi arıyor, fiyatı zaten konuşarak alıyor. Mimar ve proje yüklenicisi: standart üründen çok özel imalat kapasitesine bakıyor. Elektrikçinin son müşterisi: siteyi bir güven referansı olarak görüyor — elektrikçi siteyi ona gösteriyor. Panelde ise tek kullanıcı var: işletme sahibi, ürünü ve gelen talebi yönetiyor.
Tek geliştirici. Kapsam kararının kendisi, veri modeli, katalog ve statik üretim yapısı, yönetim paneli, talep akışı ve dağıtım.
Next.js 16 App Router, Neon Postgres ve Drizzle. Uygulama iki bölüme ayrılmış: kamuya açık site ve `/admin` altındaki panel. Ürün detay ve kategori sayfaları `generateStaticParams` ile derleme anında üretiliyor ve parametre listesi veritabanından okunuyor — yani statik çıktı, panelin içeriğinden türüyor. Bu tasarımda ziyaretçi isteği veritabanına hiç ulaşmıyor; yalnız iletişim formu sunucuya yazıyor. Panel tarafı tamamen dinamik: ürün ve varyant tanımları, müşteri kayıtları, satış ve satış kalemleri, gelen talepler. Görseller nesne deposunda tutuluyor ve yükleme anında boyutları okunup kayda yazılıyor, böylece sayfa render edilirken görsel oranları biliniyor. Oturum imzalı bir token ile taşınıyor. Yayın anahtarı tek bir ortam değişkeni: açıkken uygulama tek ekrana iniyor, kalan yapı yerinde duruyor.
Kararın gerekçesi teknik değil ticari: bu işte sipariş ölçüyle ve konuşmayla alınıyor, rafta duran bir ürünle değil. Sepet eklemek, satışa katkısı olmayan bir akışa stok senkronizasyonu, ödeme entegrasyonu, iade süreci ve fiyat şeffaflığı yükü getirirdi. Kapsamın daralması mimariyi doğrudan basitleştirdi: ziyaretçi tarafında oturum yok, sepet durumu yok, stok kilidi yok.
ÖdünleşimSite kendi başına bir satış kanalı değil; dönüşümün ölçüsü tıklamada bitiyor, sonrası WhatsApp konuşmasının içinde kalıyor ve sistemde izi yok. Bir talebin siparişe dönüp dönmediği panelden okunamıyor, elle işleniyor.
Katalog nadiren değişiyor ve ziyaretçi trafiği tamamen okuma. Sayfaları derleme anında üretmek, çalışma anında veritabanı bağlantısını ortadan kaldırıyor: ziyaretçi isteği hiçbir zaman veritabanına gitmiyor, dolayısıyla bağlantı havuzu, soğuk başlangıç ve sorgu gecikmesi ziyaretçi tarafında bir sorun değil.
ÖdünleşimKatalog tazeliği dağıtıma bağlanıyor: panelde düzeltilen bir yazım hatası, yeni bir build alınana kadar vitrine yansımıyor. Bu kabul edilebilir çünkü katalog nadiren değişiyor; günde birkaç kez değişen bir katalogda aynı karar yanlış olurdu.
İçerik hazır olmadan alan adının boş durmaması gerekiyordu. Ayrı bir "yakında" sitesi kurmak yerine uygulamanın içine bir anahtar konuldu: açıkken site tek ekrana iniyor — marka, kısa açıklama, doğrudan iletişim — kapandığında tam site açılıyor. Bakılacak ikinci bir proje yok.
ÖdünleşimUygulama iki farklı görünüm taşıyor ve bunlardan biri günlük geliştirmede hiç açılmıyor; yakında modunun bozulduğu ancak açıldığında fark edilir. Anahtar istemciye açık bir değişken olduğu için de bir güvenlik sınırı değil, yalnız bir görünüm anahtarı.
Aydınlatma ürününde görsel, metinden daha çok iş yapıyor ve sayfa çoğunlukla görselden oluşuyor. Boyut bilgisi kayıtta durduğu için render sırasında oran biliniyor ve görsel yüklenene kadar yer kaymıyor. Alternatif — her sayfa üretiminde görseli ölçmek — build süresini görsel sayısıyla doğru orantılı büyütürdü.
ÖdünleşimBoyut, yüklemedeki anlık ölçüme güveniyor; nesne deposundaki bir görsel dışarıdan değiştirilirse kayıttaki boyut sessizce yanlış kalıyor. Yeniden ölçen bir doğrulama işi yok.
Drizzle ve Neon, panelin ihtiyaç duyduğu ilişkisel modeli — ürün, müşteri, satış, satış kalemi — migration geçmişiyle birlikte okunabilir tutuyor. Nesne deposu, görselleri depo ve veritabanı dışında tutarken derleme çıktısını da şişirmiyor. Oturum için tam bir kimlik çatısı yerine imzalı token yeterli oldu: panelde tek kullanıcı sınıfı var ve kayıt akışı, parola sıfırlama, sağlayıcı girişi gibi özelliklerin hiçbirine ihtiyaç yok — kullanılmayacak bir bağımlılığı taşımanın karşılığı yok. Turnstile, iletişim formunu tek dönüşüm noktası olduğu için koruyor. Zod hem form girdisinde hem ortam değişkeni doğrulamasında çalışıyor.
Panel imzalı oturum token'ı ile korunuyor ve panel rotaları site tarafından tamamen ayrı. İletişim formu Turnstile arkasında ve doğrulama sunucu tarafında yapılıyor — istemciden gelen doğrulama sonucu tek başına kabul edilmiyor. Yazma yolu Server Action → Zod → Drizzle; ziyaretçi tarafında başka yazma yolu yok. Müşteri ve satış kayıtları yalnız panel içinde görünüyor; kamuya açık hiçbir sayfa bu tabloları okumuyor ve statik üretim de bu tabloları hiç kullanmıyor. Ortam değişkenleri Zod ile doğrulanıyor.
Bu proje için saklanmış, tarih damgalı bir Lighthouse çıktısı bulunmadığı için buraya sayfa hızı skoru yazılmadı. Yapısal ölçü şu: ziyaretçi tarafında çalışma anında veritabanı sorgusu yok, sayfalar derleme çıktısı olarak sunuluyor. Görsel oranları kayıttan okunduğu için yükleme sırasında düzen kayması oluşmuyor. Paket analizi repoda hazır bir komut olarak duruyor.
Bu repoda otomatik test yok ve bunun en çok hissedildiği yer statik üretim ile panel arasındaki bağ: panelde yayından kaldırılan bir ürünün, bir sonraki build'e kadar sitede durup durmadığını doğrulayan bir şey yok. Bugün en azından üretim parametrelerini döndüren fonksiyonu saf hâle getirip test ederdim — Ustura'da uygunluk hesabında yaptığımın aynısı, orada saf fonksiyon kenar durumları tek çağrıyla test edilebilir kılmıştı. İkinci nokta kapsam: sepet ve ödemeyi dışarıda bırakma kararının arkasındayım, ama talebin siparişe dönüp dönmediği sistemde hiç görünmüyor. Bugün talebe basit bir durum alanı eklerdim; bu bir CRM kurmak değil, konuşmanın sonucunu kaydetmek olurdu — VAP Turizm'de rezervasyon durum geçmişi tam olarak bu işi görüyor.
Ne yapmak istediğinizi anlatın; ne kadar süreceğini ve nereden başlanacağını baştan söylerim.