Merhaba, hepinize iyi günler dilerim. Umarım keyifli günler geçiriyorsunuzdur.
Bugünkü yazımda, üniversitemin son yılında geliştirdiğim “Multi-tenant E-Commerce Platform” adlı projemin hikayesini anlatacağım. Bu hikaye, projenin çıkış noktasından yol boyunca aldığım kararlara; atlattığım problemlerden projenin bugünkü haline kadar uzanan bir yolculuk. Ama sadece teknik bir anlatı olmayacak, zaman zaman bu süreçte hissettiklerime de yer vereceğim. Yani bu yazı hem bir proje anlatımı, hem de öz farkındalık ve özeleştiri içeren, biraz daha kişisel bir metin olacak.
İsterseniz devam edelim.
1. Giriş: Son Sınıfa Girerken
Son sınıfa başladığımda ne hissettiğimi tarif etmek hâlâ zor geliyor bana. Bir yandan “sonunda bitiyor” diye içim rahatlıyordu, bir yandan da içimde tarif edemediğim bir ağırlık vardı. Sanki dört yıldır biriktirdiğim her şeyin hesabı bu son yılda sorulacakmış gibi hissediyordum.
Bitirme projesi benim için basit bir ödev değildi açıkçası. İçten içe kendime bir şey ispatlamaya çalışıyordum galiba. “Elimden gelen bu mu?” sorusuna iyi bir cevap vermek istiyordum. Belki de bu yüzden kendime gereğinden fazla yüklendim, en başından beri.
Bu yazıyı da tam olarak bunun için yazıyorum. Hem geliştirdiğim projeyi anlatmak istiyorum, hem de o bir yıl boyunca içimde taşıdığım şeyleri. Çünkü bir bitirme projesi bitirilirken aslında görünmeyen bir sürü şey de bitiyor ya da başlıyor.
2. Proje Fikri: Neyi, Neden Yapmaya Karar Verdim?
Bir yıl boyunca üzerinde çalışacağım projenin konusunu seçmek benim için çok önemliydi. Uzun bir süre aynı konuyla haşır neşir olacaktım; dolayısıyla seçeceğim konu, en yorgun günümde bile beni masaya oturtacak kadar motive edici olmalıydı. Bunun yanında aradığım başka şeyler de vardı: beni geliştirmesi, bana yeni bakış açıları kazandırması ve en önemlisi, gelişimi hiç bitmeyecek bir proje olması. Çünkü gelişimi bitmeyen bir proje, sürekli yeni şeyler öğrenmek, yani sürekli gelişmek demekti.
Tüm bu kriterleri alt alta koyduğumda, bir e-ticaret platformu fikri beni gerçekten heyecanlandırdı. Gerçi dışarıdan baktığımda kendimi, içini göremediğim bir suya atlıyormuş gibi hissetmiştim açıkçası.
Nitekim o suya atladım.
3. Projemin Tanıtımı: Problem, Çözüm ve Hedef
Bir önceki bölümde bahsettiğim gibi, beni içine çekecek bir fikir arıyordum ve sonunda buldum: bir e-ticaret platformu geliştirecektim. Bu karardan sonra ilk işim, Trendyol ve Hepsiburada gibi popüler platformları eleştirel bir gözle incelemek oldu. Üstelik bu incelemeyi tek bir açıdan değil, hem müşteri hem de satıcı gözüyle yaptım.
Müşteri olarak platformlarda gezinirken beni rahatsız eden birkaç nokta vardı. Bunların ilki ve bence en önemlisi, tüm ürünlerin tek bir yerde listeleniyor olmasıydı. Yüzlerce mağazanın ürününün aynı sayfada yan yana durduğu bu yapıda, hangi mağazanın ne kadar güvenilir olduğunu kestirmek zordu. Kimi mağazalar yüksek puanlıyken kimileri oldukça düşüktü, bir kısmı da tam ortada bir yerdeydi. Kısacası, satın alacağım ürünün kalitesi konusunda içim bir türlü rahat etmiyordu.
Bu rahatsızlığın üzerine “Peki ben ne isterdim?” diye kendime sorduğumda, aklımda şu cümle belirdi:
“Bence kaliteli bir ürünün, bir pazaryerinde satılmaya ihtiyacı yok.”
İşin satıcı tarafına geçtiğimde ise kendime başka bir soru sordum: Bir mağaza sahibi olsaydım, ürünlerimi nasıl bir ortamda satmak isterdim? Bu sorunun peşinden popüler markaların satış politikalarını incelemeye başladım. Gördüğüm tablo netti: büyük markalar ürünlerini ortak bir pazaryeri yerine kendi siteleri üzerinden satıyor ve böylece kurumsal bir izlenim yaratıyordu. Satışa yeni başlamış küçük mağazalar ise Trendyol gibi pazaryerlerini tercih ediyordu — bunun arkasında da maliyet ve görünürlük kaygısının yattığını düşünüyordum.
Yani iki farklı satış modelinin de kendine göre avantajları vardı: markanın kendi sitesi üzerinden satış yapması ona kurumsal bir kimlik kazandırıyor ve müşteride güven oluşturuyordu; ortak pazaryerinde satış yapmak ise görünürlük sağlıyor ve maliyeti düşürüyordu.
Ben de tam bu noktada, iki modelin avantajlarını bir araya getirip dezavantajlarından sıyrılan bir çözüm hayal ettim. Fikrim şuydu:
Satışa yeni başlayan küçük ölçekli mağazalar, sunacağım hizmet sayesinde kısa sürede ve düşük maliyetle kendi online mağazalarına sahip olabilsin — böylece daha ilk günden kurumsal bir izlenim verebilsin.
Bu fikir bana fazlasıyla mantıklı geldi; çünkü henüz yeni kurulmuş bir mağazayı bile olduğundan çok daha güçlü gösterebiliyordu. Böylece projeme başlama kararını verdim.
4. Teknoloji ve Mimari Kararlarım
Proje fikrimi bulmuş, gerekli analizleri yapmıştım. Sırada bu projenin nasıl bir yapıda olacağına karar vermek vardı. Yazımın başında da belirttiğim gibi, bu projenin sadece bir “bitirme projesi” olarak kalmasını istemiyordum. Asıl amacım, uzun soluklu bir proje ortaya koymaktı . Üzerinde sürekli yeni şeyler öğrenebileceğim, öğrendiklerimi doğrudan uygulayabileceğim bir yapı kurmak istiyordum. Bu hedefle, tasarım sürecine önce kendi hedeflerimi netleştirerek başladım: Öğrenmeyi planladığım teknolojiler, araçlar ve mimari yaklaşımlar nelerdi? Projeyi de bu doğrultuda kurgulamaya karar verdim.
Mikroservis mimarisi konusunda teorik bilgim vardı ama hiç deneyimim yoktu. Bu projede hem mikroservisin avantaj ve dezavantajlarını bizzat yaşamak, hem de bir projenin mikroservis mimarisine nasıl evrilebileceğini öğrenmek istiyordum. Bu yüzden projemi, ileride mikroservise dönüştürülebilecek bir yapıda kurgulamam gerekiyordu. Bunun için de bana en uygun mimari yaklaşımı bulmak üzere bir araştırmaya giriştim.
O zamana kadar geliştirdiğim tüm projeler monolitikti. Presentation, Application, Domain ve Infrastructure olmak üzere dört katmanlı bir yapı kullanıyordum. Bu yapıda, uygulamanın ihtiyaç duyduğu tüm servislerin implementasyonları tek bir katmanda iç içe geçiyordu; bileşenler arasındaki bağımlılık neredeyse kaçınılmazdı. Açıkçası ortaya çıkan şey, birbirinin içine geçmiş bir sarmaşığa benziyordu. Belki o dönem daha sağlıklı bir yapı da kurabilirdim, ama şu anki bakış açısına henüz sahip değildim. Bugün kendimi daha “kuşbakışı” düşünebilen biri olarak görüyorum; bu da bazı şeylerin artık daha fazla farkında olduğumu gösteriyor sanırım.
Neyse, konuya dönelim. Mikroservis mimarisinin temelinde, her bir servisin diğerlerinden bağımsız olarak var olabilmesi yatar. Ama geçmişime baktığımda, ben hiç bu şekilde çalışmamıştım. İstediğim zaman, istediğim yerden referans alarak işi hallediyordum. (Bunun ne kadar sorunlu bir alışkanlık olduğunu, açıkçası bu projede çok daha net anladım.) Yani hem monolit mimarideki tecrübemi değerlendirmem, hem de mikroservise evrilebilecek bir yapı kurmam gerekiyordu.
Bu iki ihtiyacı bir araya getiren çözüm, Modüler Monolit Mimari oldu.

