﻿
{"id":26819,"date":"2024-01-07T10:40:23","date_gmt":"2024-01-07T10:40:23","guid":{"rendered":"https:\/\/www.gencayyildiz.com\/blog\/?p=26819"},"modified":"2024-01-07T10:40:23","modified_gmt":"2024-01-07T10:40:23","slug":"nette-rebus-kutuphanesiyle-saga-orchestration-yaklasiminin-uygulanmasi","status":"publish","type":"post","link":"https:\/\/www.gencayyildiz.com\/blog\/nette-rebus-kutuphanesiyle-saga-orchestration-yaklasiminin-uygulanmasi\/","title":{"rendered":".NET&#8217;te Rebus K\u00fct\u00fcphanesiyle Saga Orchestration Yakla\u015f\u0131m\u0131n\u0131n Uygulanmas\u0131"},"content":{"rendered":"<div id=\"fb-root\"><\/div>\n<p>Merhaba,<\/p>\n<p>Malumunuz, microservice mimarisiyle tasarlanm\u0131\u015f yaz\u0131l\u0131mlar\u0131m\u0131zda kullan\u0131c\u0131lar taraf\u0131ndan ba\u015flat\u0131lan i\u015flem ya da g\u00f6revlerin, bir servis taraf\u0131ndan al\u0131n\u0131p ard\u0131ndan belirli servisler \u00fczerinde mant\u0131ksal bir tetikleme silsilesiyle i\u015flenme ve koordine edilme s\u00fcre\u00e7lerini y\u00fcr\u00fctmemize ve biryandan da bu s\u00fcre\u00e7lerde data consistency&#8217;i(veri tutarl\u0131l\u0131\u011f\u0131) sa\u011fl\u0131kl\u0131 bir \u015fekilde sa\u011flayabilmemize imkan tan\u0131yan Saga pattern&#8217;\u0131n\u0131 kullanmakta ve \u00f6zellikle bu pattern&#8217;\u0131n uyguland\u0131\u011f\u0131 senaryolar da servis say\u0131s\u0131n\u0131n adil kotadan fazla oldu\u011fu durumlarda uygulanma zorlu\u011fu ve geli\u015ftirme maliyeti e\u011frisini d\u00fc\u015f\u00fcrebilmemizi sa\u011flayacak olan Orchestration yakla\u015f\u0131m\u0131n\u0131 tercih etmekteyiz.<\/p>\n<p>E\u011fer ki konuya dair bilgi sahibi olmaks\u0131z\u0131n direkt olarak bu i\u00e7eri\u011fe merak sald\u0131ysan\u0131z \u00f6ncelikle Saga pattern&#8217;\u0131n\u0131n ne oldu\u011funu anlayabilmek i\u00e7in <a href=\"https:\/\/www.gencayyildiz.com\/blog\/microservice-mimarilerde-saga-pattern-ile-transaction-yonetimi\/\" rel=\"noopener\" target=\"_blank\">Microservice Mimarilerde Saga Pattern \u0130le Transaction Y\u00f6netimi<\/a> ba\u015fl\u0131kl\u0131 makaleyi, ard\u0131ndan da Orchestration implementasyonunu ele ald\u0131\u011f\u0131m\u0131z <a href=\"https:\/\/www.gencayyildiz.com\/blog\/microservice-saga-commands-orchestration-implemantasyonu-ile-transaction-yonetimi\/\" rel=\"noopener\" target=\"_blank\">Microservice \u2013 Saga \u2013 Commands\/Orchestration Implementasyonu \u0130le Transaction Y\u00f6netimi<\/a> ba\u015fl\u0131kl\u0131 i\u00e7eri\u011fi okuman\u0131z gerekmektedir. Ha ayriyeten Microservice mimarisinde Saga&#8217;n\u0131n bir di\u011fer alternatif yakla\u015f\u0131m\u0131 olan Choreography implementasyonunu da <a href=\"https:\/\/www.gencayyildiz.com\/blog\/microservice-saga-events-choreography-implemantasyonu-ile-transaction-yonetimi\/\" rel=\"noopener\" target=\"_blank\">Microservice \u2013 Saga \u2013 Events\/Choreography Implementasyonu \u0130le Transaction Y\u00f6netimi<\/a> ba\u015fl\u0131kl\u0131 makaleden inceleyerek olay\u0131n dibini s\u0131y\u0131rabilir ve bu i\u00e7erikten istifade edebilecek altyap\u0131y\u0131 kusursuz bir hale getirebilirsiniz.<\/p>\n<p>Velhas\u0131l, e\u011fer ki konunun terminolojisine tam teferruatl\u0131 bir hakimiyet sa\u011flad\u0131ysan\u0131z bu i\u00e7eri\u011fimizde Orchestration yakla\u015f\u0131m\u0131n\u0131n Rebus k\u00fct\u00fcphanesiyle farkl\u0131 bir tekni\u011fini pratik a\u011f\u0131rl\u0131kl\u0131 olarak ele alaca\u011f\u0131m\u0131z\u0131 d\u00fc\u015f\u00fcnebilirsiniz.<\/p>\n<h3>Rebus Nedir?<\/h3>\n<p>Rebus, distributed sistemlerde servisler aras\u0131 asenkron mesajla\u015fma i\u00e7in kullan\u0131lan bir mesajla\u015fma k\u00fct\u00fcphanedir. Azure Service Bus, RabbitMQ, SQL Server, InMemory vs. gibi mesaj ta\u015f\u0131ma g\u00f6revinden sorumlu olan transporter isimli bir\u00e7ok sistemi desteklemektedir. Ayr\u0131ca, belirli bir d\u00fczen i\u00e7inde \u00e7al\u0131\u015fan i\u015f ad\u0131mlar\u0131n\u0131 veya microservice&#8217;lerin g\u00f6rev ger\u00e7ekle\u015ftirme s\u00fcre\u00e7lerini y\u00f6netmek i\u00e7in de kullan\u0131labilmektedir.<\/p>\n<h3>Rebus \u0130le Orchestration \u0130mplementasyonu<\/h3>\n<p>Bunu direkt \u00f6rnek bir senaryo \u00fczerinden ad\u0131m ad\u0131m inceliyor olaca\u011f\u0131z. Senaryomuz temsilen, klasik e-ticaret sistemlerindeki sipari\u015f s\u00fcre\u00e7lerine kar\u015f\u0131n olacakt\u0131r. Senaryo s\u00fcrecinde a\u015fa\u011f\u0131daki servislerden istifade ediyor olaca\u011f\u0131z;<\/p>\n<table style=\"font-size:12px;\">\n<thead>\n<tr>\n<th style=\"text-align:left;\">Servis<\/th>\n<th style=\"text-align:left;width:82px\">T\u00fcr\u00fc<\/th>\n<th style=\"text-align:left;\">A\u00e7\u0131klama<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align:left;\">Order.API<\/td>\n<td style=\"text-align:left;\">Web API<\/td>\n<td style=\"text-align:left;\">Kullan\u0131c\u0131 taraf\u0131ndan ba\u015flat\u0131lan sipari\u015fin al\u0131nmas\u0131n\u0131 ve sipari\u015fe dair stok ve \u00f6deme s\u00fcrecinin ba\u015flat\u0131lmas\u0131n\u0131 sa\u011flayacak olan servistir.<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left;\">Order.Saga<\/td>\n<td style=\"text-align:left;\">Worker Service<\/td>\n<td style=\"text-align:left;\">\u0130lgili sipari\u015fe dair servisler aras\u0131ndaki ad\u0131m ad\u0131m i\u015fleyi\u015f neticesinin y\u00fcr\u00fct\u00fcld\u00fc\u011f\u00fc handler&#8217;lar\u0131n bar\u0131nd\u0131r\u0131laca\u011f\u0131 servistir. D\u00fc\u015f\u00fcnd\u00fc\u011f\u00fcn\u00fcz zaman bu servisin sorumlulu\u011funu direkt Order.API&#8217;\u0131n alabilece\u011fine ve sanki l\u00fczumsuz oldu\u011funa kanaat getirebilirsiniz. Ancak, Order.API&#8217;\u0131n kullan\u0131c\u0131yla etkile\u015fime girmekten ba\u015fka mesaj t\u00fcketme sorumlulu\u011funu \u00fcstlendirmek ilkelere ayk\u0131r\u0131 olaca\u011f\u0131ndan dolay\u0131 bu sorumlulu\u011fu farkl\u0131 bir servise y\u00fcklemek best practice a\u00e7\u0131s\u0131ndan daha uygun olacakt\u0131r.<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left;\">Stock.Service<\/td>\n<td style=\"text-align:left;\">Worker Service<\/td>\n<td style=\"text-align:left;\">Stok i\u015flemlerinin y\u00fcr\u00fct\u00fclece\u011fi servistir.<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left;\">Payment.Service<\/td>\n<td style=\"text-align:left;\">Worker Service<\/td>\n<td style=\"text-align:left;\">\u00d6deme i\u015flemlerinin y\u00fcr\u00fct\u00fclece\u011fi servistir.<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left;\">Shared<\/td>\n<td style=\"text-align:left;\">Class Library<\/td>\n<td style=\"text-align:left;\">Servisler aras\u0131nda kullan\u0131lacak t\u00fcm ortak message ve event yap\u0131lanmalar\u0131n\u0131 ve bunlar\u0131n yan\u0131nda belli ba\u015fl\u0131 queue isimleri gibi konfig\u00fcrasyonlar\u0131 bar\u0131nd\u0131racak olan ortak bir katmand\u0131r.<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left;color:red;\">Saga.Orchestration.Service<\/td>\n<td style=\"text-align:left;\">Worker Service<\/td>\n<td style=\"text-align:left;\">Servisler aras\u0131ndaki i\u015f ve g\u00f6rev s\u00fcrecini merkezi bir noktadan y\u00f6netmek \u00fczere tasarlanacak olan ve b\u00f6ylece Orchestration yakla\u015f\u0131m\u0131n\u0131 sa\u011flayacak olan servisin ta kendisidir.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Ve bu servisler aras\u0131ndaki i\u015f ak\u0131\u015f\u0131 da a\u015fa\u011f\u0131daki gibi olacakt\u0131r;<\/p>\n<ol>\n<li>Kullan\u0131c\u0131 <code style=\"color:purple;\">Order.API<\/code> \u00fczerinden bir sipari\u015f talebinde bulunacak ve s\u00fcreci ba\u015flatacakt\u0131r.<\/li>\n<li><code style=\"color:purple;\">Order.API<\/code> sipari\u015fi veritaban\u0131na ekleyecek ve bunu <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;e bildirecektir.<\/li>\n<li><code style=\"color:purple;\">Saga.Orchestration.Service<\/code> ise olu\u015fturulan sipari\u015fe dair <code style=\"color:purple;\">Stock.Service<\/code>&#8216;e bilgide bulunacakt\u0131r. <code style=\"color:purple;\">Stock.Service<\/code> sipari\u015fteki \u00fcr\u00fcnlere g\u00f6re stok \u00e7al\u0131\u015fmas\u0131 ger\u00e7ekle\u015ftirilecektir. Stok i\u015flemleri ba\u015far\u0131l\u0131 ya da ba\u015far\u0131s\u0131z olmak \u00fczere sonu\u00e7lanacak ve neticeye g\u00f6re <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;e bildiride bulunulacakt\u0131r.\n<ol>\n<li>E\u011fer ki, <code style=\"color:purple;\">Stock.Service<\/code> sipari\u015fe dair stok i\u015flemlerini ba\u015far\u0131yla ger\u00e7ekle\u015ftirdiyse o taktirde <code style=\"color:purple;\">Payment.Service<\/code>&#8216;e \u00f6deme i\u015flemleri i\u00e7in bilgide bulunacakt\u0131r.<\/li>\n<li>Yok e\u011fer ba\u015far\u0131s\u0131z bir durum s\u00f6z konusuysa i\u015fte o taktirde <code style=\"color:purple;\">Order.Saga<\/code> servisine sipari\u015fin ba\u015far\u0131yla sonu\u00e7lanamad\u0131\u011f\u0131na dair bilgi mesaj g\u00f6nderecek ve ilgili sipari\u015f iptal edilecektir.<\/li>\n<\/ol>\n<\/li>\n<li><code style=\"color:purple;\">Payment.Service<\/code>&#8216;te ise ilgili sipari\u015fe dair \u00f6deme ba\u015far\u0131l\u0131ysa e\u011fer bunun i\u00e7in <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;e bildiride bulunulacak ve o da, <code style=\"color:purple;\">Order.Saga<\/code>&#8216;ya sipari\u015fin ba\u015far\u0131yla sonu\u00e7land\u0131\u011f\u0131na dair mesaj yay\u0131nlayacakt\u0131r.<\/li>\n<li>Yok e\u011fer \u00f6deme ger\u00e7ekle\u015ftirilemediyse hem sipari\u015fin iptal edilmesi hem de <code style=\"color:purple;\">Stock.Service<\/code>&#8216;te ilgili sipari\u015fe dair yap\u0131lan stok i\u015flemlerinin geri al\u0131nmas\u0131 i\u00e7in <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;e bildiride bulunacak, o da, sipari\u015fi iptal etmesi i\u00e7in <code style=\"color:purple;\">Order.Saga<\/code>&#8216;ya ve yap\u0131lan stok \u00e7al\u0131\u015fmalar\u0131n\u0131n geri al\u0131nmas\u0131 i\u00e7in de <code style=\"color:purple;\">Stock.Service<\/code>&#8216;e mesaj yollayacakt\u0131r.<\/li>\n<\/ol>\n<h5>\u0130lk Ad\u0131m<\/h5>\n<p>Her \u015feyden \u00f6nce yukar\u0131da olu\u015fturdu\u011fumuz projelere ilk ad\u0131m olarak a\u015fa\u011f\u0131daki k\u00fct\u00fcphanelerin y\u00fcklenmesi gerekmektedir;<\/p>\n<div style=\"text-align:center;\">\n<a href=\"https:\/\/www.nuget.org\/packages\/Rebus.ServiceProvider\" rel=\"noopener\" target=\"_blank\">Rebus.ServiceProvider<\/a><br \/>\n<a href=\"https:\/\/www.nuget.org\/packages\/Rebus.RabbitMq\" rel=\"noopener\" target=\"_blank\">Rebus.RabbitMq<\/a><br \/>\n<a href=\"https:\/\/www.nuget.org\/packages\/Rebus.SqlServer\" rel=\"noopener\" target=\"_blank\">Rebus.SqlServer<\/a>\n<\/div>\n<h5>Kazmay\u0131 Vural\u0131m<\/h5>\n<p>Projelere ilgili k\u00fct\u00fcphaneleri y\u00fckledi\u011fimize g\u00f6re art\u0131k genel senaryonun in\u015fas\u0131na ba\u015flayabiliriz. Bunun i\u00e7in a\u015fa\u011f\u0131daki ad\u0131mlar\u0131 s\u0131ras\u0131yla seyretmemiz yeterli olacakt\u0131r kanaatindeyim&#8230;<\/p>\n<ul>\n<li><strong><em>Ad\u0131m 1<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Order.API &#8211; (Order Entity&#8217;sinin Olu\u015fturulmas\u0131, Veritaban\u0131 Altyap\u0131s\u0131n\u0131n Kurgulanmas\u0131 ve Migration \u0130\u015flemleri)<\/span><br \/>\n\u0130lk olarak kullan\u0131c\u0131 taraf\u0131ndan verilecek olan sipari\u015fin <code style=\"color:purple;\">Order.API<\/code> taraf\u0131nda altyap\u0131s\u0131n\u0131 kurgulamam\u0131z gerekmektedir. Bunun i\u00e7in a\u015fa\u011f\u0131daki &#8216;Order&#8217; entity&#8217;sinden istifade edebiliriz.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class Order\r\n    {\r\n        public int Id { get; set; }\r\n        public int BuyerId { get; set; }\r\n        public List&lt;OrderItem&gt; OrderItems { get; set; }\r\n        public OrderStatus OrderStatus { get; set; }\r\n        public DateTime CreatedDate { get; set; }\r\n        public decimal TotalPrice { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Tabi bu entity i\u00e7erisindeki &#8216;OrderItem&#8217; ve &#8216;OrderStatus&#8217; t\u00fcrleri de a\u015fa\u011f\u0131daki gibi olacakt\u0131r.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderItem\r\n    {\r\n        public int Id { get; set; }\r\n        public int ProductId { get; set; }\r\n        public int Count { get; set; }\r\n        public decimal Price { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public enum OrderStatus\r\n    {\r\n        Suspend,\r\n        Completed,\r\n        Fail\r\n    }\r\n<\/pre>\n<\/div>\n<p>Sipari\u015f entity&#8217;sini olu\u015fturdu\u011fumuza g\u00f6re \u015fimdi s\u0131rada DbContext nesnesini olu\u015fturmam\u0131z gerekmektedir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class ApplicationDbContext : DbContext\r\n    {\r\n        public ApplicationDbContext(DbContextOptions options) : base(options)\r\n        {\r\n        }\r\n\r\n        public DbSet&lt;Order.API.Models.Entities.Order&gt; Orders { get; set; }\r\n        public DbSet&lt;OrderItem&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Ard\u0131ndan olu\u015fturulan bu yap\u0131y\u0131 uygulaman\u0131n &#8216;Program.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki gibi yap\u0131land\u0131rmam\u0131z gerekmektedir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar builder = WebApplication.CreateBuilder(args);\r\n.\r\n.\r\n.\r\nbuilder.Services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;\r\n    options.UseSqlServer(builder.Configuration.GetConnectionString(&quot;SQLServer&quot;)));\r\n\r\nvar app = builder.Build();\r\n.\r\n.\r\n.\r\napp.Run();\r\n<\/pre>\n<\/div>\n<p>Ve son olarak da <code>add-migration mig_1<\/code> talimat\u0131yla migration basal\u0131m ve<br \/>\n<code>update-database<\/code> talimat\u0131yla da migrate ederek veritaban\u0131n\u0131 sunucuda olu\u015ftural\u0131m.<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2023\/12\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2023\/12\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi.png\" alt=\".NET&#039;te Rebus K\u00fct\u00fcphanesiyle Saga Orchestration Yakla\u015f\u0131m\u0131n\u0131n Uygulanmas\u0131\" width=\"294\" height=\"489\" class=\"aligncenter size-full wp-image-26829\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2023\/12\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi.png 294w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2023\/12\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-180x300.png 180w\" sizes=\"auto, (max-width: 294px) 100vw, 294px\" \/><\/a><span style=\"font-size:12px;\">(Not: Bu ad\u0131m\u0131 uygularken s\u00fcre\u00e7te ihtiya\u00e7 olabilecek baz\u0131 k\u00fct\u00fcphanelerin y\u00fcklendi\u011fi varsay\u0131lm\u0131\u015ft\u0131r. Bu k\u00fct\u00fcphaneler; EF Core i\u00e7in <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore\" rel=\"noopener\" target=\"_blank\">Microsoft.EntityFrameworkCore<\/a>, EF Core&#8217;da SQL Server&#8217;\u0131n kullan\u0131labilmesi i\u00e7in <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.SqlServer\/\" rel=\"noopener\" target=\"_blank\">Microsoft.EntityFrameworkCore.SqlServer<\/a> ve package manager console \u00fczerinden migration i\u015flemlerinin y\u00fcr\u00fct\u00fclebilmesi i\u00e7in <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Tools\" rel=\"noopener\" target=\"_blank\">Microsoft.EntityFrameworkCore.Tools<\/a> k\u00fct\u00fcphaneleridir.)<\/span>\n<\/li>\n<li><strong><em>Ad\u0131m 2<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Order.API &#8211; (Sipari\u015fin Olu\u015fturulmas\u0131)<\/span><br \/>\nKullan\u0131c\u0131dan gelecek olan sipari\u015f i\u00e7in temel altyap\u0131y\u0131 kurgulad\u0131\u011f\u0131m\u0131za g\u00f6re art\u0131k sipari\u015fin olu\u015fturulmas\u0131na odaklanabiliriz. Bunun i\u00e7in \u00f6ncelikle kullan\u0131c\u0131dan sipari\u015fin bilgilerini alabilece\u011fimiz a\u015fa\u011f\u0131daki viewmodel nesnelerini olu\u015ftural\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class CreateOrderVM\r\n    {\r\n        public int BuyerId { get; set; }\r\n        public List&lt;CreateOrderItemVM&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class CreateOrderItemVM\r\n    {\r\n        public int ProductId { get; set; }\r\n        public int Count { get; set; }\r\n        public decimal Price { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Ard\u0131ndan sipari\u015fin olu\u015fturulmas\u0131n\u0131 sa\u011flayacak olan endpoint&#8217;i a\u015fa\u011f\u0131daki gibi olu\u015ftural\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar builder = WebApplication.CreateBuilder(args);\r\n.\r\n.\r\n.\r\nvar app = builder.Build();\r\n.\r\n.\r\n.\r\napp.MapPost(&quot;\/create-order&quot;, async (ApplicationDbContext context, CreateOrderVM model) =&gt;\r\n{\r\n    Order.API.Models.Entities.Order order = new()\r\n    {\r\n        BuyerId = model.BuyerId,\r\n        OrderItems = model.OrderItems.Select(oi =&gt; new OrderItem\r\n        {\r\n            Count = oi.Count,\r\n            Price = oi.Price,\r\n            ProductId = oi.ProductId\r\n        }).ToList(),\r\n        OrderStatus = OrderStatus.Suspend,\r\n        TotalPrice = model.OrderItems.Sum(oi =&gt; oi.Count * oi.Price),\r\n        CreatedDate = DateTime.Now\r\n    };\r\n\r\n    await context.Orders.AddAsync(order);\r\n    await context.SaveChangesAsync();\r\n    return Results.Created();\r\n});\r\n\r\napp.Run();\r\n<\/pre>\n<\/div>\n<p>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere sipari\u015fe dair distributed s\u00fcrecin tamamlanmas\u0131n\u0131 bekleyece\u011fimizden dolay\u0131 ilk etapta <em style=\"color:orange;\">Suspend<\/em> olarak olu\u015fturmaktay\u0131z.\n<\/li>\n<li><strong><em>Ad\u0131m 3<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Stock.Service &#8211; (MongoDB Altyap\u0131s\u0131n\u0131n Kurgulanmas\u0131 ve \u00dcr\u00fcn Stok Bilgilerine Dair Dummy Datalar\u0131n Eklenmesi)<\/span><br \/>\nGelecek olan sipari\u015fin stok i\u015flemlerine dair de <code style=\"color:purple;\">Stock.Service<\/code>&#8216;de bir altyap\u0131 olu\u015fturmam\u0131z gerekmektedir. Bu altyap\u0131 i\u00e7eri\u011fi zenginle\u015ftirmek maksad\u0131yla MongoDB \u00fczerine kurgulanm\u0131\u015f olacakt\u0131r. Bunun i\u00e7in \u00f6ncelikle a\u015fa\u011f\u0131daki &#8216;MongoDBService&#8217;i olu\u015ftural\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class MongoDBService\r\n    {\r\n        readonly IMongoDatabase _database;\r\n        readonly IConfiguration _configuration;\r\n        public MongoDBService(IConfiguration configuration)\r\n        {\r\n            _configuration = configuration;\r\n            MongoClient client = new(_configuration&#x5B;&quot;MongoDB:Server&quot;]);\r\n            _database = client.GetDatabase(_configuration&#x5B;&quot;MongoDB:DatabaseName&quot;]);\r\n        }\r\n        public IMongoCollection&lt;T&gt; GetCollection&lt;T&gt;() =&gt; _database.GetCollection&lt;T&gt;(typeof(T).Name.ToLowerInvariant());\r\n    }\r\n<\/pre>\n<\/div>\n<p>Tabi bu servisi olu\u015fturabilmek i\u00e7in <a href=\"https:\/\/www.nuget.org\/packages\/mongodb.driver\/\" rel=\"noopener\" target=\"_blank\">MongoDB.Driver<\/a> k\u00fct\u00fcphanesinin ilgili servise y\u00fcklenmesi gerekmektedir.<\/p>\n<p>Ba\u011flant\u0131 sa\u011flayaca\u011f\u0131m\u0131z MongoDB sunucusu bilgilerini de a\u015fa\u011f\u0131daki gibi appsettings.json dosyas\u0131na kaydedelim.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n  &quot;MongoDB&quot;: {\r\n    &quot;Server&quot;: &quot;mongodb:\/\/localhost:27017&quot;,\r\n    &quot;DatabaseName&quot;: &quot;SagaOrchestrationRebusDB&quot;\r\n  }\r\n<\/pre>\n<\/div>\n<p>Ve ard\u0131ndan ilgili servisi, projenin &#8216;Program.cs&#8217; dosyas\u0131 \u00fczerinden a\u015fa\u011f\u0131daki gibi yap\u0131land\u0131rarak uygulaman\u0131n IoC Container&#8217;\u0131na ekleyelim.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar builder = Host.CreateApplicationBuilder(args);\r\n.\r\n.\r\n.\r\nbuilder.Services.AddSingleton&lt;MongoDBService&gt;();\r\n\r\nvar host = builder.Build();\r\n.\r\n.\r\n.\r\nhost.Run();\r\n<\/pre>\n<\/div>\n<p>Devam\u0131nda ise MongoDB veritaban\u0131nda stok bilgilerini tutacak a\u015fa\u011f\u0131daki entity&#8217;i olu\u015ftural\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class Stock\r\n    {\r\n        &#x5B;BsonRepresentation(BsonType.ObjectId)]\r\n        &#x5B;BsonId]\r\n        &#x5B;BsonElement(Order = 0)]\r\n        public ObjectId Id { get; set; }\r\n        &#x5B;BsonRepresentation(BsonType.Int64)]\r\n        &#x5B;BsonElement(Order = 1)]\r\n        public int ProductId { get; set; }\r\n        &#x5B;BsonRepresentation(BsonType.Int64)]\r\n        &#x5B;BsonElement(Order = 2)]\r\n        public int Count { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Ve bu entity e\u015fli\u011finde yukar\u0131da yap\u0131land\u0131rd\u0131\u011f\u0131m\u0131z servis sayesinde \u00fcr\u00fcnlerin stok bilgilerini bar\u0131nd\u0131ran dummy datalar\u0131 olu\u015ftural\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar builder = Host.CreateApplicationBuilder(args);\r\n.\r\n.\r\n.\r\nbuilder.Services.AddSingleton&lt;MongoDBService&gt;();\r\n\r\nvar host = builder.Build();\r\n\r\nusing var scope = builder.Services.BuildServiceProvider().CreateScope();\r\nvar mongoDbService = scope.ServiceProvider.GetRequiredService&lt;MongoDBService&gt;();\r\nif (!await (await mongoDbService.GetCollection&lt;Stock.Service.Models.Stock&gt;().FindAsync(x =&gt; true)).AnyAsync())\r\n{\r\n    mongoDbService.GetCollection&lt;Stock.Service.Models.Stock&gt;().InsertOne(new()\r\n    {\r\n        ProductId = 1,\r\n        Count = 200\r\n    });\r\n    mongoDbService.GetCollection&lt;Stock.Service.Models.Stock&gt;().InsertOne(new()\r\n    {\r\n        ProductId = 2,\r\n        Count = 300\r\n    });\r\n    mongoDbService.GetCollection&lt;Stock.Service.Models.Stock&gt;().InsertOne(new()\r\n    {\r\n        ProductId = 3,\r\n        Count = 50\r\n    });\r\n    mongoDbService.GetCollection&lt;Stock.Service.Models.Stock&gt;().InsertOne(new()\r\n    {\r\n        ProductId = 4,\r\n        Count = 10\r\n    });\r\n    mongoDbService.GetCollection&lt;Stock.Service.Models.Stock&gt;().InsertOne(new()\r\n    {\r\n        ProductId = 5,\r\n        Count = 60\r\n    });\r\n}\r\n\r\nhost.Run();\r\n<\/pre>\n<\/div>\n<p>Art\u0131k bu \u00e7al\u0131\u015fmalar e\u015fli\u011finde <code style=\"color:purple;\">Stock.Service<\/code>&#8216;in bir kere aya\u011fa kald\u0131r\u0131lmas\u0131 hem veritaban\u0131n\u0131n hem de dummy datalar\u0131n olu\u015fturulmas\u0131 a\u00e7\u0131s\u0131ndan yeterli olacakt\u0131r.<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2023\/12\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-1.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2023\/12\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-1.png\" alt=\".NET&#039;te Rebus K\u00fct\u00fcphanesiyle Saga Orchestration Yakla\u015f\u0131m\u0131n\u0131n Uygulanmas\u0131\" width=\"198\" height=\"60\" class=\"aligncenter size-full wp-image-26830\" \/><\/a>\n<\/li>\n<li><strong><em>Ad\u0131m 4<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Shared &#8211; (BaseCorrelation S\u0131n\u0131f\u0131n\u0131n Olu\u015fturulmas\u0131)<\/span><br \/>\n\u0130\u00e7eri\u011fimizin sonraki ad\u0131mlar\u0131nda Rebus ile Saga Orchestration davran\u0131\u015f\u0131n\u0131 uygularken geli\u015ftirece\u011fimiz merkezi Saga yap\u0131lanmas\u0131nda, servislerden gelecek olan mesajlar\u0131 e\u015fle\u015ftirmemizi ve bu mesajlar\u0131n ayn\u0131 i\u015f ak\u0131\u015f\u0131 i\u00e7erisinde belirli bir sipari\u015fe kar\u015f\u0131l\u0131k ili\u015fkilendirmemizi sa\u011flayacak olan korelasyon yap\u0131s\u0131n\u0131 olu\u015fturmam\u0131z gerekmektedir. Bu yap\u0131, herhangi bir de\u011fer olabilir. Usul gere\u011fi m\u00fcmk\u00fcn mertebe unique olmas\u0131na \u00f6zen g\u00f6sterilmesi gerekece\u011finden dolay\u0131 biz bu ihtiyaca istinaden &#8216;BaseCorrelation&#8217; s\u0131n\u0131f\u0131 ile \u00e7\u00f6z\u00fcm getiriyor olaca\u011f\u0131z. Haliyle ilgili s\u0131n\u0131f\u0131n i\u00e7eri\u011fini <code style=\"color:purple;\">Shared<\/code> class library&#8217;sinde a\u015fa\u011f\u0131daki gibi olu\u015ftural\u0131m;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    &#x5B;Serializable]\r\n    public class BaseCorrelation\r\n    {\r\n        public BaseCorrelation() { }\r\n        public BaseCorrelation(Guid correlationId)\r\n        {\r\n            CorrelationId = correlationId;\r\n        }\r\n        &#x5B;DataMember]\r\n        public Guid CorrelationId { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere &#8216;BaseCorrelation&#8217; s\u0131n\u0131f\u0131 i\u00e7erisinde &#8216;Guid&#8217; t\u00fcr\u00fcnden bir de\u011fer al\u0131nmakta ve bu &#8216;CorrelationId&#8217; property&#8217;sinde saklanmaktad\u0131r. B\u00f6ylece sistemde olu\u015fturulacak t\u00fcm message ve event yap\u0131lanmalar\u0131n\u0131 bu s\u0131n\u0131ftan t\u00fcreterek, ayn\u0131 i\u015f ak\u0131\u015f\u0131ndaki farkl\u0131 olaylar\u0131 ve mesajlar\u0131, ortak bir ba\u011flam bilgisi olmas\u0131 amac\u0131yla \u00fcretilmi\u015f olan korelasyon de\u011feri \u00fczerinden e\u015fle\u015ftirecek ve tek bir sipari\u015fe kar\u015f\u0131n olay zincirinin ba\u011f\u0131n\u0131 kurguluyor olaca\u011f\u0131z.\n<\/li>\n<li><strong><em>Ad\u0131m 5<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Saga.Orchestration.Service &#8211; (Saga Orchestration Altyap\u0131s\u0131n\u0131n Kurgulanmas\u0131)<\/span><br \/>\nRebus k\u00fct\u00fcphanesiyle yap\u0131lan Orchestration operasyonunda t\u0131pk\u0131 MassTransit ile yapm\u0131\u015f oldu\u011fumuz \u00e7al\u0131\u015fmada oldu\u011fu gibi belli ba\u015fl\u0131 davran\u0131\u015flara sahip nesneler senaryoda sorumluluk \u00fcstlenmekte ve haliyle \u00f6ncelikle bu nesnelerin \u00fcretilmesi gerekmektedir. \u015eimdi gelin bu nesneleri tek tek izah ederek ilerleyelim;<\/p>\n<ul>\n<li><em style=\"color:purple;\"><u>Saga Data Nesnesi<\/u><\/em><br \/>\nOrchestration operasyonu s\u00fcrecinde distributed bir \u015fekilde takip edilen ak\u0131\u015f\u0131n veri format\u0131n\u0131 temsil eden bir nesnedir. Bizim \u00f6rnek senaryomuzda bu bir sipari\u015f modeline kar\u015f\u0131l\u0131k gelmekte ve bir sipari\u015fle ilgili servisler aras\u0131 ileti\u015fim s\u00fcrecindeki verilerin geli\u015fimini takip etmemizi sa\u011flayacak olan bilgileri bar\u0131nd\u0131racak olan bir nesneyi ifade etmektedir. B\u00f6ylece bizler saga data nesnesi sayesinde distributed ak\u0131\u015ftaki olgunun state&#8217;ini tutabilmekte ve y\u00f6netebilmekteyiz.<\/p>\n<p>Bir s\u0131n\u0131f\u0131n saga data s\u0131n\u0131f\u0131 olabilmesi i\u00e7in <code>ISagaData<\/code> interface&#8217;inden implemente edilmesi gerekmektedir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderSagaData : BaseCorrelation, ISagaData\r\n    {\r\n        public Guid Id { get; set; }\r\n        public int Revision { get; set; }\r\n        public int OrderId { get; set; }\r\n        public int BuyerId { get; set; }\r\n        public decimal TotalPrice { get; set; }\r\n        public List&lt;OrderItem&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Yukar\u0131da g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere bir sipari\u015f s\u00fcrecinin state&#8217;ini takip etmemizi sa\u011flayacak olan &#8216;OrderSagaData&#8217; s\u0131n\u0131f\u0131n\u0131 olu\u015fturmu\u015f bulunuyoruz. Burada dikkatinizi bir \u00f6nceki ad\u0131mda olu\u015fturdu\u011fumuz &#8216;BaseCorrelation&#8217; s\u0131n\u0131f\u0131na \u00e7ekmek isterim. Evet, olu\u015fturdu\u011fumuz bu saga data nesnesi her ne kadar sipari\u015f state&#8217;ini tutacak olsa da hangi sipari\u015fe dair state&#8217;i tuttu\u011funu anlayabilmek i\u00e7in bunu da &#8216;BaseCorrelation&#8217;dan t\u00fcretiyor ve bu s\u0131n\u0131ftaki korelasyon yap\u0131lanmas\u0131n\u0131 saga data nesnesine aktar\u0131yoruz.<\/p>\n<p>Ayr\u0131ca yukar\u0131da kullan\u0131lan &#8216;OrderItem&#8217; referans\u0131n\u0131 da <code style=\"color:purple;\">Shared<\/code> i\u00e7erisinde a\u015fa\u011f\u0131daki gibi olu\u015ftural\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderItem\r\n    {\r\n        public int ProductId { get; set; }\r\n        public int Count { get; set; }\r\n        public decimal Price { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<\/li>\n<li><em style=\"color:purple;\"><u>Saga Nesnesi<\/u><\/em><br \/>\nOrchestration yakla\u015f\u0131m\u0131n\u0131 uygulamam\u0131z\u0131 sa\u011flayacak olan, bir i\u015fin-ak\u0131\u015f\u0131n yani \u00f6rnek senaryomuzdaki sipari\u015f s\u00fcrecinin temsil edilmesine ve servisler aras\u0131ndaki koordinasyonun merkezi bir \u015fekilde y\u00fcr\u00fct\u00fclmesine imkan tan\u0131yacak olan s\u0131n\u0131f\u0131n ta kendisidir.<\/p>\n<p>Bizler saga s\u0131n\u0131f\u0131 sayesinde servisler aras\u0131ndaki mesajla\u015fmalar\u0131 takip edebilmekte, durumlar\u0131 y\u00f6netebilmekte ve i\u015f s\u00fcrecine uygun bir \u015fekilde durumlar aras\u0131 ge\u00e7i\u015fler yapabilmekteyiz.<\/p>\n<p>Bir s\u0131n\u0131f\u0131n saga s\u0131n\u0131f\u0131 olabilmesi i\u00e7in <code>Saga&lt;T&gt;<\/code> abstract class&#8217;\u0131n\u0131 implemente etmesi gerekmektedir. \u015e\u00f6yle ki;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderSaga : Saga&lt;OrderSagaData&gt;\r\n    {\r\n        protected override void CorrelateMessages(ICorrelationConfig&lt;OrderSagaData&gt; config)\r\n        {\r\n\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Evet, yukar\u0131daki kod blo\u011funda g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere bizler &#8216;OrderSaga&#8217; ad\u0131nda bir saga nesnesi olu\u015fturmu\u015f bulunuyoruz. \u0130\u00e7erik s\u00fcrecinde bu nesnenin hacminin farkl\u0131 implementasyonlarla geni\u015fledi\u011fini g\u00f6r\u00fcyor olacaks\u0131n\u0131z. \u015eimdilik en sade haliyle incelememiz gerekirse e\u011fer <code>&lt;T&gt;<\/code> parametresine verilen &#8216;OrderSagaData&#8217; nesnesine dikkatinizi \u00e7ekerim. Evet, saga nesnesi sayesinde servisler aras\u0131 i\u015f ak\u0131\u015f s\u00fcrecini gelen message ve event&#8217;lere g\u00f6re merkezi bir \u015fekilde y\u00f6netecek ve ileti\u015fimi ger\u00e7ekle\u015ftiriyor olaca\u011f\u0131z lakin bunu yaparken de i\u015fleyece\u011fimiz verinin t\u00fcr\u00fcn\u00fcn &#8216;OrderSagaData&#8217; olaca\u011f\u0131n\u0131 bildirmi\u015f oluyoruz. Bizler i\u015f ak\u0131\u015f s\u00fcrecinin(yani sipari\u015f s\u00fcrecinin) hangi noktas\u0131nda ya da hangi servisinde olursak olal\u0131m ona g\u00f6re arkada \u00fcretilmi\u015f olan &#8216;OrderSagaData&#8217; nesnesinin de\u011ferleri \u015fekillenecek ve kuraca\u011f\u0131m\u0131z korelasyon a\u011f\u0131yla da tek bir ba\u011flamla i\u00e7erisinde bir sipari\u015fin en nihai halini yine bu nesne \u00fczerinden elde edebiliyor olaca\u011f\u0131z.<\/p>\n<p>Ayr\u0131ca <code>Saga<\/code> abstract class&#8217;\u0131n\u0131n implemente ettirdi\u011fi <em>CorrelateMessages<\/em> isimli metotta ise birazdan olu\u015fturaca\u011f\u0131m\u0131z ve s\u00fcre\u00e7te i\u015flemeye \u00e7al\u0131\u015faca\u011f\u0131m\u0131z message ve event&#8217;lerin, saga data s\u0131n\u0131f\u0131m\u0131z olan &#8216;OrderSagaData&#8217; nesnesiyle e\u015fle\u015ftirilmesini ve bir olguya yani sipari\u015fe dair korelasyon a\u011f\u0131n\u0131n kurulmas\u0131n\u0131 sa\u011fl\u0131yor olaca\u011f\u0131z. Oraya gelece\u011fiz, \u015fimdi bunun i\u00e7in \u00f6ncelikle \u00e7al\u0131\u015fmada kullanaca\u011f\u0131m\u0131z event ve message&#8217;lar\u0131n olu\u015fturulmas\u0131n\u0131 ger\u00e7ekle\u015ftirelim.\n<\/li>\n<\/ul>\n<\/li>\n<li><strong><em>Ad\u0131m 6<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Shared &#8211; (Event ve Message&#8217;lar\u0131n Olu\u015fturulmas\u0131)<\/span><br \/>\n\u015eimdi kurguda kullanaca\u011f\u0131m\u0131z t\u00fcm event ve message yap\u0131lanmalar\u0131n\u0131 sorumluluklar\u0131n\u0131 izah ederek tek tek olu\u015fturmaya odaklanal\u0131m. Burada olu\u015fturaca\u011f\u0131m\u0131z t\u00fcm event ve message&#8217;lar\u0131n korelasyon ili\u015fkisini sa\u011flayabilmek i\u00e7in ba\u015ftan 4. ad\u0131mda olu\u015fturdu\u011fumuz &#8216;BaseCorrelation&#8217; s\u0131n\u0131f\u0131ndan t\u00fcretildi\u011fini vurgulamakta fayda g\u00f6rmekteyim.<\/p>\n<ol>\n<li><em style=\"color:green;\">OrderStartedEvent<\/em><br \/>\nKullan\u0131c\u0131 taraf\u0131ndan sipari\u015f verildi\u011fi an s\u00fcrecin ba\u015flad\u0131\u011f\u0131n\u0131 <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;e bildirecek olan event&#8217;tir. Yani MassTransit ile yapt\u0131\u011f\u0131m\u0131z \u00e7al\u0131\u015fmadaki <span style=\"color:red;\">Tetikleyici Event<\/span>&#8216;e kar\u015f\u0131l\u0131k gelmektedir. Bu event yay\u0131nland\u0131\u011f\u0131 taktirde <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;in veritaban\u0131nda &#8216;OrderSagaData&#8217;ya kar\u015f\u0131l\u0131k bir sat\u0131r olu\u015fturulacak ve distributed i\u015f ak\u0131\u015f\u0131 s\u00fcrecindeki t\u00fcm ilerlemeler ad\u0131m ad\u0131m bu sat\u0131r \u00fczerinden takip ediliyor olacakt\u0131r.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderStartedEvent : BaseCorrelation\r\n    {\r\n        public OrderStartedEvent() { }\r\n        public OrderStartedEvent(Guid correlationId) : base(correlationId) { }\r\n        public int OrderId { get; set; }\r\n        public int BuyerId { get; set; }\r\n        public decimal TotalPrice { get; set; }\r\n        public List&lt;OrderItem&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<\/li>\n<li><em style=\"color:green;\">OrderCreatedEvent<\/em><br \/>\nSipari\u015f olu\u015fturulduktan ve s\u00fcre\u00e7 ba\u015flat\u0131ld\u0131ktan sonra <code style=\"color:purple;\">Stock.Service<\/code>&#8216;i tetikleyebilmek i\u00e7in yay\u0131nlanacak event&#8217;tir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderCreatedEvent : BaseCorrelation\r\n    {\r\n        public OrderCreatedEvent(Guid correlationId) : base(correlationId) { }\r\n        public List&lt;OrderItem&gt;? OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>MassTransit&#8217;te ki <span style=\"color:red;\">Ba\u015far\u0131l\u0131 Event<\/span>&#8216;e kar\u015f\u0131l\u0131k gelmektedir.\n<\/li>\n<li><em style=\"color:green;\">OrderCompletedEvent<\/em><br \/>\nSipari\u015fe dair t\u00fcm servislerin i\u015flemlerini ba\u015far\u0131yla tamamland\u0131\u011f\u0131n\u0131 ve sipari\u015fin ba\u015far\u0131yla sonland\u0131\u011f\u0131n\u0131 ifade eden event&#8217;tir. <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>, bu event&#8217;i yay\u0131nlad\u0131\u011f\u0131 taktirde <code style=\"color:purple;\">Order.Saga<\/code> servisi sipari\u015fin durumunu <em style=\"color:orange;\">Completed<\/em>&#8216;a \u00e7ekecektir. MassTransit&#8217;te ki <span style=\"color:red;\">Ba\u015far\u0131l\u0131 Event<\/span>&#8216;e kar\u015f\u0131l\u0131k gelmektedir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderCompletedEvent\r\n    {\r\n        public int OrderId { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Ayr\u0131ca bu event&#8217;in dikkat ederseniz herhangi bir korelasyon de\u011feri bulunmamaktad\u0131r. \u00c7\u00fcnk\u00fc bu event, sipari\u015fin sonland\u0131\u011f\u0131n\u0131 ifade edece\u011finden dolay\u0131 bu bilgiyi i\u00e7erisindeki &#8216;OrderId&#8217; ile ta\u015f\u0131yacak ve art\u0131k korelasyonel bir ba\u011flam i\u00e7erisinde \u00e7al\u0131\u015fmaya gerek kalmayacakt\u0131r. Bu mant\u0131kla i\u00e7eri\u011fin devam\u0131nda tecr\u00fcbe edilece\u011fi \u00fczere, <em>OrderCompletedEvent<\/em> event&#8217;inin yay\u0131nlanaca\u011f\u0131 noktada <code style=\"color:purple;\">Saga.Orchestration.Service<\/code> taraf\u0131ndan ilgili sipari\u015fe dair saga bilgisi tamamlanacak ve veritaban\u0131ndan kald\u0131r\u0131lm\u0131\u015f olacakt\u0131r. Yani birnevi sipari\u015f saga a\u00e7\u0131s\u0131ndan Finilize edilmi\u015f olacakt\u0131r. O noktaya geldi\u011fimizde bunu tekrardan hat\u0131rlataca\u011f\u0131m.\n<\/li>\n<li><em style=\"color:green;\">OrderFailedEvent<\/em><br \/>\nSipari\u015fe dair distributed i\u015f ak\u0131\u015f\u0131 s\u00fcrecinde servislerden herhangi birinde bir ba\u015far\u0131s\u0131zl\u0131\u011f\u0131n s\u00f6z konusu oldu\u011funu yani uzun laf\u0131n k\u0131sas\u0131 sipari\u015fin ba\u015far\u0131s\u0131z oldu\u011funu ifade eden event&#8217;tir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderFailedEvent : BaseCorrelation\r\n    {\r\n        public OrderFailedEvent(Guid correlationId) : base(correlationId)\r\n        {\r\n        }\r\n\r\n        public int OrderId { get; set; }\r\n        public string Message { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Bu event <code style=\"color:purple;\">Saga.Orchestration.Service<\/code> taraf\u0131ndan yay\u0131nland\u0131\u011f\u0131 taktirde <code style=\"color:purple;\">Order.Saga<\/code> taraf\u0131ndan ilgili sipari\u015fe dair state <em style=\"color:orange;\">Fail<\/em>&#8216;a \u00e7ekilecektir. MassTransit&#8217;te ki <span style=\"color:red;\">Ba\u015fars\u0131z\u0131 Event<\/span>&#8216;e kar\u015f\u0131l\u0131k gelmektedir.\n<\/li>\n<li><em style=\"color:green;\">StockReservedEvent<\/em><br \/>\nSipari\u015fe dair <code style=\"color:purple;\">Stock.Service<\/code>&#8216;de ki stok i\u015flemlerinin ba\u015far\u0131l\u0131 oldu\u011funu ifade eden event&#8217;tir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockReservedEvent : BaseCorrelation\r\n    {\r\n        public StockReservedEvent(Guid correlationId) : base(correlationId) { }\r\n        public List&lt;OrderItem&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>MassTransit&#8217;te ki <span style=\"color:red;\">Ba\u015far\u0131l\u0131 Event<\/span>&#8216;e kar\u015f\u0131l\u0131k gelmektedir.\n<\/li>\n<li><em style=\"color:green;\">StockNotReservedEvent<\/em><br \/>\nSipari\u015fe dair <code style=\"color:purple;\">Stock.Service<\/code>&#8216;de ki stok i\u015flemlerinde ba\u015far\u0131s\u0131zl\u0131\u011f\u0131n oldu\u011funu ifade eden event&#8217;tir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockNotReservedEvent : BaseCorrelation\r\n    {\r\n        public StockNotReservedEvent(Guid correlationId) : base(correlationId) { }\r\n        public string Message { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>MassTransit&#8217;te ki <span style=\"color:red;\">Ba\u015far\u0131s\u0131z Event<\/span>&#8216;e kar\u015f\u0131l\u0131k gelmektedir.\n<\/li>\n<li><em style=\"color:green;\">PaymentStartedEvent<\/em><br \/>\n<code style=\"color:purple;\">Stock.Service<\/code> i\u015flemini ba\u015far\u0131yla tamamlad\u0131\u011f\u0131 taktirde <code style=\"color:purple;\">Payment.Service<\/code>&#8216;i tetikleyecek ve \u00f6deme s\u00fcrecini ba\u015flatacak olan event&#8217;tir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentStartedEvent : BaseCorrelation\r\n    {\r\n        public PaymentStartedEvent(Guid correlationId) : base(correlationId)\r\n        {\r\n        }\r\n        public decimal TotalPrice { get; set; }\r\n        public List&lt;OrderItem&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>MassTransit&#8217;te ki <span style=\"color:red;\">Ba\u015far\u0131l\u0131 Event<\/span>&#8216;e kar\u015f\u0131l\u0131k gelmektedir.\n<\/li>\n<li><em style=\"color:green;\">PaymentCompletedEvent<\/em><br \/>\nSipari\u015fe dair <code style=\"color:purple;\">Payment.Service<\/code>&#8216;de \u00f6deme i\u015flemlerinin ba\u015far\u0131l\u0131 oldu\u011funu ifade eden event\u2019tir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentCompletedEvent : BaseCorrelation\r\n    {\r\n        public PaymentCompletedEvent(Guid correlationId) : base(correlationId)\r\n        {\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>MassTransit&#8217;te ki <span style=\"color:red;\">Ba\u015far\u0131l\u0131 Event<\/span>&#8216;e kar\u015f\u0131l\u0131k gelmektedir.\n<\/li>\n<li><em style=\"color:green;\">PaymentFailedEvent<\/em><br \/>\nSipari\u015fe dair <code style=\"color:purple;\">Payment.Service<\/code>&#8216;de ki \u00f6deme i\u015flemlerinde ba\u015far\u0131s\u0131zl\u0131\u011f\u0131n oldu\u011funu ifade eden event&#8217;tir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentFailedEvent : BaseCorrelation\r\n    {\r\n        public List&lt;OrderItem&gt; OrderItems { get; set; }\r\n        public string Messages { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>MassTransit&#8217;te ki <span style=\"color:red;\">Ba\u015far\u0131s\u0131z Event<\/span>&#8216;e kar\u015f\u0131l\u0131k gelmektedir.\n<\/li>\n<li><em style=\"color:green;\">StockRollbackMessage<\/em><br \/>\n<code style=\"color:purple;\">Payment.Service<\/code>&#8216;te <em>PaymentFailedEvent<\/em> event\u2019i yay\u0131nlan\u0131rsa e\u011fer <code style=\"color:purple;\">Stock.Service<\/code>&#8216;de ki yap\u0131lan i\u015flemlerin geri al\u0131nmas\u0131n\u0131 sa\u011flayacak olan message&#8217;d\u0131r.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockRollbackMessage : BaseCorrelation\r\n    {\r\n        public StockRollbackMessage(Guid correlationId) : base(correlationId) { }\r\n        public List&lt;OrderItem&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/div>\n<p>MassTransit&#8217;te ki <span style=\"color:red;\">Compensable Event<\/span>&#8216;e kar\u015f\u0131l\u0131k gelmektedir.\n<\/li>\n<\/ol>\n<\/li>\n<li><strong><em>Ad\u0131m 7<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Saga.Orchestration.Service &#8211; (OrderSaga \u0130\u00e7erisinde Korelasyon \u0130li\u015fkisini Ger\u00e7ekle\u015ftirme)<\/span><br \/>\nEvent ve message&#8217;lar\u0131 da olu\u015fturdu\u011fumuza g\u00f6re art\u0131k 5. ad\u0131mda bahsi ge\u00e7en korelasyonel a\u011f\u0131 olu\u015fturmaya odaklanabiliriz. Bunun i\u00e7in <code style=\"color:purple;\">Saga.Orchestration.Service<\/code> i\u00e7erisinde olu\u015fturdu\u011fumuz &#8216;OrderSaga&#8217; s\u0131n\u0131f\u0131ndaki <em>CorrelateMessages<\/em> metodunda a\u015fa\u011f\u0131daki geli\u015ftirmeyi ger\u00e7ekle\u015ftirelim.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderSaga : Saga&lt;OrderSagaData&gt;\r\n    {\r\n        protected override void CorrelateMessages(ICorrelationConfig&lt;OrderSagaData&gt; config)\r\n        {\r\n            config.Correlate&lt;OrderStartedEvent&gt;(\r\n                orderStartedEvent =&gt; orderStartedEvent.CorrelationId,\r\n                orderSagaData =&gt; orderSagaData.CorrelationId);\r\n\r\n            config.Correlate&lt;StockReservedEvent&gt;(\r\n                stockReservedEvent =&gt; stockReservedEvent.CorrelationId,\r\n                orderSagaData =&gt; orderSagaData.CorrelationId);\r\n\r\n            config.Correlate&lt;StockNotReservedEvent&gt;(\r\n                stockNotReservedEvent =&gt; stockNotReservedEvent.CorrelationId,\r\n                orderSagaData =&gt; orderSagaData.CorrelationId);\r\n\r\n            config.Correlate&lt;PaymentCompletedEvent&gt;(\r\n                paymentCompletedEvent =&gt; paymentCompletedEvent.CorrelationId,\r\n                orderSagaData =&gt; orderSagaData.CorrelationId);\r\n\r\n            config.Correlate&lt;PaymentFailedEvent&gt;(\r\n                paymentFailedEvent =&gt; paymentFailedEvent.CorrelationId,\r\n                orderSagaData =&gt; orderSagaData.CorrelationId);\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Yukar\u0131daki \u00e7al\u0131\u015fmay\u0131 incelerseniz e\u011fer servislerden <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;e gelecek olan event&#8217;lerin &#8216;CorrelationId&#8217; de\u011ferinin, &#8216;OrderSagaData&#8217; t\u00fcr\u00fcnden olan saga data&#8217;n\u0131n &#8216;CorrelationId&#8217; ile e\u015fle\u015ftirilmesini <code>Correlate&lt;T&gt;<\/code> metoduyla ger\u00e7ekle\u015ftiriyoruz. B\u00f6ylece bir sipari\u015f s\u00fcrecinde distributed i\u015f ak\u0131\u015f\u0131na dair takibin hangi ba\u011flam \u00e7er\u00e7evesinde oldu\u011funu bu korelasyonel a\u011f sayesinde ili\u015fkilendirerek s\u0131n\u0131rlam\u0131\u015f oluyoruz.<\/p>\n<p>Art\u0131k korelasyonel a\u011f\u0131 da kurgulad\u0131\u011f\u0131m\u0131za g\u00f6re art\u0131k saga nesnesindeki operasyonlara odaklanabiliriz.\n<\/li>\n<li><strong><em>Ad\u0131m 8<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Saga.Orchestration.Service &#8211; (OrderSaga&#8217;n\u0131n IAmInitiatedBy ve IHandleMessages \u0130mplementasyonlar\u0131n\u0131n Ger\u00e7ekle\u015ftirilmesi)<\/span><br \/>\nS\u0131rada sipari\u015f s\u00fcrecindeki distributed ak\u0131\u015f\u0131n nas\u0131l olaca\u011f\u0131n\u0131 handle etmek vard\u0131r. Yani saga nesnesine gelen event&#8217;lere kar\u015f\u0131n hangi servise hangi event&#8217;in ya da message&#8217;\u0131n g\u00f6nderilece\u011fini ayarlamam\u0131z gerekmektedir. Bunun i\u00e7in <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;de ki saga nesnesinde a\u015fa\u011f\u0131daki \u00e7al\u0131\u015fmay\u0131 yapal\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderSaga :\r\n        Saga&lt;OrderSagaData&gt;,\r\n        IAmInitiatedBy&lt;OrderStartedEvent&gt;,\r\n        IHandleMessages&lt;StockReservedEvent&gt;,\r\n        IHandleMessages&lt;StockNotReservedEvent&gt;,\r\n        IHandleMessages&lt;PaymentCompletedEvent&gt;,\r\n        IHandleMessages&lt;PaymentFailedEvent&gt;\r\n    {\r\n        public Task Handle(OrderStartedEvent message)\r\n        {\r\n            ...\r\n        }\r\n\r\n        public Task Handle(StockReservedEvent message)\r\n        {\r\n            ...\r\n        }\r\n\r\n        public Task Handle(StockNotReservedEvent message)\r\n        {\r\n            ...\r\n        }\r\n\r\n        public Task Handle(PaymentCompletedEvent message)\r\n        {\r\n            ...\r\n        }\r\n\r\n        public Task Handle(PaymentFailedEvent message)\r\n        {\r\n            ...\r\n        }\r\n\r\n        protected override void CorrelateMessages(ICorrelationConfig&lt;OrderSagaData&gt; config)\r\n        {\r\n            .\r\n            .\r\n            .\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Burada g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere <code>IAmInitiatedBy<\/code> ve <code>IHandleMessages<\/code> interface&#8217;leri kullan\u0131lmaktad\u0131r. Bu interface&#8217;ler ile saga nesnesine di\u011fer servislerden gelecek olan event ve message&#8217;lar tan\u0131mlanarak, gerekli aksiyonlar sa\u011flanabilmektedir. Bu interface&#8217;lerin definition&#8217;lar\u0131na g\u00f6z atarsak e\u011fer;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\npublic interface IAmInitiatedBy&lt;in TMessage&gt; : IHandleMessages&lt;TMessage&gt; { }\r\n<\/pre>\n<\/div>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\npublic interface IHandleMessages { }\r\n\r\npublic interface IHandleMessages&lt;in TMessage&gt; : IHandleMessages\r\n{\r\n    Task Handle(TMessage message);\r\n}\r\n<\/pre>\n<\/div>\n<p>\u015feklinde olduklar\u0131n\u0131 g\u00f6r\u00fcr\u00fcz. Yani bu interface&#8217;lerin her ikisinin de implementasyonunun da <code>Task Handle(TMessage message);<\/code> imzas\u0131na kar\u015f\u0131n bir metodun uygulat\u0131ld\u0131\u011f\u0131n\u0131 s\u00f6yleyebiliriz. Dolay\u0131s\u0131yla yukar\u0131daki \u00e7al\u0131\u015fmada <strong><em>9<\/em><\/strong> ile <strong><em>32.<\/em><\/strong> sat\u0131r aral\u0131\u011f\u0131ndaki Handle metot tan\u0131mlar\u0131 bu implementasyonlara kar\u015f\u0131n olu\u015fturulmu\u015ftur.<\/p>\n<p>Burada <code>IAmInitiatedBy<\/code> interface&#8217;i, saga&#8217;n\u0131n(yani kurgumuz gere\u011fi bir sipari\u015fin) ba\u015flat\u0131lmas\u0131n\u0131 sa\u011flayacak event&#8217;i temsil etmektedir. <strong><em>3.<\/em><\/strong> sat\u0131ra g\u00f6z atarsan\u0131z e\u011fer bu event&#8217;in <em>OrderStartedEvent<\/em> oldu\u011fu ifade edilmekte ve b\u00f6ylece bu event yay\u0131nland\u0131\u011f\u0131 taktirde saga nesnesinin yeni bir saga data olu\u015fturaca\u011f\u0131 bildirilmi\u015f olmaktad\u0131r. Ve bu event&#8217;e kar\u015f\u0131n <strong><em>9<\/em><\/strong> ile <strong><em>12.<\/em><\/strong> sat\u0131r aral\u0131\u011f\u0131ndaki ilgili t\u00fcr\u00fc handle eden metot tetiklenecektir.<\/p>\n<p><code>IHandleMessages<\/code> interface&#8217;i ise di\u011fer event yahut message&#8217;lar\u0131 temsil etmektedir. Bu interface ile belirtilen event&#8217;lerin yay\u0131nlanmas\u0131 durumunda bu saga nesnesi taraf\u0131ndan bunlar\u0131n kar\u015f\u0131lanabilece\u011fini ifade etmemizi sa\u011flamaktad\u0131r. <strong><em>14<\/em><\/strong> ile <strong><em>32.<\/em><\/strong> sat\u0131r aral\u0131\u011f\u0131ndaki metotlar ise gelecek event&#8217;in t\u00fcr\u00fcne uygun olacak \u015fekilde tetiklenecektir.\n<\/li>\n<li><strong><em>Ad\u0131m 9<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Saga.Orchestration.Service &#8211; (HandleMessage Operasyonlar\u0131n\u0131 Ger\u00e7ekle\u015ftirme)<\/span><br \/>\n\u015eimdi de handle metotlar\u0131ndaki gerekli \u00e7al\u0131\u015fmalar\u0131 ger\u00e7ekle\u015ftirelim.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderSaga(IBus bus) :\r\n        Saga&lt;OrderSagaData&gt;,\r\n        IAmInitiatedBy&lt;OrderStartedEvent&gt;,\r\n        IHandleMessages&lt;StockReservedEvent&gt;,\r\n        IHandleMessages&lt;StockNotReservedEvent&gt;,\r\n        IHandleMessages&lt;PaymentCompletedEvent&gt;,\r\n        IHandleMessages&lt;PaymentFailedEvent&gt;\r\n    {\r\n        public async Task Handle(OrderStartedEvent message)\r\n        {\r\n            if (!IsNew)\r\n                return;\r\n\r\n            Data.OrderId = message.OrderId;\r\n            Data.BuyerId = message.BuyerId;\r\n            Data.OrderItems = message.OrderItems;\r\n            Data.TotalPrice = message.TotalPrice;\r\n\r\n            await bus.Send(new OrderCreatedEvent(message.CorrelationId)\r\n            {\r\n                OrderItems = message.OrderItems,\r\n            });\r\n        }\r\n\r\n        public async Task Handle(StockReservedEvent message)\r\n        {\r\n            await bus.Send(new PaymentStartedEvent(message.CorrelationId)\r\n            {\r\n                OrderItems = message.OrderItems,\r\n                TotalPrice = Data.TotalPrice\r\n            });\r\n        }\r\n\r\n        public async Task Handle(StockNotReservedEvent message)\r\n        {\r\n            await bus.Send(new OrderFailedEvent(message.CorrelationId)\r\n            {\r\n                Message = message.Message,\r\n                OrderId = Data.OrderId\r\n            });\r\n        }\r\n\r\n        public async Task Handle(PaymentCompletedEvent message)\r\n        {\r\n            await bus.Send(new OrderCompletedEvent()\r\n            {\r\n                OrderId = Data.OrderId\r\n            });\r\n\r\n            MarkAsComplete();\r\n        }\r\n\r\n        public async Task Handle(PaymentFailedEvent message)\r\n        {\r\n            await bus.Send(new OrderFailedEvent(message.CorrelationId)\r\n            {\r\n                OrderId = Data.OrderId,\r\n                Message = message.Messages\r\n            });\r\n\r\n            await bus.Send(new StockRollbackMessage(message.CorrelationId)\r\n            {\r\n                OrderItems = message.OrderItems\r\n            });\r\n        }\r\n\r\n        protected override void CorrelateMessages(ICorrelationConfig&lt;OrderSagaData&gt; config)\r\n        {\r\n            .\r\n            .\r\n            .\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Bu ad\u0131mda yap\u0131lan \u00e7al\u0131\u015fmalar\u0131 incelemeye \u00f6ncelikle inject edilen <code>IBus<\/code> referans\u0131n\u0131 vurgulayarak ba\u015flayal\u0131m. Bizler Rebus ile mesaj g\u00f6nderim i\u015flemlerini bu referans arac\u0131l\u0131\u011f\u0131yla kolayl\u0131kla ger\u00e7ekle\u015ftirebiliyoruz.<\/p>\n<p><strong><em>9<\/em><\/strong> ile <strong><em>23.<\/em><\/strong> sat\u0131r aral\u0131\u011f\u0131nda <em>OrderStartedEvent<\/em>&#8216;e kar\u015f\u0131n handle i\u015flemleri y\u00fcr\u00fct\u00fclmektedir. Bu event geldi\u011fi taktirde \u00f6ncelikle <code>IsNew<\/code> property&#8217;si ile yeni bir saga yani sipari\u015f s\u00fcrecinin olup olmad\u0131\u011f\u0131 de\u011ferlendirilmekte ve e\u011fer ki bu sipari\u015f daha \u00f6nce i\u015flendiyse tekrar etmeye gerek g\u00f6r\u00fclmemektedir. Yok e\u011fer yeni sipari\u015f durumu s\u00f6z konusuysa o taktirde &#8216;OrderSagaData&#8217; t\u00fcr\u00fcnden saga data&#8217;ya <code>Data<\/code> property&#8217;si \u00fczerinden eri\u015filmekte ve ba\u015flat\u0131c\u0131 event&#8217;in de\u011ferleri i\u015flenmekte ve ard\u0131ndan <em>OrderCreatedEvent<\/em> yay\u0131nlanmaktad\u0131r.<\/p>\n<p><strong><em>25<\/em><\/strong> ile <strong><em>32.<\/em><\/strong> sat\u0131r aral\u0131\u011f\u0131nda ise <em>StockReservedEvent<\/em>&#8216;e kar\u015f\u0131l\u0131k <em>PaymentStartedEvent<\/em>, <strong><em>34<\/em><\/strong> ile <strong><em>41.<\/em><\/strong> sat\u0131r aral\u0131\u011f\u0131nda <em>StockNotReservedEvent<\/em>&#8216;e kar\u015f\u0131l\u0131k <em>OrderFailedEvent<\/em>, <strong><em>43<\/em><\/strong> ile <strong><em>51.<\/em><\/strong> sat\u0131r aral\u0131\u011f\u0131nda <em>PaymentCompletedEvent<\/em>&#8216;e kar\u015f\u0131l\u0131k <em>OrderCompletedEvent<\/em> ve son olarak da <strong><em>53<\/em><\/strong> ile <strong><em>65.<\/em><\/strong> sat\u0131r aral\u0131\u011f\u0131nda ise <em>PaymentFailedEvent<\/em>&#8216;e kar\u015f\u0131l\u0131k <em>OrderFailedEvent<\/em> ile birlikte <em>StockRollbackMessage<\/em> yay\u0131nlanmaktad\u0131r.<\/p>\n<p><strong><em>43<\/em><\/strong> ile <strong><em>51.<\/em><\/strong> sat\u0131r aral\u0131\u011f\u0131na tekrar gelirsek e\u011fer burada <code>MarkAsComplete<\/code> metodunun \u00e7a\u011fr\u0131ld\u0131\u011f\u0131n\u0131 g\u00f6rmekteyiz. Bu metot 6. ad\u0131mda <em>OrderCompletedEvent<\/em>&#8216;i olu\u015ftururken bahsetti\u011fim finalize i\u015flemini ger\u00e7ekle\u015ftirmektedir. Yani, ilgili handle metodunda <em>OrderCompletedEvent<\/em> yay\u0131nland\u0131\u011f\u0131ndan dolay\u0131 art\u0131k sipari\u015fin ba\u015far\u0131l\u0131 oldu\u011fu kabul edilmekte ve b\u00f6ylece i\u015f ak\u0131\u015f\u0131n\u0131n tamamland\u0131\u011f\u0131 ifade edilmektedir. Dolay\u0131s\u0131yla <code>MarkAsComplete<\/code> metodu \u00e7a\u011fr\u0131lmakta ve <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;in veritaban\u0131nda sipari\u015fe dair olu\u015fturulmu\u015f olan saga data sat\u0131r\u0131 kald\u0131r\u0131lmaktad\u0131r.\n<\/li>\n<li><strong><em>Ad\u0131m 10<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Shared &#8211; (Queue&#8217;lar\u0131n ve AMQP Url&#8217;in Yap\u0131land\u0131r\u0131lmas\u0131)<\/span><br \/>\n\u015eimdi de t\u00fcm servislerin ileti\u015fim s\u00fcre\u00e7lerinde handle edecekleri mesajlar\u0131 hangi queue&#8217;lardan alacaklar\u0131n\u0131 yap\u0131land\u0131ral\u0131m. Bunun i\u00e7in <code style=\"color:purple;\">Shared<\/code> katman\u0131nda a\u015fa\u011f\u0131daki s\u0131n\u0131f\u0131 olu\u015fturabiliriz.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public static class RabbitMQQueueSettings\r\n    {\r\n        public const string OrderInputQueueName = &quot;order-input-queue-name&quot;;\r\n        public const string StockInputQueueName = &quot;stock-input-queue-name&quot;;\r\n        public const string PaymentInputQueueName = &quot;payment-input-queue-name&quot;;\r\n        public const string SagaOrchestrationInputQueueName = &quot;saga-orchestration-input-queue-name&quot;;\r\n    }\r\n<\/pre>\n<\/div>\n<p>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;de dahil t\u00fcm servislerin queue isimlendirmesini <em>RabbitMQQueueSettings<\/em> s\u0131n\u0131f\u0131nda ayarlam\u0131\u015f bulunuyoruz.<\/p>\n<p>Queue isimlerinin yan\u0131nda ayr\u0131ca servisler aras\u0131 ileti\u015fim s\u00fcrecinde kullanaca\u011f\u0131m\u0131z RabbitMQ transporter&#8217;\u0131n\u0131n AMQP Url&#8217;ine t\u00fcm servislerin eri\u015febilece\u011fi \u015fekilde bir tan\u0131mlamada bulunmam\u0131z gerekmektedir. Evet, bu i\u015flem i\u00e7in her servisin appsettings.json dosyas\u0131ndan istifade edebiliriz ki do\u011frusu da budur. \u00c7\u00fcnk\u00fc production ortam\u0131nda bu tarz de\u011ferler elbet environment&#8217;tan yap\u0131land\u0131r\u0131lacakt\u0131r. Haliyle bunda mutab\u0131k\u0131z. Lakin bizler makalemizin hacmini \u015fi\u015firmemek i\u00e7in bu de\u011feri de yine <code style=\"color:purple;\">Shared<\/code> katman\u0131nda olu\u015fturdu\u011fumuz a\u015fa\u011f\u0131daki <em>RabbitMQAMQPUrl<\/em> s\u0131n\u0131f\u0131n da tutuyor olacak ve bu best practice&#8217;i bilin\u00e7li bir \u015fekilde g\u00f6z ard\u0131 ediyor olaca\u011f\u0131z.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class RabbitMQAMQPUrl\r\n    {\r\n        public const string AMQPUrl = &quot;amqps:\/\/irresudw:slx6TGLxuMAthCz4X61dYE6Crr-Vx7lx@chimpanzee.rmq.cloudamqp.com\/irresudw&quot;;\r\n    }\r\n<\/pre>\n<\/div>\n<p>Evet, art\u0131k bundan sonraki ad\u0131mlarda bu yap\u0131land\u0131rma de\u011ferlerini kullanarak servislerimizi konfig\u00fcre edebiliriz.\n<\/li>\n<li><strong><em>Ad\u0131m 11<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Saga.Orchestration.Service &#8211; (Program.cs Yap\u0131land\u0131rmas\u0131 ve Veritaban\u0131n\u0131n Olu\u015fturulmas\u0131)<\/span><br \/>\n\u0130lk konfig\u00fcre edece\u011fimiz servis <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;dir. Bunun i\u00e7in ilgili servisin &#8216;Program.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki \u00e7al\u0131\u015fmay\u0131 yapal\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar builder = Host.CreateApplicationBuilder(args);\r\n\r\nbuilder.Services.AddRebus(rebus =&gt; rebus\r\n     .Routing(r =&gt;\r\n        r.TypeBased()\r\n        .Map&lt;OrderCreatedEvent&gt;(RabbitMQQueueSettings.StockInputQueueName)\r\n        .Map&lt;PaymentStartedEvent&gt;(RabbitMQQueueSettings.PaymentInputQueueName)\r\n        .Map&lt;OrderFailedEvent&gt;(RabbitMQQueueSettings.OrderInputQueueName)\r\n        .Map&lt;OrderCompletedEvent&gt;(RabbitMQQueueSettings.OrderInputQueueName)\r\n        .Map&lt;StockRollbackMessage&gt;(RabbitMQQueueSettings.StockInputQueueName))\r\n     .Transport(t =&gt;\r\n         t.UseRabbitMq(RabbitMQAMQPUrl.AMQPUrl,\r\n         inputQueueName: RabbitMQQueueSettings.SagaOrchestrationInputQueueName))\r\n     .Sagas(s =&gt;\r\n         s.StoreInSqlServer(&quot;Server=localhost, 1433;Database=SagaOrchestrationRebusDB;User ID=SA;Password=1q2w3e4r+!;TrustServerCertificate=True&quot;,\r\n         dataTableName: &quot;Sagas&quot;,\r\n         indexTableName: &quot;SagaIndexes&quot;,\r\n         automaticallyCreateTables: true))\r\n     .Timeouts(t =&gt;\r\n         t.StoreInSqlServer(&quot;Server=localhost, 1433;Database=SagaOrchestrationRebusDB;User ID=SA;Password=1q2w3e4r+!;TrustServerCertificate=True&quot;,\r\n         tableName: &quot;Timeouts&quot;,\r\n         automaticallyCreateTables: true)));\r\n\r\nbuilder.Services.AutoRegisterHandlersFromAssemblyOf&lt;Program&gt;();\r\n\r\nvar host = builder.Build();\r\nhost.Run();\r\n<\/pre>\n<\/div>\n<p>Yap\u0131lan \u00e7al\u0131\u015fmay\u0131 incelersek e\u011fer; <strong><em>3.<\/em><\/strong> sat\u0131rda <code>AddRebus<\/code> fonksiyonunu kullanarak Rebus servisini uygulamaya dahil ediyoruz. <strong><em>4<\/em><\/strong> ile <strong><em>10.<\/em><\/strong> sat\u0131r aral\u0131\u011f\u0131nda <code>Routing<\/code> metodu ile t\u00fcr\u00fcne g\u00f6re mesajlar\u0131n hangi kuyruklara y\u00f6nlendirilece\u011fini belirliyoruz. Burada <code>TypeBased<\/code> metodu ile y\u00f6nlendirme stratejisi olarak mesaj t\u00fcr\u00fcne dayal\u0131 bir davran\u0131\u015f\u0131 belirleyece\u011fimizi ifade etmi\u015f oluyoruz. Ayr\u0131ca <code>Map<\/code> fonksiyonu ile de belirtilen mesaj t\u00fcr\u00fcn\u00fc \u00f6nceden ayarlad\u0131\u011f\u0131m\u0131z kuyruk ad\u0131yla e\u015fle\u015ftiriyoruz ki b\u00f6ylece bu servis taraf\u0131ndan, o t\u00fcrden mesaj\u0131n hangi kuyruktan gelece\u011fini ve t\u00fcketebilece\u011fini yap\u0131land\u0131rm\u0131\u015f oluyoruz. Tabi burada t\u00fcm mesaj t\u00fcrlerine g\u00f6re ayr\u0131 ayr\u0131 yap\u0131land\u0131rma da bulunmaktansa <code>MapAssemblyOf<\/code> metoduyla da a\u015fa\u011f\u0131daki gibi t\u00fcm t\u00fcrlere kar\u015f\u0131n assembly bazl\u0131 k\u00fcm\u00fclatif bir tan\u0131mda bulunabilirdik.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n.\r\n.\r\n.\r\nbuilder.Services.AddRebus(rebus =&gt; rebus\r\n     .Routing(r =&gt;\r\n        r.TypeBased()\r\n        .MapAssemblyOf&lt;BaseCorrelation&gt;(&quot;..queue-name..&quot;)\r\n.\r\n.\r\n.\r\n<\/pre>\n<\/div>\n<p>Velhas\u0131l, <strong><em>11.<\/em><\/strong> sat\u0131rdaki <code>Transport<\/code> metodu ile de bu servisin kullanaca\u011f\u0131 transporter&#8217;\u0131 belirtiyor ve <strong><em>12.<\/em><\/strong> sat\u0131rda <code>UseRabbitMq<\/code> metodu ile RabbitMQ&#8217;ya g\u00f6re gerekli yap\u0131land\u0131rmay\u0131 sa\u011fl\u0131yoruz. Ayr\u0131ca bu servisin mesaj g\u00f6nderirken hangi kuyru\u011fa g\u00f6nderece\u011fini de <code>inputQueueName<\/code> parametresine verilen queue de\u011feriyle belirtiyoruz. Yani <code style=\"color:purple;\">Saga.Orchestration.Service<\/code> bir message&#8217;\u0131 send ya da publish etti\u011finde bu, burada belirtilen kuyru\u011fa g\u00f6nderilmi\u015f olacakt\u0131r.<\/p>\n<p><strong><em>14.<\/em><\/strong> sat\u0131rda <code>Sagas<\/code> metodu ile de Rebus k\u00fct\u00fcphanesinin servisler aras\u0131ndaki ileti\u015fim s\u00fcrecinde i\u015f ak\u0131\u015f\u0131n\u0131n durumunu nerede saklayaca\u011f\u0131n\u0131 belirtiyoruz.<\/p>\n<p>Ve son olarak <strong><em>19.<\/em><\/strong> sat\u0131rda \u00e7a\u011f\u0131rd\u0131\u011f\u0131m\u0131z <code>Timeouts<\/code> metodu ile de Rebus&#8217;\u0131n timeout s\u00fcre\u00e7lerine dair kay\u0131tlar\u0131n nerede tutulaca\u011f\u0131n\u0131 yap\u0131land\u0131r\u0131yoruz.<\/p>\n<p>Burada <code>Sagas<\/code> ve <code>Timeouts<\/code> i\u015flemleri i\u00e7in gerekli olan veritaban\u0131 yap\u0131lar\u0131 <code style=\"purple\">automaticallyCreateTables<\/code> parametresi sayesinde servis aya\u011fa kald\u0131r\u0131ld\u0131\u011f\u0131 an otomatik olarak olu\u015fturulacakt\u0131r. Tabi veritaban\u0131n\u0131 bizlerin olu\u015fturmas\u0131 gerekmektedir. Kaynaktaki connection string de\u011ferine bakarsan\u0131z veritaban\u0131 ismine <code>SagaOrchestrationRebusDB<\/code> verdi\u011fimizi g\u00f6r\u00fcrs\u00fcn\u00fcz. Haliyle \u015fimdi bu isimde bir veritaban\u0131n\u0131 olu\u015ftural\u0131m ve ard\u0131ndan servisimizi aya\u011fa kald\u0131r\u0131p gerekli tablo yap\u0131lanmas\u0131n\u0131 generate ettirelim.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nCREATE DATABASE SagaOrchestrationRebusDB\r\n<\/pre>\n<\/div>\n<p><a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi.png\" alt=\".NET&#039;te Rebus K\u00fct\u00fcphanesiyle Saga Orchestration Yakla\u015f\u0131m\u0131n\u0131n Uygulanmas\u0131\" width=\"168\" height=\"186\" class=\"aligncenter size-full wp-image-26839\" \/><\/a>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere olu\u015fturdu\u011fumuz veritaban\u0131 i\u00e7erisine ilgili tablo yap\u0131s\u0131 olu\u015fturulmu\u015ftur. \u015eimdi olu\u015fturulan bu tablolar\u0131 inceleyelim.<\/p>\n<table style=\"font-size:12px;\">\n<thead>\n<tr>\n<th colspan=\"3\">Tablolar<\/th>\n<\/tr>\n<tr>\n<th style=\"width:37%;\">Sagas<\/th>\n<th style=\"width:37%;\">SagaIndexes<\/th>\n<th>Timeouts<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"vertical-align: text-top;\">Distributed i\u015f ak\u0131\u015f\u0131 s\u00fcrecindeki state&#8217;i saklamak ve takip etmek i\u00e7in kullan\u0131lan tablodur.<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-1.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-1.png\" alt=\".NET&#039;te Rebus K\u00fct\u00fcphanesiyle Saga Orchestration Yakla\u015f\u0131m\u0131n\u0131n Uygulanmas\u0131\" width=\"221\" height=\"120\" class=\"aligncenter size-full wp-image-26840\" \/><\/a>Burada i\u015f ak\u0131\u015f\u0131ndaki her bir state&#8217;i bir versiyon numaras\u0131yla temsil edece\u011fi <code>revision<\/code> kolonunda tutmaktad\u0131r. Her bir state de\u011fi\u015fikli\u011fi bu kolonun de\u011ferini +1 art\u0131racakt\u0131r. B\u00f6ylece biz ilgili olguya dair i\u015f ak\u0131\u015f\u0131nda state&#8217;in ka\u00e7 kez de\u011fi\u015fti\u011fini \u00f6\u011frenebilmekteyiz.<\/p>\n<p><code>data<\/code> kolonunda ise her bir duruma kar\u015f\u0131n kullan\u0131lan <em>OrderSagaData<\/em> t\u00fcr\u00fcnden saga data verisi binary format\u0131nda tutulmaktad\u0131r.\n<\/td>\n<td style=\"vertical-align: text-top;\">Sagas tablosuyla ili\u015fkilendirilmi\u015f olan bu SagaIndexes tablosu i\u015f ak\u0131\u015f\u0131 state&#8217;ini belirleyen index bilgilerini i\u00e7ermektedir.<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-2.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-2.png\" alt=\".NET&#039;te Rebus K\u00fct\u00fcphanesiyle Saga Orchestration Yakla\u015f\u0131m\u0131n\u0131n Uygulanmas\u0131\" width=\"223\" height=\"144\" class=\"aligncenter size-full wp-image-26841\" \/><\/a><code>saga_type<\/code> kolonunda state&#8217;i takip edilen saga data&#8217;n\u0131n t\u00fcr\u00fc, <code>key<\/code> kolonunda korelasyon de\u011ferini ifade eden property ad\u0131 ve <code>value<\/code> kolonunda ise i\u015f ak\u0131\u015f\u0131ndaki b\u00fct\u00fcnselli\u011fi sa\u011flayacak olan korelasyonel de\u011fer tutulmaktad\u0131r. <\/p>\n<\/td>\n<td style=\"vertical-align: text-top;\">Timeout s\u00fcre\u00e7lerini y\u00f6netmek i\u00e7in kullan\u0131lan tablodur. Bu tablo sayesinde, Rebus ile yap\u0131lan i\u015flemlerin yahut olaylar\u0131n belirli bir s\u00fcre i\u00e7erisinde tamamlanmas\u0131n\u0131 bekledi\u011fi durumlar\u0131 takip edebiliriz.<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-3.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-3.png\" alt=\".NET&#039;te Rebus K\u00fct\u00fcphanesiyle Saga Orchestration Yakla\u015f\u0131m\u0131n\u0131n Uygulanmas\u0131\" width=\"220\" height=\"145\" class=\"aligncenter size-full wp-image-26842\" \/><\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/li>\n<li><strong><em>Ad\u0131m 12<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Order.API &#8211; (Program.cs Yap\u0131land\u0131rmas\u0131)<\/span><br \/>\nS\u0131radaki konfig\u00fcre edece\u011fimiz servisimiz <code style=\"color:purple;\">Order.API<\/code> olacakt\u0131r. Bunun i\u00e7in ilgili servisin &#8216;Program.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki \u00e7al\u0131\u015fmay\u0131 yapal\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\r\n.\r\n.\r\n.\r\nbuilder.Services.AddRebus(rebus =&gt; rebus\r\n     .Routing(r =&gt; r.TypeBased().MapAssemblyOf&lt;BaseCorrelation&gt;(RabbitMQQueueSettings.SagaOrchestrationInputQueueName))\r\n     .Transport(t =&gt; t.UseRabbitMq(RabbitMQAMQPUrl.AMQPUrl, inputQueueName: RabbitMQQueueSettings.OrderInputQueueName)));\r\n\r\nbuilder.Services.AutoRegisterHandlersFromAssemblyOf&lt;Program&gt;();\r\n.\r\n.\r\n.\r\n<\/pre>\n<\/div>\n<p>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere <code style=\"color:purple;\">Order.API<\/code>&#8216;a t\u00fcm mesajlar\u0131n <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;in mesaj g\u00f6nderece\u011fi kuyruktan yani k\u0131saca <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;ten gelece\u011fini bildirmi\u015f bulunmaktay\u0131z. \u00c7\u00fcnk\u00fc <code style=\"color:purple;\">Order.API<\/code> ve di\u011fer t\u00fcm servisler sade ve sadece <code style=\"color:purple;\">Saga.Orchestration.Service<\/code>&#8216;ten mesaj alacak ya da ona mesaj g\u00f6nderecektir. Ayr\u0131ca bu servisin g\u00f6nderece\u011fi mesajlar\u0131 da, <code>inputQueueName<\/code> parametresine bakarsak <code>RabbitMQQueueSettings.OrderInputQueueName<\/code> de\u011ferine kar\u015f\u0131l\u0131k gelen kuyru\u011fa g\u00f6nderece\u011fini ifade etmi\u015f bulunmaktay\u0131z. Haliyle buradaki yap\u0131land\u0131rma mant\u0131\u011f\u0131n\u0131 di\u011fer servislerde de birebir g\u00f6r\u00fcyor olaca\u011f\u0131z.<\/p>\n<p><code>AutoRegisterHandlersFromAssemblyOf<\/code> metodu ise sonraki ad\u0131mlarda olu\u015fturaca\u011f\u0131m\u0131z handler s\u0131n\u0131flar\u0131n\u0131n assembly seviyesinde otomatik olarak yakalay\u0131p, register edilmesini sa\u011flamaktad\u0131r. Ha tabi, her ne kadar bu senaryoda <code style=\"color:purple;\">Order.API<\/code>&#8216;da bir handler s\u0131n\u0131f\u0131 olu\u015fturmayacak olsak da Rebus ile yap\u0131lan \u00e7al\u0131\u015fmalarda handler yap\u0131lanmalar\u0131n\u0131n aktivite edilmesi i\u00e7in bu metodun \u00e7a\u011fr\u0131lmas\u0131 elzemdir ve sonraki s\u00fcre\u00e7lerde olu\u015fturma ihtimaline nazaran uygulama seviyesinde \u00f6nceden \u00e7a\u011fr\u0131l\u0131p haz\u0131r bir \u015fekilde tutulmas\u0131 k\u00e2fidir.\n<\/li>\n<li><strong><em>Ad\u0131m 13<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Order.API &#8211; (Sipari\u015f Olu\u015fturma S\u00fcrecinde OrderStartedEvent&#8217;in Yay\u0131nlanmas\u0131)<\/span><br \/>\n\u015eimdi de 2. ad\u0131mda <code style=\"color:purple;\">Order.API<\/code>&#8216;da geli\u015ftirdi\u011fimiz <code>\/create-order<\/code> minimal api&#8217;sinde a\u015fa\u011f\u0131daki gibi <em>OrderStartedEvent<\/em>&#8216;i yay\u0131nlayal\u0131m ve distributed i\u015f ak\u0131\u015f\u0131 s\u00fcrecini ba\u015flatal\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n.\r\n.\r\n.\r\n\r\napp.MapPost(&quot;\/create-order&quot;, async (IBus bus, ApplicationDbContext context, CreateOrderVM model) =&gt;\r\n{\r\n    .\r\n    .\r\n    .\r\n\r\n    OrderStartedEvent orderStartedEvent = new()\r\n    {\r\n        OrderId = order.Id,\r\n        BuyerId = model.BuyerId,\r\n        TotalPrice = order.TotalPrice,\r\n        OrderItems = model.OrderItems.Select(oi =&gt; new Shared.Datas.OrderItem\r\n        {\r\n            Count = oi.Count,\r\n            Price = oi.Price,\r\n            ProductId = oi.ProductId\r\n        }).ToList(),\r\n        CorrelationId = Guid.NewGuid(),\r\n    };\r\n\r\n    await bus.Send(orderStartedEvent);\r\n\r\n    return Results.Created();\r\n});\r\n\r\napp.Run();\r\n<\/pre>\n<\/div>\n<\/li>\n<li><strong><em>Ad\u0131m 14<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Stock.Service &#8211; (Program.cs Yap\u0131land\u0131rmas\u0131)<\/span><br \/>\n<code style=\"color:purple;\">Stock.Service<\/code>&#8216;de de \u00f6nceki ad\u0131mlarda anlat\u0131lan mant\u0131kta yap\u0131land\u0131rmay\u0131 a\u015fa\u011f\u0131daki gibi sa\u011flayal\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n.\r\n.\r\n.\r\n\r\nbuilder.Services.AddRebus(rebus =&gt; rebus\r\n     .Routing(r =&gt; r.TypeBased().MapAssemblyOf&lt;BaseCorrelation&gt;(RabbitMQQueueSettings.SagaOrchestrationInputQueueName))\r\n     .Transport(t =&gt; t.UseRabbitMq(RabbitMQAMQPUrl.AMQPUrl, inputQueueName: RabbitMQQueueSettings.StockInputQueueName)));\r\n\r\nbuilder.Services.AutoRegisterHandlersFromAssemblyOf&lt;Program&gt;();\r\n.\r\n.\r\n.\r\n<\/pre>\n<\/div>\n<\/li>\n<li><strong><em>Ad\u0131m 15<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Stock.Service &#8211; (OrderCreatedEventHandler ve StockRollbackMessageHandler&#8217;\u0131n Olu\u015fturulmas\u0131)<\/span><br \/>\n<code style=\"color:purple;\">Stock.Service<\/code>&#8216;i yap\u0131land\u0131rd\u0131ktan sonra gerekli handler s\u0131n\u0131flar\u0131n\u0131 a\u015fa\u011f\u0131daki gibi olu\u015ftural\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderCreatedEventHandler(MongoDBService mongoDBService, IBus bus) : IHandleMessages&lt;OrderCreatedEvent&gt;\r\n    {\r\n        public async Task Handle(OrderCreatedEvent message)\r\n        {\r\n            List&lt;bool&gt; stockResults = new();\r\n            var stockCollection = mongoDBService.GetCollection&lt;Models.Stock&gt;();\r\n\r\n            foreach (var orderItem in message.OrderItems)\r\n                stockResults.Add(await (await stockCollection.FindAsync(s =&gt; s.ProductId == orderItem.ProductId &amp;&amp; s.Count &gt;=\r\n(long)orderItem.Count)).AnyAsync());\r\n\r\n            if (stockResults.TrueForAll(s =&gt; s.Equals(true)))\r\n            {\r\n                foreach (var orderItem in message.OrderItems)\r\n                {\r\n                    var stock = await (await stockCollection.FindAsync(s =&gt; s.ProductId == orderItem.ProductId)).FirstOrDefaultAsync();\r\n\r\n                    stock.Count -= orderItem.Count;\r\n\r\n                    await stockCollection.FindOneAndReplaceAsync(x =&gt; x.ProductId == orderItem.ProductId, stock);\r\n                }\r\n\r\n                StockReservedEvent stockReservedEvent = new(message.CorrelationId)\r\n                {\r\n                    OrderItems = message.OrderItems\r\n                };\r\n\r\n                await bus.Send(stockReservedEvent);\r\n            }\r\n            else\r\n            {\r\n                StockNotReservedEvent stockNotReservedEvent = new(message.CorrelationId)\r\n                {\r\n                    Message = &quot;Stok yetersiz....&quot;\r\n                };\r\n\r\n                await bus.Send(stockNotReservedEvent);\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockRollbackMessageHandler(MongoDBService mongoDBService) : IHandleMessages&lt;StockRollbackMessage&gt;\r\n    {\r\n        public async Task Handle(StockRollbackMessage message)\r\n        {\r\n            var stockCollection = mongoDBService.GetCollection&lt;Models.Stock&gt;();\r\n\r\n            foreach (var orderItem in message.OrderItems)\r\n            {\r\n                var stock = await (await stockCollection.FindAsync(x =&gt; x.ProductId == orderItem.ProductId)).FirstOrDefaultAsync();\r\n\r\n                stock.Count += orderItem.Count;\r\n                await stockCollection.FindOneAndReplaceAsync(x =&gt; x.ProductId == orderItem.ProductId, stock);\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Yukar\u0131daki kodlar\u0131 incelerseniz e\u011fer gerekli stok i\u015flemleri ger\u00e7ekle\u015ftirilmekte yahut compensable transaction operasyonu y\u00fcr\u00fct\u00fclmektedir.\n<\/li>\n<li><strong><em>Ad\u0131m 16<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Payment.Service &#8211; (Program.cs Yap\u0131land\u0131rmas\u0131)<\/span><br \/>\nAyn\u0131 mant\u0131kta <code style=\"color:purple;\">Payment.Service<\/code>&#8216;de de &#8216;Program.cs&#8217; yap\u0131land\u0131rmas\u0131n\u0131 ger\u00e7ekle\u015ftirelim.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n.\r\n.\r\n.\r\nbuilder.Services.AddRebus(rebus =&gt; rebus\r\n     .Routing(r =&gt; r.TypeBased().MapAssemblyOf&lt;BaseCorrelation&gt;(RabbitMQQueueSettings.SagaOrchestrationInputQueueName))\r\n     .Transport(t =&gt; t.UseRabbitMq(RabbitMQAMQPUrl.AMQPUrl, inputQueueName: RabbitMQQueueSettings.PaymentInputQueueName)));\r\n\r\nbuilder.Services.AutoRegisterHandlersFromAssemblyOf&lt;Program&gt;();\r\n.\r\n.\r\n.\r\n<\/pre>\n<\/div>\n<\/li>\n<li><strong><em>Ad\u0131m 17<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Payment.Service &#8211; (PaymentStartedEventHandler&#8217;\u0131n Olu\u015fturulmas\u0131)<\/span><br \/>\nVe \u00f6demeyle ilgili i\u015flemleri ba\u015flatacak olan <em>PaymentStartedEvent<\/em>&#8216;e kar\u015f\u0131n <em>PaymentStartedEventHandler<\/em>&#8216;\u0131 olu\u015ftural\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentStartedEventHandler(IBus bus) : IHandleMessages&lt;PaymentStartedEvent&gt;\r\n    {\r\n        public async Task Handle(PaymentStartedEvent message)\r\n        {\r\n            \/\/\u00d6deme ba\u015far\u0131l\u0131...\r\n            await bus.Send(new PaymentCompletedEvent(message.CorrelationId));\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Burada farazi bir \u00f6deme i\u015fleminin yap\u0131ld\u0131\u011f\u0131n\u0131 varsayarak yolumuza devam edelim.\n<\/li>\n<li><strong><em>Ad\u0131m 19<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Order.Saga &#8211; (Program.cs Yap\u0131land\u0131rmas\u0131)<\/span><br \/>\nVe son olarak <code style=\"color:purple;\">Order.Saga<\/code> servisinde de &#8216;Program.cs&#8217; yap\u0131land\u0131rmas\u0131n\u0131 ger\u00e7ekle\u015ftirelim.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n.\r\n.\r\n.\r\nbuilder.Services.AddRebus(rebus =&gt; rebus\r\n     .Routing(r =&gt; r.TypeBased().MapAssemblyOf&lt;BaseCorrelation&gt;(RabbitMQQueueSettings.SagaOrchestrationInputQueueName))\r\n     .Transport(t =&gt; t.UseRabbitMq(RabbitMQAMQPUrl.AMQPUrl, inputQueueName: RabbitMQQueueSettings.OrderInputQueueName)));\r\n\r\nbuilder.Services.AutoRegisterHandlersFromAssemblyOf&lt;Program&gt;();\r\n.\r\n.\r\n.\r\n<\/pre>\n<\/div>\n<\/li>\n<li><strong><em>Ad\u0131m 18<\/em><\/strong> <span style=\"font-size:12px;color:green;\">Order.Saga &#8211; (OrderCompletedEventHandler ve OrderFailedEventHandler&#8217;\u0131n Olu\u015fturulmas\u0131)<\/span><br \/>\nVe ard\u0131ndan sipari\u015fin nihai durumunu belirleyecek olan handler&#8217;lar\u0131 olu\u015ftural\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderCompletedEventHandler(ApplicationDbContext applicationDbContext) : IHandleMessages&lt;OrderCompletedEvent&gt;\r\n    {\r\n        public async Task Handle(OrderCompletedEvent message)\r\n        {\r\n            Order.Saga.Models.Entities.Order order = await applicationDbContext.Orders.FindAsync(message.OrderId);\r\n            if (order != null)\r\n            {\r\n                order.OrderStatus = Models.Enums.OrderStatus.Completed;\r\n                await applicationDbContext.SaveChangesAsync();\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderFailedEventHandler(ApplicationDbContext applicationDbContext) : IHandleMessages&lt;OrderFailedEvent&gt;\r\n    {\r\n        public async Task Handle(OrderFailedEvent message)\r\n        {\r\n            Order.Saga.Models.Entities.Order order = await applicationDbContext.Orders.FindAsync(message.OrderId);\r\n            if (order != null)\r\n            {\r\n                order.OrderStatus = Order.Saga.Models.Enums.OrderStatus.Fail;\r\n                await applicationDbContext.SaveChangesAsync();\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Burada dikkat edilmesi gereken nokta \u015fudur ki, <code style=\"color:purple;\">Order.Saga<\/code> servisinde yap\u0131lan veritaban\u0131 \u00e7al\u0131\u015fmalar\u0131nda <code style=\"color:purple;\">Order.API<\/code>&#8216;dakine benzer context ve entity yap\u0131s\u0131 in\u015fa edilmi\u015f ve IoC Container&#8217;a provide edilmi\u015ftir. Tabi tekrara girmemek ve i\u00e7eri\u011fi haddinden fazla uzatmamak i\u00e7in bu k\u0131s\u0131mlar es ge\u00e7ilmi\u015ftir. Yap\u0131lan \u00e7al\u0131\u015fmalar\u0131n tam teferruatl\u0131 i\u00e7eri\u011fine makalenin sonunda payla\u015f\u0131lan github linki \u00fczerinden g\u00f6zlemde bulunabilirsiniz.\n<\/li>\n<li><strong><em>Ad\u0131m 19<\/em><\/strong> <span style=\"font-size:12px;color:red;\">Test<\/span><br \/>\nArt\u0131k yap\u0131lan bunca \u00e7al\u0131\u015fman\u0131n testini ger\u00e7ekle\u015ftirebiliriz. Bunun i\u00e7in servislerimizi derleyip aya\u011fa kald\u0131ral\u0131m ve a\u015fa\u011f\u0131daki json data e\u015fli\u011finde bir sipari\u015f talebinde bulunal\u0131m.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n{\r\n  &quot;buyerId&quot;: 1,\r\n  &quot;orderItems&quot;: &#x5B;\r\n    {\r\n      &quot;productId&quot;: 1,\r\n      &quot;count&quot;: 10,\r\n      &quot;price&quot;: 5\r\n    },\r\n    {\r\n      &quot;productId&quot;: 2,\r\n      &quot;count&quot;: 30,\r\n      &quot;price&quot;: 50\r\n    },\r\n    {\r\n      &quot;productId&quot;: 3,\r\n      &quot;count&quot;: 5,\r\n      &quot;price&quot;: 5\r\n    }\r\n  ]\r\n}\r\n<\/pre>\n<\/div>\n<p><a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-4.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-4.png\" alt=\".NET&#039;te Rebus K\u00fct\u00fcphanesiyle Saga Orchestration Yakla\u015f\u0131m\u0131n\u0131n Uygulanmas\u0131\" width=\"720\" height=\"631\" class=\"aligncenter size-full wp-image-26846\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-4.png 720w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi-4-300x263.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><\/a>Bu i\u015flem neticesinde s\u00fcrecin a\u015fa\u011f\u0131daki g\u00f6rseldeki gibi oldu\u011funu g\u00f6rece\u011fiz.<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi.gif\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2024\/01\/NETte-Rebus-Kutuphanesiyle-Saga-Orchestration-Yaklasiminin-Uygulanmasi.gif\" alt=\".NET\u2019te Rebus K\u00fct\u00fcphanesiyle Saga Orchestration Yakla\u015f\u0131m\u0131n\u0131n Uygulanmas\u0131\" width=\"800\" height=\"450\" class=\"aligncenter size-full wp-image-26848\" \/><\/a>Ayr\u0131ca yeri gelmi\u015fken bahsetmekte fayda g\u00f6rmekteyim ki, distributed i\u015f ak\u0131\u015f\u0131 s\u00fcrecinde Sagas tablosunun &#8216;data&#8217; kolonundaki binary veriyi a\u015fa\u011f\u0131daki sorguyla metinsele d\u00f6n\u00fc\u015ft\u00fcrebilir ve i\u00e7eri\u011fine dair g\u00f6zlemde bulunabilirsiniz.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nSELECT CONVERT(VARCHAR(MAX), data)\r\nFROM SagaOrchestrationRebusDB.dbo.Sagas\r\n<\/pre>\n<\/div>\n<div style=\"font-size:12px;line-height: 25px;background-color:black;color:white\">\n{&#8220;$type&#8221;:&#8221;Saga.Orchestration.Service.SagaDatas.OrderSagaData, Saga.Orchestration.Service&#8221;,&#8221;Id&#8221;:&#8221;7e630713-c610-4c9b-8a38-03f61f3060a8&#8243;,&#8221;Revision&#8221;:1,&#8221;OrderId&#8221;:5,&#8221;BuyerId&#8221;:1,&#8221;TotalPrice&#8221;:1825.0,&#8221;OrderItems&#8221;:{&#8220;$type&#8221;:&#8221;System.Collections.Generic.List`1[[Shared.Datas.OrderItem, Shared]], System.Private.CoreLib&#8221;,&#8221;$values&#8221;:[{&#8220;$type&#8221;:&#8221;Shared.Datas.OrderItem, Shared&#8221;,&#8221;ProductId&#8221;:1,&#8221;Count&#8221;:10,&#8221;Price&#8221;:5.0},{&#8220;$type&#8221;:&#8221;Shared.Datas.OrderItem, Shared&#8221;,&#8221;ProductId&#8221;:2,&#8221;Count&#8221;:30,&#8221;Price&#8221;:50.0},{&#8220;$type&#8221;:&#8221;Shared.Datas.OrderItem, Shared&#8221;,&#8221;ProductId&#8221;:4,&#8221;Count&#8221;:55,&#8221;Price&#8221;:5.0}]},&#8221;CorrelationId&#8221;:&#8221;c5d0b313-2bf4-4081-935f-72fa0c9895f0&#8243;}\n<\/div>\n<\/li>\n<\/ul>\n<p>\u0130\u015fte bu kadar \ud83d\ude42<\/p>\n<p>Nihai olarak;<br \/>\nSaga Orchestration implementasyonunu Rebus k\u00fct\u00fcphanesiyle, test ad\u0131m\u0131 da dahil olmak \u00fczere 19 ad\u0131mda uygulam\u0131\u015f ve MassTransit&#8217;e nazaran konuya dair alternatif bir yakla\u015f\u0131m\u0131 tecr\u00fcbe etmi\u015f bulunmaktay\u0131z. Nacizane fikrim, Rebus k\u00fct\u00fcphanesi MassTransit&#8217;e kar\u015f\u0131n yap\u0131land\u0131rma a\u00e7\u0131s\u0131ndan d\u00fc\u015f\u00fck bir komplikasyona sahip olsa da onun kadar geni\u015f bir yelpazede entegrasyon imkan\u0131 sa\u011flamamakta ve k\u00fc\u00e7\u00fck yahut orta \u00f6l\u00e7ekli uygulamalarda daha pratik \u00e7\u00f6z\u00fcmler sunmaktad\u0131r.<\/p>\n<p>\u0130lgilenenlerin faydalanmas\u0131 dile\u011fiyle&#8230;<br \/>\nSonraki yaz\u0131lar\u0131mda g\u00f6r\u00fc\u015fmek \u00fczere&#8230;<br \/>\n\u0130yi \u00e7al\u0131\u015fmalar&#8230;<\/p>\n<p>Not : \u00d6rnek \u00e7al\u0131\u015fmaya a\u015fa\u011f\u0131daki github adresinden eri\u015febilirsiniz.<br \/>\n<a href=\"https:\/\/github.com\/gncyyldz\/Saga.Orchestration.Example.With.Rebus\" rel=\"noopener\" target=\"_blank\">https:\/\/github.com\/gncyyldz\/Saga.Orchestration.Example.With.Rebus<\/a><\/p>\n<!-- AddThis Advanced Settings generic via filter on the_content --><!-- AddThis Share Buttons generic via filter on the_content -->","protected":false},"excerpt":{"rendered":"<p>Merhaba, Malumunuz, microservice mimarisiyle tasarlanm\u0131\u015f yaz\u0131l\u0131mlar\u0131m\u0131zda kullan\u0131c\u0131lar taraf\u0131ndan ba\u015flat\u0131lan i\u015flem ya da g\u00f6revlerin, bir servis taraf\u0131ndan al\u0131n\u0131p ard\u0131ndan belirli servisler \u00fczerinde mant\u0131ksal bir tetikleme silsilesiyle i\u015flenme ve koordine edilme s\u00fcre\u00e7lerini y\u00fcr\u00fctmemize ve biryandan da&#46;&#46;&#46;<!-- AddThis Advanced Settings generic via filter on get_the_excerpt --><!-- AddThis Share Buttons generic via filter on get_the_excerpt --><\/p>\n","protected":false},"author":1,"featured_media":26849,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4912,2739,3573],"tags":[3575,4994,4996,4995,4993,4372],"class_list":["post-26819","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net-8","category-net-core","category-microservices","tag-microservice","tag-rebus","tag-rebus-microservice","tag-rebus-saga","tag-rebus-saga-orchestration","tag-saga-orchestration"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/26819","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/comments?post=26819"}],"version-history":[{"count":20,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/26819\/revisions"}],"predecessor-version":[{"id":26850,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/26819\/revisions\/26850"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media\/26849"}],"wp:attachment":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media?parent=26819"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/categories?post=26819"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/tags?post=26819"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}