Choreography-Based Saga Implementation

Saga Pattern Distributed Transactions

Saga Pattern dağıtık ortamda distributed transcation yönetilirken veri tutarlılığını hedefler. Implemente edilirken Choreography-Based Saga ve Orchestration-Based Saga olmak üzere iki yaklaşım vardır. Saga Pattern Nedir yazısında bu konu teorik olarak incelendi. Bu yazıda Choreography-Based Saga Implementation konusu üzerine pratik yapacağız.

Senaryo

Bir e-commerce sistemimiz olduğunu düşünelim. Bu basit sistem üzerinde sipariş, stok ve ödeme servisleriyle çalışacağız.

Saga Choreography Pattern

Yukarıda bulunan diyagramı incelediğimizde;

  1. Order.Api bir istekte bulunarak OrderService‘e CreateOrderCommand tipinde bir komut gönderiyor.
  2. OrderService aldığı bu komut ile Pending durumunda bir sipariş oluşturarak OrderCreatedEvent tipinde bir event publish ediyor.
  3. Bu event’e subscribe olmuş StockService, siparişte bulunan ürünlere bakarak stok kontrolü yapıyor. Yeterli stok bulunduğu taktirde stokları siparişteki ürün miktarı kadar azaltarak StockReservedEvent tipinde bir event publish ediyor. Aksi durumda StockNotReserved tipinde bir event yayınlıyor. Bu event’e subscribe olmuş OrderService tarafından oluşturulmuş siparişin durumu Rejected olarak güncelleniyor.
  4. Stok başarıyla düşürüldükten sonra sıra ödemenin alınmasına geliyor. PaymentService gerekli ödeme işlemlerini yaptıktan sonra başarılı olması durumunda PaymentConfirmedEvent tipinde bir event publish ediyor. Bunu dinleyen OrderService oluşturmuş olduğu siparişi Confirmed olarak işaretleyerek transaction’u tamamlıyor. Aksi durumda bu event’e subscribe olmuş StockService ve OrderService compensable transaction yaparak telafi ediyor.

Bu uygulamada bulunan tüm projeler MassTransit.AspNetCore ve MassTransit.RabbitMQ Nuget paketlerini kullanılmaktadır. Ayrıca proje içi konfigürasyonlara yazının uzamaması adına yer verilmemiştir, yazı sonunda proje paylaşılmıştır.

Order Api

Senaryomuza göre ilk giriş noktasıdır. Client tarafından tetiklenerek, controller aracılığıyla sipariş isteğini OrderService‘e iletecektir.

Görüldüğü üzere command, MassTransit aracılığıyla RabbitMQ altyapısıyla gönderiliyor.

OrderService

Sipariş isteklerini alarak Pending durumunda daha sonradan işlenmek üzere bir sipariş oluşturarak OrderCreatedEvent tipinde bir event publish eder.

Görüldüğü üzere gerekli business-logic işlendikten sonra sipariş oluşturuluyor ve event publish ediliyor.

StockService

Bir sipariş oluşturulduktan sonra siparişteki ürün adetleri kadar stoğun güncelleneceği servistir.

Görüldüğü üzere gerekli stok kontrolü bir değişken ile simüle ediliyor. Yeterli stok olması durumunda stok düşümü yapılarak StockReservedEvent, aksi durumda StockNotReserved event’i publish edilmektedir.

Stoğun yetersiz olması durumunda OrderService oluşturduğu siparişi bu evente subscribe olarak işliyor. StockService tarafından üretilen mesajı IConsumer<TMessage> arayüzünü implemente ederek consume ediyor. Nihayetinde ConsumeContext<TMessage> üzerinden siparişin durumunu Rejected olarak işaretliyor.

PaymentService

Stoğun başarıyla ayrılması durumunda sıra ödemenin alınmasına geliyor.

Ödeme bir değişken üzerinden simüle ediliyor. Ödemenin başarılı olması durumunda PaymentConfirmedEvent, aksi durumda PaymentRejectedEvent tipinde event publish ediliyor.

Öncelikle ödemenin başarılı olduğu durumda siparişin tamamlandığı Consumer sınıfına göz atalım.

OrderService tarafından dinlenen event, ödeme başarılı olduğunda bu mesajı consume ederek siparişin durumunu Confirmed olarak işaretliyor.

Ödemenin başarısız olduğu durumda bu sefer hem stoğun eski haline getirilmesi gerekiyor hem de sipariş durumunun değiştirilmesi gerekiyor.

Ödemenin başarısız olduğu durumda StockService tarafından ilgili mesaj consume edilerek gerekli stoklar eski haline getiriliyor.

Ödemenin başarısız olduğu durumda OrderService tarafından yine mesaj consume edilerek sipariş durumu Rejected olarak değiştiriliyor.

Görüldüğü üzere mikroservisler arasında distributed transaction veri tutarlılığını Choreography Pattern uygulanarak sağladık. Her bir servisin hem subscriber hem de publisher rolünde olduğuna dikkat edelim. Bu yaklaşımda servis sayısı -bir önceki yazıda değinildiği üzere- arttıkça yönetilmesi zor bir hale gelecektir. Bu durumda Orchestration-Based Saga yaklaşımını kullanmak daha doğru olacaktır.

Senaryoda geliştirilmiş olan projeye buradan ulaşabilirsiniz.

You may also like...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.