[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"\u002Fblog\u002Fmikroservis-event-driven-mimari":3,"\u002Fblog\u002Fmikroservis-event-driven-mimari-surround":260},{"id":4,"title":5,"author":6,"body":11,"date":249,"description":250,"extension":251,"image":252,"meta":253,"minRead":254,"navigation":255,"path":256,"seo":257,"stem":258,"__hash__":259},"blog\u002Fblog\u002Fmikroservis-event-driven-mimari.md","Velora'da mikroservisleri event bus ile konuşturmak",{"name":7,"description":8,"avatar":9},"Uhut Sancar","Frontend Developer",{"src":10,"alt":7},"\u002Fprofile.jpg",{"type":12,"value":13,"toc":238},"minimark",[14,18,21,26,29,48,55,59,62,77,80,84,91,111,129,132,136,139,160,168,172,186,193,200,207,211,217,221,224,227],[15,16,17],"p",{},"Velora, .NET ile geliştirdiğim mikroservis tabanlı bir e-ticaret altyapısı. Frontend tarafında çalışırken sürekli tükettiğim API'lerin arkasında ne olduğunu uçtan uca görmek istediğim için başladım.",[15,19,20],{},"Projede servisleri ayırmak beklediğimden kolay, onları birbirine bağlamak beklediğimden zor oldu. Asıl mesele servisi bölmek değil; bölündükten sonra aralarındaki iletişimi nasıl kuracağın.",[22,23,25],"h2",{"id":24},"servis-sınırlarını-nereden-çizdim","Servis sınırlarını nereden çizdim",[15,27,28],{},"Çözümde altı servis var: Identity, Catalog, Basket, Order, Payment ve Notification. Bunları teknik katmana göre değil, iş sürecine göre ayırdım. Her servisin kendi veritabanı, kendi modeli ve kendi API'si var.",[15,30,31,32,36,37,36,40,43,44,47],{},"Order tarafında katmanları da ayrı tuttum: ",[33,34,35],"code",{},"OrderService.Api",", ",[33,38,39],{},"OrderService.Application",[33,41,42],{},"OrderService.Domain"," ve ",[33,45,46],{},"OrderService.Infrastructure",". Domain kuralları dışarıya bağımlı olmayan bir yerde duruyor, veri erişimi ve dış entegrasyonlar Infrastructure'a düşüyor.",[15,49,50,51,54],{},"İstemciler servislere doğrudan gitmiyor. Önde Ocelot ile kurduğum bir API Gateway var; yönlendirme kuralları ",[33,52,53],{},"ocelot.json"," içinde tanımlı. Böylece Blazor istemcisi tek bir adres biliyor, arkadaki servis sayısı değişince istemci tarafı etkilenmiyor.",[22,56,58],{"id":57},"neden-senkron-çağrı-değil","Neden senkron çağrı değil",[15,60,61],{},"İlk refleks, sipariş oluşturulduğunda Order servisinin Payment servisini doğrudan çağırması oluyor. Bu çalışır ama iki servisi birbirine bağlar: Payment ayaktayken sipariş alınır, değilken alınamaz. Üstelik yarın araya Notification girdiğinde Order'ın kodunu tekrar açman gerekir.",[15,63,64,65,68,69,72,73,76],{},"Bu yüzden akışı event üzerinden kurdum. Order bir sipariş başlattığında ",[33,66,67],{},"OrderStartedIntegrationEvent"," yayınlıyor, kimin dinlediğini bilmiyor. Payment bu event'i dinleyip ödemeyi işliyor ve sonucuna göre ",[33,70,71],{},"OrderPaymentSuccessIntegrationEvent"," ya da ",[33,74,75],{},"OrderPaymentFailedIntegrationEvent"," yayınlıyor.",[15,78,79],{},"Order da bu sonuç event'lerini dinleyip siparişin durumunu güncelliyor. İki servis birbirinin adresini bilmiyor, sadece ortak bir sözleşmeyi biliyor.",[22,81,83],{"id":82},"event-busı-soyutlamak","Event bus'ı soyutlamak",[15,85,86,87,90],{},"Servislerin doğrudan RabbitMQ ile konuşmasını istemedim. ",[33,88,89],{},"BuildingBlocks"," altında ayrı bir EventBus katmanı kurdum.",[15,92,93,96,97,36,100,36,103,106,107,110],{},[33,94,95],{},"EventBus.Base"," sözleşmeleri tutuyor: ",[33,98,99],{},"IEventBus",[33,101,102],{},"IIntegrationEventHandler",[33,104,105],{},"IntegrationEvent"," ve abonelikleri tutan ",[33,108,109],{},"InMemoryEventBusSubscriptionManager",". Servisler yalnızca bu arayüzleri tanıyor.",[15,112,113,114,43,117,120,121,124,125,128],{},"Somut implementasyonlar ayrı projelerde: ",[33,115,116],{},"EventBus.RabbitMQ",[33,118,119],{},"EventBus.AzureServiceBus",". Hangisinin kullanılacağına ",[33,122,123],{},"EventBusFactory"," karar veriyor; konfigürasyondaki ",[33,126,127],{},"EventBusType"," değerine bakıp ilgili sınıfı üretiyor, varsayılan olarak RabbitMQ dönüyor.",[15,130,131],{},"Bu ayrımın pratik faydası şu: mesajlaşma altyapısını değiştirmek tek bir konfigürasyon satırı. Servis kodlarında tek satır değişmiyor.",[22,133,135],{"id":134},"kuyruk-isimlendirmesi-ve-normalizasyon","Kuyruk isimlendirmesi ve normalizasyon",[15,137,138],{},"Burası ilk denemede kafamı en çok karıştıran yerdi. Event sınıfının adı ile kuyruğun adı aynı olmak zorunda değil ve olmaması gerekiyor.",[15,140,141,144,145,43,148,151,152,155,156,159],{},[33,142,143],{},"ProcessEventName"," metodu, konfigürasyondaki ",[33,146,147],{},"DeleteEventPrefix",[33,149,150],{},"DeleteEventSuffix"," bayraklarına göre sınıf adının başındaki ve sonundaki ekleri temizliyor. ",[33,153,154],{},"GetSubName"," ise temizlenmiş isme ",[33,157,158],{},"SubscriberClientAppName"," değerini ekleyip abonelik adını üretiyor.",[15,161,162,163,167],{},"Bunun sonucu şu: aynı event'i dinleyen iki farklı servis, aynı exchange'e bağlı ama ",[164,165,166],"strong",{},"ayrı"," kuyruklara sahip oluyor. Aynı kuyruğu paylaşsalardı mesajı ikisi de değil, yalnızca biri alırdı. Ödeme başarılı olduğunda hem Order'ın durumu güncellemesi hem Notification'ın bildirim göndermesi gerektiği için bu ayrım kritik.",[22,169,171],{"id":170},"rabbitmq-tarafında-dikkat-ettiklerim","RabbitMQ tarafında dikkat ettiklerim",[15,173,174,175,178,179,72,182,185],{},"Bağlantıyı ",[33,176,177],{},"RabbitMQPersistentConnection"," içinde ayrı tuttum ve Polly ile üstel geri çekilmeli yeniden deneme ekledim. ",[33,180,181],{},"BrokerUnreachableException",[33,183,184],{},"SocketException"," alındığında her denemede bekleme süresi ikiye katlanıyor. Container'lar ayağa kalkarken RabbitMQ genelde uygulamadan sonra hazır oluyor; bu olmadan servis ilk saniyede patlıyordu.",[15,187,188,189,192],{},"Yayınlarken mesajı kalıcı işaretliyorum, kuyrukları ",[33,190,191],{},"durable"," açıyorum. Aksi halde broker yeniden başladığında bekleyen mesajlar kayboluyor.",[15,194,195,196,199],{},"Tüketirken mesajı otomatik onaylatmıyorum. Handler işini bitirdikten sonra ",[33,197,198],{},"BasicAck"," çağırıyorum. Otomatik onayda handler ortasında bir hata olursa mesaj gitmiş oluyor; manuel onayda iş gerçekten tamamlandıysa düşüyor.",[15,201,202,203,206],{},"Event işlenirken her mesaj için ayrı bir DI scope açılıyor, handler o scope içinden çözülüyor. Uzun ömürlü bir servisin içinde ",[33,204,205],{},"DbContext"," gibi scoped bağımlılıkların birbirine karışmaması için gerekli.",[22,208,210],{"id":209},"altyapıyı-ayrı-tutmak","Altyapıyı ayrı tutmak",[15,212,213,216],{},[33,214,215],{},"docker-compose-files"," altında RabbitMQ, Redis, SQL Server ve Consul için ayrı compose dosyaları duruyor. Hepsini tek dosyaya koymak yerine ayırdım; yalnızca RabbitMQ ile uğraştığım bir günde diğerlerini ayağa kaldırmak zorunda kalmıyorum.",[22,218,220],{"id":219},"geriye-kalan","Geriye kalan",[15,222,223],{},"Event driven yaklaşım servisleri gevşek bağlı yapıyor ama karşılığında bir bedel var: akışı tek bir yerden okuyamıyorsun. Bir siparişin neden tamamlanmadığını anlamak için üç servisin logunu birlikte bakmak gerekiyor.",[15,225,226],{},"Bu yüzden sıradaki adımlarım, event'lere korelasyon kimliği taşımak ve tüketici tarafını idempotent hale getirmek. Aynı mesaj iki kez düştüğünde sistemin aynı sonucu üretmesi gerekiyor; şu anki halinde bu garanti değil.",[15,228,229,230,237],{},"Proje devam ediyor, kaynak kodu ",[231,232,236],"a",{"href":233,"rel":234},"https:\u002F\u002Fgithub.com\u002Fuhutsancar\u002FVelora\u002Ftree\u002Fmain\u002FSellingBuddy",[235],"nofollow","GitHub'da"," duruyor.",{"title":239,"searchDepth":240,"depth":240,"links":241},"",2,[242,243,244,245,246,247,248],{"id":24,"depth":240,"text":25},{"id":57,"depth":240,"text":58},{"id":82,"depth":240,"text":83},{"id":134,"depth":240,"text":135},{"id":170,"depth":240,"text":171},{"id":209,"depth":240,"text":210},{"id":219,"depth":240,"text":220},"2026-06-12",".NET ile kurduğum SellingBuddy servislerinde sipariş ve ödeme akışını RabbitMQ üzerinden event driven hale getirirken servis sınırları, integration event'ler ve kuyruk isimlendirmesi üzerine öğrendiklerim.","md","\u002Fimages\u002Fmicroservices-event-driven.svg",{},7,true,"\u002Fblog\u002Fmikroservis-event-driven-mimari",{"title":5,"description":250},"blog\u002Fmikroservis-event-driven-mimari","JVpJjj0FzZ5uJab6sSrK6Hx4f79zVEJUCnmdXnbk4yc",[261,262],null,{"title":263,"path":264,"stem":265,"description":266,"children":-1},"MongoDB'de embed mi reference mı?","\u002Fblog\u002Fmongodb-embed-reference","blog\u002Fmongodb-embed-reference","Veri modelini uygulamanın sorgu şekline göre kurmak, embed ve reference arasındaki seçim ve bu kararın pagination ile index performansına etkisi."]