Modüler Monolit’te her servis, ayrı bir modül olarak tasarlanıyor. Örneğin Store modülü mağazayla ilgili iş kurallarını yönetirken, Customer modülü müşteriyle ilgili kuralları yönetiyor. Benim projemde de yaklaşık 10 modül var ve her biri, mümkün olduğunca yalın iş kurallarıyla kurgulandı.
Peki neden “mümkün olduğunca”? Çünkü bazı senaryolarda modüllerin iş birliği yapması kaçınılmaz. Örneğin bir sipariş oluşturulurken, ürünün fiyat ve stok bilgisine ihtiyaç duyulur, bu da modüller arası iletişimi gerektirir. Peki bu iletişim nasıl kurulmalı? İhtiyaç duyan modül, diğer modülün tüm iç detaylarını bilmeli mi?
Tabii ki hayır.

Bunun için her modülün kendi içinde bir Contracts katmanı var. Bu katman, modülün dış dünyaya sunduğu hizmetleri tanımlıyor. Bir modül başka bir modülden bilgiye ihtiyaç duyduğunda, yalnızca o modülün Contracts katmanına bağımlı olur ve orada tanımlanan hizmetleri kullanır. Örneğin bir sipariş oluşturma senaryosunda, Order modülü fiyat bilgisi için Pricing modülüne ihtiyaç duyabilir. Bunu karşılamak için Pricing modülünün Contracts katmanında IPricingModuleApi adında bir interface tanımlanır; diğer modüller bu interface üzerinden fiyatlandırma bilgisine ulaşabilir. Bu interface'in veriyi nasıl elde ettiği ise ilgili modülün Application katmanında tanımlanır. Yani bilgiyi kullanan modülün, implementasyon detaylarını bilmesine hiç gerek kalmaz.
Peki bir modülün dışarıdan bu ihtiyacı nasıl tanımlanıyor? Application katmanındaki use-case’ler içinde, dış kaynaktan veriye ihtiyaç duyduğumuzda tıpkı bir servis interface’i tanımlar gibi bir interface tanımlıyoruz. Bu interface, dış kaynaktan gelecek verinin bir nevi sözleşmesi oluyor. Bu sözleşmenin gerçek implementasyonu ise Infrastructure katmanında yapılıyor; burada, ihtiyaç duyulan bilgi ilgili modülün Contracts katmanından elde ediliyor.
Kısacası: Contracts, bir modülün dışarıya sunduğu hizmetleri; Integrations ise dışarıdan alınan verilerin nasıl kullanılacağını tanımlıyor.
5. Geliştirme Süreci: İlk Satırdan İlk Çalışan Versiyona
Geliştirme sürecine başlamadan önce kendimi teorik olarak sağlama almak istedim. Neyi, hangi sebeple, nasıl kullanmam gerektiğine dair pek çok kaynak okudum; uygulamayı düşündüğüm her yapının artılarına ve eksilerine hakim olmaya çalıştım. Bu hazırlık sürecinin ardından, öğrendiklerimi hayata geçirmeye başladım.
İlk olarak Store modülünü oluşturdum; çünkü Store, e-ticaret domain’inin yapı taşıydı — geri kalan her şey bir şekilde ona bağlanıyordu. Ardından Customer modülüyle devam ettim ve diğer modülleri de kendi mantığım doğrultusunda sırayla inşa ettim. Günün sonunda elimde yaklaşık 10 modül vardı ve bu modülleri şimdilik in-process, senkron bir şekilde haberleştirdim.
Böylece elimde temel seviyede çalışan bir yapı oluştu.
Temel ama eksik.
Peki eksik olan neydi?
6. İşler Sarpa Sarınca: Beklemediğim Problemler
Az önce bahsettiğim gibi modüllerimi belli bir mantıkla oluşturmuş, birbirlerine kendimce sağlam bir yolla bağlamış ve uygulamamı test etmeye başlamıştım.
Yazımın başında modüler monolit mimariyi kullandığımdan bahsetmiştim. Buna ek olarak, modüllerin veritabanlarını da birbirinden ayrı ele almıştım. İşte tam bu noktada, tutarlılık konusu benim için en can alıcı problem haline geldi.
Normal bir monolit projede tek bir veritabanı olduğu için, tüm işlemleri tek bir transaction içinde güvenle yürütebiliyordum. Herhangi bir yerde hata fırlatıldığında, tüm işlemler otomatik olarak geri alınıyordu, hiçbiri kaydedilmemiş oluyordu. Ama ben veritabanlarını ayırdığım için, tutarlılığı artık tek bir transaction ile yönetemez hale gelmiştim; çünkü her transaction yalnızca kendi veritabanıyla ilgiliydi. Bu yeni problemi nasıl çözebileceğimin arayışına girdim.
Modüller arasındaki iletişim de güvenli değildi. Örneğin bir modülden diğerine istek gönderilirken herhangi bir hata durumunda, modüller arası tutarsızlık ortaya çıkıyordu. Sorunun kaynağı, aradaki haberleşme bilgisinin kaybolabilmesiydi. Bir işlem sonrasında başka bir modülü de haberdar etmem ve orada da bir değişiklik tetiklemem gerekiyorsa, bu çağrıların bir şekilde kaydedilmesi gerekiyordu.
Bu problemi Outbox Pattern ile çözebileceğimi öğrendim. Bu yaklaşımda, bir modülde işlem gerçekleştiğinde ve başka bir modülün haberdar edilmesi gerektiğinde, modüldeki değişiklik kaydedilirken aynı anda veritabanındaki ayrı bir tabloya da bu çağrı kaydediliyor. Kritik nokta şu: bu iki kayıt işleminin aynı transaction içinde gerçekleşmesi gerekiyor. Aksi halde, değişiklik veritabanına kaydedilip event kaydedilmeyebilir , bu da bir hata anında, sistemin asla fark edemeyeceği bir tutarsızlığa yol açar. Bir worker aracılığıyla bu kayıtlı çağrılar bir message queue’ya gönderilir ve ilgili modüller bu şekilde haberdar edilir. Böylece iki modül arasındaki iletişimde bir hata yaşansa bile, event veritabanında kayıtlı olduğu için sistem eski haline döndüğünde aynı event tekrar kuyruğa yazılabilir ve tutarlılık korunmuş olur.
Çözümü burada anlattım, ama henüz hayata geçirmedim. Şu an elimde olan yalnızca teorik bilgi: projemin nerede eksik olduğunu ve bunu nasıl çözebileceğimi biliyorum.
Peki bu düzeltmeleri uygulamaya hazır mıyım?
Sanırım hayır.
7. Aynı Anda Her Şey: Dersler, Proje ve Gelecek Kaygısı
Bitirme projesi geliştirirken, bildiğiniz gibi aynı zamanda son sınıftaydık. Yani hala sorumlu olduğumuz dersler, her ders için ayrı sunumlar ve projeler vardı. Bir yandan bitirme projesini geliştirmeye çalışırken, bir yandan da bu sorumlulukları yetiştirebilme telaşı beni psikolojik olarak fazlasıyla yordu diyebilirim.
Dersler, projeler, sunumlar derken bitirme projesinin teslim tarihi de yaklaştıkça, dönem dönem kendimi ekstra kaygılı buldum. “Neyi nasıl yetiştireceğim?” diyerek strese girdiğim çok fazla an oldu. Sanırım beklentim yüksekti, ama yapabileceklerim sınırlıydı. Bu ikisi arasında bir yerde ya beklentimden vazgeçecektim, ya da elimden gelenden. Ben de beklentilerimden feragat etmeyi tercih ettim.
Bu beklentilerin içinde benim için en önemlisi, uygulamadaki tutarlılığı çok daha sağlıklı bir hale getirmekti. Ama yaşadığım zaman baskısı yüzünden, hedeflediğim ideal tutarlılığın biraz gerisinde kalmak zorunda kaldım. Projeyi teslim etmiş olsam da, hâlâ o ideal tutarlılığı sağlamaya çalışıyorum.
8. Teslim Günü: Jüri Karşısında
Teslim gününde jüriye projemi anlatırken, ilk olarak projemin hangi problemi çözmeyi amaçladığını anlatmakla başladım. Çünkü ne kadar iyi bir proje geliştirirsem geliştireyim, gerçek hayattaki bir ihtiyaca dokunmadığı sürece bunun pek bir anlamı olmayacaktı.
Bunun ardından, projeyi tasarlarken hangi faktörleri göz önünde bulundurduğumu, neyi neden tercih ettiğimi özenle anlattım. Tercihlerimin arkasındaki mantığı jüriye istediğim gibi aktarabildiğim için, sunumum oldukça iyi geçti.
Teslimin ertesi günü jüriden projemin beğenildiğine dair bir mesaj almak beni gerçekten çok mutlu etti. En azından, bunca zamandır doğru bir şeyler yapmaya çalıştığımdan emin oldum.
9. Geriye Dönüp Baktığımda: Keşkelerim ve Kazandıklarım
Geriye dönüp baktığımda, açıkçası “keşke” diyebileceğim bir şey pek yok gibi. Çünkü bu süreç boyunca her gün elimden gelenin en iyisini ve o an bildiğim en doğrusunu yapmaya çalıştığıma inanıyorum.
Tabii bu benim kendi değerlendirmem. Belki de zaman içinde, aslında öyle olmadığını fark edeceğim. :)
10. Projemin Şuandaki Hali
Geliştirdiğim proje şu anda bir MVP (Minimum Viable Product) olarak canlı ortamda çalışıyor. Yazının başından beri bahsettiğim gibi, şu anda eventual consistency konusunda kendimi geliştirmeye devam ediyorum; dolayısıyla projedeki gelecek güncellemeler de bu doğrultuda ilerleyecek.
Dilerseniz projeyi buradan ziyaret edebilirsiniz: shop.kayas.dev
Bu yazıda hem bitirme projesi sürecinden, hem de bir son sınıf öğrencisi olarak yaşadığım hislerden bahsetmeye çalıştım. Umarım sıkıcı olmamış, akıcı bir yazı olmuştur.
Okuduğunuz için teşekkür ederim, hepinize iyi günler dilerim.
Comments 0
You cannot comment because you are not logged in.
Log in to comment.