Event Odaklı Düşünmek: Yeni Başlayanlar için Komut ve Event
Event odaklı sistemlerin arkasındaki zihinsel değişimi öğrenin: komut vermek yerine olguları duyurmak. İsimlendirme, ayrıştırma, nihai tutarlılık ve idempotency.
Birçok ekip bir mesaj broker’ı ekler, RPC çağrılarının adını “event” olarak değiştirir ve komutlarla düşünmeye devam eder. Değişen isimdir, zihniyet değil. Bir komut hâlâ “şunu şimdi yap” der ve tek bir bilinen worker’a yöneliktir; yani hiçbir şey gerçekten ayrışmaz. Mesajlaşmanın operasyonel maliyetini ödersiniz, ama faydasını görmezsiniz.
request/response HTTP ve CRUD ile rahatsanız ama daha önce hiç event odaklı sistem kurmadıysanız, küçük bir değişim işi yerine oturtur. Talimat vermek (komut) yerine zaten olmuş olguları duyurun (event). Bu bir düşünme biçimi olduğu için, herhangi bir broker’a dokunmadan önce doğru yapabilirsiniz.
Komut mu, Event mi, Mesaj mı?
Üç kelime sanki aynı anlama geliyormuş gibi kullanılıyor. Gelmiyor.
Komut, bir şeyin yapılması için verilen bir talimattır. Belirli bir handler’a yöneliktir ve gönderen bunun olmasını bekler. Komutlar emir kipiyle okunur: PlaceOrder, ChargeCard, SendEmail. Sonuç göndereni bağlar. Handler yoksa, işlem başarısız olur.
Event, bir şeyin zaten olduğunu bildiren bir ifadedir. Bir olgudur, ilgilenen herkese duyurulur. Event’ler geçmiş zamanla okunur: OrderPlaced, CardCharged, EmailBounced. Producer kimin tepki verdiğini bilmez, umursamaz. Kimse dinlemiyor olsa bile producer yine de doğrudur.
Mesaj bunların hiçbiri değildir. Bir komutu ya da bir event’i hat üzerinden taşıyan zarftır. “Message-driven” taşımayı anlatır. “Event-driven” niyeti anlatır. DoThisNow mesajlarıyla dolu bir kuyruk message-driven’dır ama hâlâ komut biçimindedir.
Tek bir ayırt edici test çoğu durumu çözer. Bunu ele alması gereken tek bir servis adı söyleyebiliyorsanız, yoksa işlem başarısızsa, o bir komuttur. Producer sıfır consumer ile de aynı ölçüde doğruysa, o bir event’tir.
“Event” kelimesi fazla yüklüdür ve bunu baştan söylemekte fayda var. AWS bir event’i durum değişikliği olarak tanımlar. Confluent onu belirli bir anda olmuş bir şey olarak tanımlar. Martin Fowler ise “event-driven” teriminin en az dört farklı desen için kullanıldığını ve bunları tek çatı altında toplamanın kafa karışıklığı yarattığını belirtir. Burada event, zaten olmuş bir şeye dair, ilgilenen herkese duyurulan bir olgudur.
Event’leri Olgu Olarak İsimlendirin
Bir event ismi bir sözleşmedir. Ona öyle davranın.
Geçmiş zaman ve alan dili kullanın: OrderPlaced, PaymentReceived, InventoryReserved, EmailBounced. Her isim zaten doğru olan bir olguyu ifade eder. Ne olduğunu anlatır, sonra ne olması gerektiğini asla anlatmaz.
Sahte event’lerin çoğu tam da burada saklanır. SendWelcomeEmail, event kılığına girmiş bir komuttur. Tek bir servise ne yapacağını söyler. Onu, kendisini tetikleyen olguya, UserRegistered ismine çevirin; e-posta servisi kendi kararıyla tepki verebilir. Yani isim testi basittir: emir gibi okunuyorsa komuttur, geçmiş gibi okunuyorsa event’tir.
Event’in içinde olguyu tanımaya ve sonra fazlasını aramaya yetecek kadar bilgi taşıyın. OrderPlaced en azından sipariş id’sini taşımalı. Bundan fazlasını ne kadar taşıyacağı gerçek bir karardır ve buna aşağıda geri döneceğiz.
Producer, Consumer’larını Tanımaz
Bu, request/response’un sizden hiç istemediği tersine çevirmedir.
Request/response’da çağıran, çağrılanı import eder ya da adresler. Çağıran, haber verilecek herkesin listesini elinde tutar. Bir yan etki ekleyin, örneğin arama indeksini güncellemek, ve çağıranı tekrar düzenlersiniz. Producer, aşağı akıştaki her adımın bilgisini biriktirir.
Event odaklı bir sistemde producer bir olguyu bir kanala yayınlar ve durur. Consumer’lar kendileri abone olur. Producer onların isimlerini asla öğrenmez. Bu yüzden bir consumer eklemek producer’da sıfır değişiklik gerektirir. Gerçekten bir şeyi ayrıştırıp ayrıştırmadığınızın tek testi budur. Bir dinleyici eklemek yayıncıyı düzenlemek anlamına geliyorsa, elinizde hâlâ komutlar var demektir.
Çalıştırabileceğiniz Küçük Bir Örnek
İşte tersine çevirme, yaklaşık kırk satırda, süreç içinde, hiçbir altyapı olmadan. Node’un yerleşik EventEmitter’ını kullanır, yani kurmanız gereken hiçbir şey yok.
import { EventEmitter } from "node:events";
// Komut versiyonu (sıkı bağlı): sipariş kodu her yan etkiyi bilmek ve
// her birini doğrudan çağırmak zorundadır.
// sendConfirmationEmail(order);
// reserveInventory(order);
// Yeni bir tepki eklemek her seferinde bu fonksiyonu düzenlemek demektir.
// Event versiyonu: sipariş kodu bir olguyu duyurur ve durur.
const bus = new EventEmitter();
type OrderPlaced = {
eventId: string;
orderId: string;
email: string;
items: { sku: string; qty: number }[];
};
// Consumer 1: e-posta. Kendisi abone olur; producer onu hiç görmez.
bus.on("orderPlaced", (event: OrderPlaced) => {
console.log(`email: ${event.orderId} için ${event.email} adresine onay`);
});
// Consumer 2: envanter, idempotency korumasıyla. At-least-once teslimat,
// aynı event'in birden fazla kez gelebileceği anlamına gelir.
const processed = new Set<string>();
bus.on("orderPlaced", (event: OrderPlaced) => {
if (processed.has(event.eventId)) {
console.log(`inventory: yinelenen atlanıyor ${event.eventId}`);
return;
}
processed.add(event.eventId);
for (const item of event.items) {
console.log(`inventory: ${item.qty} x ${item.sku} rezerve ediliyor`);
}
});
// Sipariş kodu olguyu yayınlar. İki consumer'ı da import etmez, tanımaz.
const placed: OrderPlaced = {
eventId: "evt-1",
orderId: "order-42",
email: "[email protected]",
items: [{ sku: "BOOK-1", qty: 2 }],
};
bus.emit("orderPlaced", placed);
bus.emit("orderPlaced", placed); // aynı event'in yeniden teslimi
Bunu order.ts olarak kaydedin ve tip açıklamalarını yükleme sırasında ayıklayan Node 24 ile çalıştırın:
node order.ts
email: order-42 için [email protected] adresine onay
inventory: 2 x BOOK-1 rezerve ediliyor
email: order-42 için [email protected] adresine onay
inventory: yinelenen atlanıyor evt-1
Sipariş kodu orderPlaced yayınlar ve durur. E-postadan ya da envanterden hiç söz etmez. Bir arama indeksi consumer’ı eklemek, bir bus.on(...) daha ve sipariş kodunda sıfır değişiklik olurdu. İsim değişikliğinin tek başına asla veremediği ayrıştırma budur.
İkinci emit’e dikkat edin, aynı event’in yeniden teslimi. Envanter işlenmiş id kümesine bakar ve atlar. E-posta consumer’ının koruması yoktur, bu yüzden ikinci bir onay gönderir. Korumanın ortadan kaldırdığı tehlike budur. Bu senkron, süreç içi örnekte işlenmiş id’lerden oluşan bir Set yeterlidir, çünkü handler’lar hiç örtüşmez. Dağıtık bir sistem üçüncü bir durum ekler: ilk kopya hâlâ işlenirken gelen bir kopya. Buna aşağıda döneceğiz.
İnce Event’ler ve Kalın Event’ler
Bir event’in ne kadar durum taşıdığı, adı olan bir tasarım kararıdır. Fowler, “event-driven” altında toplanan desenleri ayırır ve bunlardan ikisi ilk sisteminizi şekillendirir.
Event notification ince versiyondur. OrderPlaced bir id’den fazlasını pek taşımaz. Consumer, ihtiyaç duyduğu ayrıntıları almak için producer’a geri çağrı yapar. Şema bağı düşük kalır, çünkü event’in neredeyse bir şeması yoktur. Bedeli bir çalışma zamanı bağımlılığıdır: consumer tepki verdiği anda producer’ın erişilebilir olmasına ihtiyaç duyar ve ağda daha fazla trafik olur.
Event-carried state transfer kalın versiyondur. Event, consumer’ın ihtiyaç duyduğu durumu taşır, yani consumer hiç geri çağrı yapmaz. Fowler’ın deyimiyle, kendi kopyasını tutan bir alıcı “never needs to talk to the main customer system” (ana müşteri sistemiyle hiç konuşması gerekmez). Bu, dayanıklılık ve daha düşük gecikme kazandırır. Bedeli ise yine onun deyimiyle “lots of data schlepped around and lots of copies” (ortalıkta çok fazla veri dolaşması ve çok fazla kopya) ve akıl yürütmeniz gereken bayat veridir.
İlk sistem için birini bilerek seçin ve adını koyun. İnce notification’larla başlamak, siz öğrenirken şemaları küçük tutar. Bir consumer, producer kapalıyken de çalışmaya devam etmek zorundaysa state transfer’e uzanın.
Fowler aynı terim altında iki desen daha sayar: event sourcing ve CQRS, ikisi de burada kapsam dışı. Event sourcing, event günlüğünün kendisini gerçeğin kaynağı olarak saklar. CQRS okumaları yazmalardan ayırır. İhtiyaç duyduğunuzda CQRS deseni yazısı daha derine iner.
Nihai Tutarlılık Bir Özelliktir
Ayrıştırdığınızda, okuma tarafı bir an için yazma tarafının gerisinde kalır. Sipariş verilir ve bir an sonra fatura görünümü yetişir. Bu pencere bir kusur değildir. Ayrıştırmanın ve erişilebilirliğin bedelidir ve genelde küçüktür.
Bunu bir ürün kararı olarak çerçeveleyin. “Fatura, ödeme adımından bir saniye sonra görünür” çoğu akış için sorun değildir ve dayanıklılık kazandırır: fatura servisi kısa süre kapalı olsa bile ödeme adımı bloke olmaz. Microsoft’un mimari kılavuzu nihai tutarlılığı bu stilin bilinçli bir ödünleşimi olarak ele alır, bir kaza olarak değil. Bu gecikme için akış başına bütçenizi belirleyin ve önemli olduğu yerde arayüzde görünür kılın.
Idempotency, Çünkü Teslimat Tekrarlanır
Çoğu broker at-least-once teslim eder. Bu, kopyaların olacağı anlamına gelir, yani her consumer iki kez çalışmaya güvenli olmalı. Bu sonradan yamalanacak bir uç durum değildir. İlk consumer’dan itibaren tasarladığınız bir özelliktir.
Temel desen, örnekteki korumadır. Event’ten bir anahtar türetin, genelde id’si ya da bir iş anahtarı. İşlediğiniz anahtarları kaydedin. Gördüğünüz her şeyi atlayın. Microsoft, sıralama ve yalnızca-bir-kez işlemeyi bu stilin süregelen zorlukları olarak sayar ve yanıt olarak idempotent işlemeyi gösterir.
Öğrenirken kuralı broker’dan bağımsız tutun: teslimatın tekrarlanabileceğini varsayın ve handler’ı iki kez çalışmaya güvenli yapın. Belirli bir broker’ın kesin teslimat garantilerine ihtiyaç duyduğunuzda, AWS mesajlaşma karşılaştırması farkları ele alır ve idempotency rehberi anahtarları, depolamayı ve süreç içi örneğin gösteremediği işlenirken gelen kopya durumunu ele alır.
Bir Dakikada Koreografi ve Orkestrasyon
Birkaç servis birbirinin event’lerine tepki vermeye başlayınca, nasıl koordine olacaklarını seçersiniz.
Koreografi, her servisin event’lere tepki verip kendi event’ini yaymasıdır; merkezi bir beyin yoktur. En üst düzeyde ayrıştırılmıştır ve genişletmesi kolaydır. Başarısızlık biçimi görünürlüktür: hiçbir tek yer akışın tamamını göstermez, bu yüzden “OrderPlaced’ten sonra ne oluyor?” sorusunu yanıtlamak zorlaşır.
Orkestrasyon, bir koordinatörün her adımı sırayla sürmesidir. İzlemesi daha kolaydır ve bir adım başarısız olunca telafi etmesi daha kolaydır, akışı bilen merkezi bir sahibin bedeli karşılığında.
Basit fan-out için koreografiyle başlayın. Çok adımlı bir iş işleminin sıralı, görünür ve telafi edilebilir olması gerektiğinde orkestrasyona uzanın. Saga deseni yazısı bu durumu derinlemesine ele alır.
Yeni Başlayanların Sık Düştüğü Tuzaklar
İlk sistemdeki acının çoğu altı başarısızlık biçiminden gelir. Her birinin doğrudan bir çözümü var.
Event kılığına girmiş komutlar. Bir bus’a yayınlanan bir SendEmail tipi hâlâ bir komuttur. Çözüm: olguyu isimlendirin, OrderPlaced, ve e-posta servisi tepki vermeye kendisi karar versin.
Exactly-once varsaymak. Yeniden teslimatta müşteriyi iki kez ücretlendirmek. Çözüm: at-least-once varsayın ve her consumer’ı idempotent yapın.
Sahipsiz event çorbası. Onlarca event tipi ve kimse kimin ürettiğini ya da tükettiğini söyleyemiyor. Çözüm: bir event kataloğu tutun. Her event’in tek bir sahip producer’ı ve belgelenmiş consumer’ları olsun.
Paylaşılan şema üzerinden bağlanma. Birçok ekibin düzenlediği tek bir kalın tip, her değişiklik için koordineli bir deploy gerektirir. Çözüm: şemayı producer sahiplensin, ekleyerek versiyonlasın ve consumer’lar bilinmeyen alanları yok saysın.
Soru sormak için event kullanmak. Veri almak için bir event yayınlayıp yanıt beklemek, RPC’yi kötü bir şekilde yeniden kurar. Çözüm: sorgular request/response’tur. Event’ler olgu duyurur; soru sormazlar.
Sıra varsaymak. Event’lerin gönderdiğiniz sırayla geleceğini beklemek. Çözüm: global sıraya güvenmeyin. Sıranın önemli olduğu yerde anahtar ya da versiyon kullanın, ya da sıraya aldırmayan handler’lar yazın.
Buradan Nereye
Yeni başlayan için güvenli varsayılan çoğu ilk sistemde geçerlidir: olguları geçmiş zaman event’leri olarak modelleyin, ince notification’larla başlayın, consumer’ları idempotent tutun ve basit fan-out’u koreografik bırakın. Belirli bir ihtiyaç ortaya çıkınca bir istisnaya uzanın. Bir consumer, producer kapalıyken de çalışmak zorundaysa event-carried state transfer’i seçin. Çok adımlı bir işlemin sıralı ve telafi edilebilir olması gerektiğinde orkestrasyonu seçin. Event sourcing ya da CQRS’e ancak günlüğün kendisi ya da ayrı bir okuma modeli maliyetini hak ettiğinde uzanın.
Tek bir sonraki adım tüm bunlardan daha küçük. Bir sonraki özelliğiniz için, herhangi bir handler yazmadan önce geçmiş zaman event ismini yazın. İsim geçmiş gibi okunuyorsa, event’lerle düşünüyorsunuz. Emir gibi okunuyorsa, hâlâ komut veriyorsunuz.
Daha derine inmek için: event storming bir ekibin event’leri birlikte keşfetmesine yardımcı olur, domain-driven design bu isimlerin geldiği alan dilini keskinleştirir, outbox deseni bir event yaymayı ve veritabanınıza yazmayı atomik kılar ve event odaklı CRM anlatımı desenleri işlenmiş tek bir sistemde gösterir.
Kaynaklar
- What do you mean by “Event-Driven”? - Martin Fowler - Tek terim altında toplanan dört desen; event notification ile event-carried state transfer için birincil kaynak.
- Event Collaboration - Martin Fowler - Bileşenler istek yapmak yerine bir şey değiştiğinde event yayınlar; request-event tersine çevirmesinin kaynağı.
- Event Sourcing - Martin Fowler - Event günlüğünü gerçeğin kaynağı olarak saklamak; bu yeni başlayan rehberinin kapsamadığı sınırı işaretlemek için anıldı.
- Event Message - Enterprise Integration Patterns - Diğer uygulamalara bir şeyin olduğunu bildirmenin kanonik deseni.
- Command Message - Enterprise Integration Patterns - Başka bir uygulamada bir prosedür çağırmanın kanonik deseni; komut-event ayrımını sabitleyen karşıtlık.
- What is an Event-Driven Architecture? - AWS - Event’in bir durum değişikliği olduğu ve producer, router ve consumer’ların ayrıştığı satıcı çerçevesi.
- Event-Driven Architecture Style - Microsoft Learn - Bilinçli bir ödünleşim olarak nihai tutarlılık, ayrıca idempotency ile yanıtlanan sıralama ve yalnızca-bir-kez işleme zorlukları.
- What is an Event? - Confluent, Apache Kafka 101 - Streaming dünyasının tanımı: event, belirli bir anda olmuş bir şeydir.
- Eventarc overview - Google Cloud - CloudEvents üzerine kurulu, üçüncü bir satıcının producer ve consumer modeli; desenin tek buluta bağlı olmadığını gösterir.
İlgili yazılar
API, ödeme ve mesaj tüketicisi geliştirenler için idempotency'ye pratik giriş: HTTP metot semantiği, idempotency key'leri, upsert ve yaygın tuzaklar.
Microservices mimarisinde AWS Step Functions ve EventBridge kullanarak Saga pattern implementasyonu: idempotency, compensation logic ve production-ready pattern'ler.
Nub ve Vite+, 2026'nın oxc tabanlı iki Rust araç zinciri; rakip gibi görünüp öyle değiller. Hangi ikilinin hangi repoda yeri olduğuna dair net bir kural.
Sunucu tarafı micro-frontend kompozisyonu, bağımsız ekiplerin tek bir sayfanın parçalarını sahiplenmesini sağlar. Zor kısım sahiplik sınırı ve sürümlenmiş tutarlılık sözleşmesidir.
Tek bir PII branded type'ı observability API imzalarınıza yerleştirin; TypeScript hassas alanları runtime redactor görmeden, çağrı yerinde reddetsin.