{"id":1003,"date":"2017-01-08T00:20:13","date_gmt":"2017-01-07T22:20:13","guid":{"rendered":"https:\/\/gokhan-gokalp.com\/?p=1003"},"modified":"2018-09-26T14:03:34","modified_gmt":"2018-09-26T11:03:34","slug":"masstransit-saga-state-machine-ile-model-workflow-u-olusturmak","status":"publish","type":"post","link":"https:\/\/gokhan-gokalp.com\/tr\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/","title":{"rendered":"MassTransit Saga State Machine ile Model Workflow&#8217;u Olu\u015fturmak"},"content":{"rendered":"<p>Merhaba arkada\u015flar.<\/p>\n<p>Bir s\u00fcredir gerek yo\u011fun i\u015f temposu gerekse de sosyal hayat\u0131mdaki baz\u0131\u00a0yo\u011funluklardan dolay\u0131, yeni bir makale yazmaya f\u0131rsat bulamam\u0131\u015ft\u0131m. Sizlerde fark ederseniz bir s\u00fcredir makale konular\u0131m\u0131 microservice ve messaging yap\u0131lar\u0131 \u00fczerine yo\u011funla\u015ft\u0131rd\u0131m.<\/p>\n<p>Bu makale i\u00e7eri\u011finde ise geli\u015ftiriyor oldu\u011fumuz microservice ve messaging yap\u0131lar\u0131nda, long-running business process i\u015flemlerinde consistency&#8217;i MassTransit Saga State Machine implementasyonu ile nas\u0131l sa\u011flar\u0131z ve nas\u0131l bir model workflow&#8217;u olu\u015ftururuz gibi konulara, ara\u015ft\u0131rmalar\u0131m s\u0131ras\u0131nda edinebildi\u011fim bilgiler do\u011frultusunda\u00a0de\u011finmeye \u00e7al\u0131\u015faca\u011f\u0131m.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2016\/12\/16631677-abstract-word-cloud-for-distributed-transaction-with-related-tags-and-terms.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-1007 lazyload\" data-src=\"\/wp-content\/uploads\/2016\/12\/16631677-abstract-word-cloud-for-distributed-transaction-with-related-tags-and-terms.jpg\" alt=\"16631677-abstract-word-cloud-for-distributed-transaction-with-related-tags-and-terms\" width=\"450\" height=\"335\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/12\/16631677-abstract-word-cloud-for-distributed-transaction-with-related-tags-and-terms.jpg 450w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/12\/16631677-abstract-word-cloud-for-distributed-transaction-with-related-tags-and-terms-300x223.jpg 300w\" data-sizes=\"(max-width: 450px) 100vw, 450px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 450px; --smush-placeholder-aspect-ratio: 450\/335;\" \/><\/a><\/p>\n<p>Dilerseniz \u00f6ncelikle Saga kavram\u0131n\u0131 biraz a\u00e7al\u0131m.<\/p>\n<h4>Nedir Bu Saga?<\/h4>\n<blockquote><p>Saga pattern&#8217;\u0131, birden \u00e7ok sistem ile ilgili bir collaborating s\u00f6z konusu oldu\u011funda ve herhangi bir failure an\u0131nda\u00a0<strong>backtrack<\/strong> veya\u00a0<strong>corrective<\/strong> bir aksiyon al\u0131nabilmesini sa\u011flayan bir patttern&#8217;d\u0131r.<\/p><\/blockquote>\n<p>Farkl\u0131 bir tan\u0131mla ise long-running business process&#8217;lerde consistency&#8217;i sa\u011flayabilmek i\u00e7in distributed transaction kullan\u0131m mekanizmas\u0131 olarak da tan\u0131mlayabiliriz. Saga kavram\u0131, Hector Garcaa-Molrna ve Kenneth Salem taraf\u0131ndan <em><a href=\"http:\/\/www.cs.cornell.edu\/andru\/cs711\/2002fa\/reading\/sagas.pdf\" target=\"_blank\" rel=\"noopener\">Sagas<\/a> <\/em>isimli akademik \u00e7al\u0131\u015fmas\u0131 ile ele al\u0131nm\u0131\u015ft\u0131r.<\/p>\n<p>Bir \u00f6nceki &#8220;<em><a href=\"https:\/\/gokhan-gokalp.com\/masstransit-kullanarak-rabbitmq-ile-messaging-altyapisi-olusturma\/\" target=\"_blank\" rel=\"noopener\">MassTransit kullanarak RabbitMQ ile Messaging Altyap\u0131s\u0131 Olu\u015fturma<\/a><\/em>&#8221;\u00a0makalesinde ger\u00e7ekle\u015ftirdi\u011fimiz \u00f6rnek senaryoyu hat\u0131rlarsak e\u011fer:<\/p>\n<p><a href=\"\/wp-content\/uploads\/2016\/11\/order-mq.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-963 lazyload\" data-src=\"\/wp-content\/uploads\/2016\/11\/order-mq.jpg\" alt=\"order-mq\" width=\"1046\" height=\"395\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/11\/order-mq.jpg 1046w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/11\/order-mq-300x113.jpg 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/11\/order-mq-1024x387.jpg 1024w\" data-sizes=\"(max-width: 1046px) 100vw, 1046px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1046px; --smush-placeholder-aspect-ratio: 1046\/395;\" \/><\/a><\/p>\n<p>Yukar\u0131daki gibi basit bir\u00a0e-commerce sistemindeki sipari\u015f i\u015flemini \u00f6rnek vermi\u015ftik.<\/p>\n<p>Burada yer alan &#8220;OrderService&#8221; ile &#8220;IOrderCommand&#8221; tipindeki\u00a0message&#8217;lar\u0131 consume ederek, farkl\u0131 i\u015flemler ve event&#8217;lar ger\u00e7ekle\u015ftirmi\u015ftik.\u00a0Sonras\u0131nda ise sistemin &#8220;XCommand&#8221; message&#8217;\u0131na\u00a0g\u00f6re i\u015flem yapacak olan &#8220;XService&#8221; lere sahip olabilece\u011finden de bahsetmi\u015ftik. Gelin buraya order i\u015flemi ile ilgili bir ka\u00e7\u00a0yeni ad\u0131m daha ekleyelim.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2016\/12\/saga-order.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-1008 lazyload\" data-src=\"\/wp-content\/uploads\/2016\/12\/saga-order.jpg\" alt=\"saga-order\" width=\"1005\" height=\"561\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/12\/saga-order.jpg 1005w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/12\/saga-order-300x167.jpg 300w\" data-sizes=\"(max-width: 1005px) 100vw, 1005px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1005px; --smush-placeholder-aspect-ratio: 1005\/561;\" \/><\/a><\/p>\n<p>Order senaryosuna &#8220;BillingService&#8221; ve &#8220;FraudService&#8221; ide dahil ettik.<\/p>\n<p>Order i\u015fleminin tamamlanabilmesi i\u00e7in event&#8217;lar sequentially veya parallel olarak distributed bir \u015fekilde bu service&#8217;ler aras\u0131nda i\u015flenmektedir. Peki bunar gibi -n kadar daha service&#8217;i\u00a0sisteme dahil etti\u011fimizi\u00a0d\u00fc\u015f\u00fcnelim. Peki \u015fimdi birbirleri ile related olan bu service&#8217;ler aralar\u0131ndaki message flow&#8217;u, nas\u0131l manage edilebilecek? Bunlara ek olarak birde ger\u00e7ekten uygulamalar\u0131 microservice olarak geli\u015ftiriyorsak ve\u00a0order i\u015flemi gibi belli step&#8217;lerin sonucunda bir <strong>artifact<\/strong> olu\u015fturan s\u00fcre\u00e7lere de sahipsek, transaction b\u00fct\u00fcnl\u00fc\u011f\u00fc <strong>reliable<\/strong> bir \u015fekilde nas\u0131l sa\u011flanacak? \u00c7\u00fcnk\u00fc baz\u0131 event&#8217;lar ba\u015far\u0131l\u0131 bir \u015fekilde process edilebilirken baz\u0131lar\u0131n\u0131n ise fail olabilme durumu mevcut olacakt\u0131r. Bu tarz i\u015flemler ise tutars\u0131z(inconsistent) sonu\u00e7lar do\u011furacakt\u0131r.<\/p>\n<p>\u0130\u015fte bu tarz s\u00fcre\u00e7ler\u00a0i\u00e7in MassTransit framework&#8217;\u00fc i\u00e7erisindeki Saga State Machine ile bir workflow olu\u015fturaca\u011f\u0131z. Bu implementasyon ile temelinde birden \u00e7ok i\u015f ak\u0131\u015flar\u0131n\u0131n birden \u00e7ok sisteme da\u011f\u0131lmas\u0131nda, herhangi bir i\u015f a\u015famas\u0131nda meydana gelebilecek olan aksi bir eyleme kar\u015f\u0131 \u00f6nlem(compensation) al\u0131nabilmesini sa\u011flayacak yap\u0131lar yazabilmek\u00a0m\u00fcmk\u00fcnd\u00fcr.<\/p>\n<p>Peki Saga bunu nas\u0131l sa\u011fl\u0131yor?<\/p>\n<p><a href=\"\/wp-content\/uploads\/2016\/12\/saga_compensation.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-1010 lazyload\" data-src=\"\/wp-content\/uploads\/2016\/12\/saga_compensation.png\" alt=\"saga_compensation\" width=\"1024\" height=\"300\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/12\/saga_compensation.png 1024w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/12\/saga_compensation-300x88.png 300w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/300;\" \/><\/a><\/p>\n<p>Yukar\u0131daki resimde order i\u015flemi gibi long lived business process&#8217;in oldu\u011funu d\u00fc\u015f\u00fcnelim ve burada, transaction T4 durumundan C4 durumuna ge\u00e7erken bir hata al\u0131nd\u0131\u011f\u0131 i\u00e7in tekrardan T3 durumundan T4 durumuna bir <strong>compensation<\/strong>\u00a0sa\u011fland\u0131\u011f\u0131n\u0131 g\u00f6r\u00fcyoruz.<\/p>\n<p>Implementasyon k\u0131sm\u0131na ba\u015flamadan \u00f6nce, sizlere bir ka\u00e7 tamamlay\u0131c\u0131 kavramdan bahsetmek istiyorum. \u00d6ncelikle model workflow&#8217;u olu\u015fturmak, bir saga <strong>de\u011fildir<\/strong>. \u00a0Workflow state&#8217;den ba\u011f\u0131ms\u0131zd\u0131r ve State Machine&#8217;in aksine <strong>activity<\/strong>&#8216;ler ile ilgilenir. Workflow \u00fczerindeki transition&#8217;lar ise, herhangi bir action tamamland\u0131\u011f\u0131nda ger\u00e7ekle\u015fir. Workflow, State Machine ile beraber tan\u0131mlanabilir ve i\u015f ak\u0131\u015flar\u0131 belirli state&#8217;ler do\u011frultusunda ilerletilebilinir.<\/p>\n<h4>State Machine Nedir?<\/h4>\n<p>State Machine, bir object&#8217;in bir state&#8217;den bir di\u011fer state&#8217;e\u00a0 transition&#8217;\u0131 ile ilgilenir. State Machine bu i\u015flemi ise saga object&#8217;ine gelen\u00a0bir request&#8217;in state&#8217;ini, bir ba\u015fka yerde <strong>correlation id<\/strong> ile beraber persist ederek handle eder. Sonras\u0131nda ise o correlation id&#8217;ye ba\u011fl\u0131 olarak ger\u00e7ekle\u015fen event&#8217;lar ile object state&#8217;ini y\u00f6netir.<\/p>\n<p>Bu yap\u0131 ise long-running business process&#8217;lerde\u00a0state koordinasyonunu merkezile\u015ftirmeyi ve tutarl\u0131l\u0131\u011f\u0131n\u0131 sa\u011flamaktad\u0131r. A\u00e7\u0131k\u00e7as\u0131 biraz karma\u015f\u0131k bir konu fakat ilgili business&#8217;a ba\u011fl\u0131 olarak uyguland\u0131\u011f\u0131nda, ta\u015flar yerlerine kendili\u011finden oturuyor.<\/p>\n<h4>MassTransit Saga State Machine ile Model Workflow&#8217;u\u00a0Implementasyonu<\/h4>\n<p>Ger\u00e7ekle\u015ftirecek oldu\u011fumuz \u00f6rnekte\u00a0daha \u00f6nce de bahsetti\u011fim gibi bir \u00f6nceki &#8220;<em><a href=\"https:\/\/gokhan-gokalp.com\/masstransit-kullanarak-rabbitmq-ile-messaging-altyapisi-olusturma\/\" target=\"_blank\" rel=\"noopener\">MassTransit kullanarak RabbitMQ ile Messaging Altyap\u0131s\u0131 Olu\u015fturma<\/a><\/em>&#8221; makale konusu \u00fczerinden devam edece\u011fiz.\u00a0Bu \u00f6rnek \u00fczerinde bir ka\u00e7 noktay\u0131 refactor edece\u011fiz ve\u00a0ard\u0131ndan MassTransit Saga implementasyonunu ekstra bir &#8220;BillingService&#8221; i daha ekleyerek, bir model workflow&#8217;u olu\u015fturaca\u011f\u0131z.<\/p>\n<p>Refactor edece\u011fimiz ilk nokta hat\u0131rlarsak ilk message&#8217;\u0131 produce etti\u011fimiz nokta &#8220;LightMessagingCore.Boilerplate.OrderUI&#8221; projesi idi.<\/p>\n<pre class=\"lang:c# decode:true\">public class OrderController : Controller\r\n{\r\n    private readonly ISendEndpoint _bus;\r\n\r\n    public OrderController()\r\n    {\r\n        var busControl = BusConfigurator.Instance.ConfigureBus();\r\n        var sendToUri = new Uri($\"{MqConstants.RabbitMQUri}{ConfigurationManager.AppSettings[\"OrderQueueName\"]}\");\r\n\r\n        _bus = busControl.GetSendEndpoint(sendToUri).Result;\r\n    }\r\n\r\n    \/\/ GET: Order\r\n    public ActionResult Index(OrderModel orderModel)\r\n    {\r\n        if (orderModel.OrderId &gt; 0)\r\n            CreateOrder(orderModel);\r\n\r\n        return View();\r\n    }\r\n\r\n    private void CreateOrder(OrderModel orderModel)\r\n    {\r\n        _bus.Send(orderModel).Wait();\r\n    }\r\n}<\/pre>\n<p>Burada &#8220;OrderController&#8221; i\u00e7erisinde initialize etti\u011fimiz bus \u00fczerinden &#8220;OrderQueue&#8221; ya bir &#8220;IOrderCommand&#8221; produce ediyorduk.<\/p>\n<p>Bu noktada art\u0131k ilgili command&#8217;\u0131 direkt olarak &#8220;OrderQueue&#8221; ya produce etmek yerine, &#8220;SagaQueue&#8221; ya produce edece\u011fiz. Bunun i\u00e7in &#8220;sendToUri&#8221; k\u0131sm\u0131n\u0131 a\u015fa\u011f\u0131daki gibi g\u00fcncelleyelim.<\/p>\n<pre class=\"lang:c# decode:true \">var sendToUri = new Uri($\"{MqConstants.RabbitMQUri}{ConfigurationManager.AppSettings[\"SagaQueueName\"]}\");<\/pre>\n<p>\u00c7\u00fcnk\u00fc bir Saga State Machine tan\u0131mlayarak, bir model workflow&#8217;u olu\u015fturaca\u011f\u0131z. Ilk produce olan command ile birlikte bir state olu\u015fturaca\u011f\u0131z ve correlation&#8217;\u0131 sa\u011flayarak ilgili process&#8217;leri MassTransit State Machine&#8217;i \u00fczerinden devam ettirece\u011fiz. Bu y\u00fczden command&#8217;\u0131n ilk produce olaca\u011f\u0131 nokta, Saga Queue&#8217;su olacakt\u0131r.<\/p>\n<p>URI&#8217;\u0131 g\u00fcncelledikten sonra &#8220;Web.config&#8221; e gelerek, &#8220;SagaQueueName&#8221; key&#8217;ini a\u015fa\u011f\u0131daki gibi tan\u0131mlayal\u0131m.<\/p>\n<pre class=\"lang:c# decode:true\">  &lt;appSettings&gt;\r\n    &lt;add key=\"RabbitMQUri\" value=\"rabbitmq:\/\/localhost\/\" \/&gt;\r\n    &lt;add key=\"RabbitMQUserName\" value=\"guest\" \/&gt;\r\n    &lt;add key=\"RabbitMQPassword\" value=\"guest\" \/&gt;\r\n    &lt;add key=\"SagaQueueName\" value=\"lightmessagingcore.boilerplate.saga\"\/&gt;\r\n  &lt;\/appSettings&gt;<\/pre>\n<p>&#8220;LightMessagingCore.Boilerplate.OrderUI&#8221; projesi i\u00e7erisinde yapacaklar\u0131m\u0131z bu kadar.<\/p>\n<p>Sagas implementasyonuna ge\u00e7meden \u00f6nce ihtiya\u00e7\u00a0duyaca\u011f\u0131m\u0131z messaging contract&#8217;lar\u0131n\u0131, &#8220;LightMessagingCore.Boilerplate.Messaging&#8221; projesi alt\u0131nda a\u015fa\u011f\u0131daki gibi tan\u0131mlayal\u0131m.<\/p>\n<pre class=\"lang:c# decode:true \">using System;\r\n\r\nnamespace LightMessagingCore.Boilerplate.Messaging\r\n{\r\n    public interface IOrderReceivedEvent\r\n    {\r\n        Guid CorrelationId { get; }\r\n        int OrderId { get; }\r\n        string OrderCode { get; }\r\n    }\r\n}<\/pre>\n<p>&#8220;IOrderReceivedEvent&#8221; \u0131n\u0131 &#8220;OrderService&#8221; i\u00e7erisinde, order sagas \u00fczerinden bir message\u00a0produce edildi\u011finde consume edebilmek i\u00e7in\u00a0kullan\u0131yor olaca\u011f\u0131z.<\/p>\n<pre class=\"lang:c# decode:true \">using System;\r\n\r\nnamespace LightMessagingCore.Boilerplate.Messaging\r\n{\r\n    public interface IOrderProcessedEvent\r\n    {\r\n        Guid CorrelationId { get; set; }\r\n        int OrderId { get; set; }\r\n    }\r\n}<\/pre>\n<p>&#8220;IOrderProcessedEvent&#8221; \u0131n\u0131 ise &#8220;OrderService&#8221; i\u00e7erisinde consume edilen message&#8217;\u0131 business requirements do\u011frultusunda handle ettikten sonra, faturaland\u0131r\u0131lma i\u015flemleri i\u00e7in &#8220;BillingService&#8221; e bir event f\u0131rlatabilmek i\u00e7in kullanaca\u011f\u0131z.<\/p>\n<p>&#8220;LightMessagingCore.Boilerplate.OrderService&#8221; projesine gidelim ve &#8220;OrderReceivedConsumer&#8221; class&#8217;\u0131n\u0131 a\u015fa\u011f\u0131daki gibi refactor edelim.<\/p>\n<pre class=\"lang:c# decode:true \">using System;\r\nusing System.Threading.Tasks;\r\nusing LightMessagingCore.Boilerplate.Messaging;\r\nusing MassTransit;\r\n\r\nnamespace LightMessagingCore.Boilerplate.OrderService\r\n{\r\n    public class OrderReceivedConsumer : IConsumer\r\n    {\r\n        public async Task Consume(ConsumeContext context)\r\n        {\r\n            var orderCommand = context.Message;\r\n\r\n            await Console.Out.WriteLineAsync($\"Order code: {orderCommand.OrderCode} Order id: {orderCommand.OrderId} is received.\");\r\n\r\n            \/\/do something..\r\n\r\n            await context.Publish(\r\n                new { CorrelationId = context.Message.CorrelationId, OrderId = orderCommand.OrderId });\r\n        }\r\n    }\r\n}<\/pre>\n<p>Bir \u00f6nceki \u00f6rne\u011fimizden fark\u0131 ise art\u0131k &#8220;IOrderCommand&#8221; yerine &#8220;IOrderReceivedEvent&#8221; lar\u0131n\u0131 consume ediyoruz ve ilgili business i\u015flemleri ger\u00e7ekle\u015ftirdikten sonra, geriye bir &#8220;IOrderProcessedEvent&#8221; \u0131\u00a0publish ediyoruz.<\/p>\n<p>Faturaland\u0131rma i\u015flemleri i\u00e7in ise business&#8217;a &#8220;BillingService&#8221; i dahil edece\u011fimizi s\u00f6ylemi\u015ftik. Solution \u00fczerine &#8220;LightMessagingCore.Boilerplate.BillingService&#8221; isminde yeni bir console application daha ekleyelim. Ekleme i\u015fleminin ard\u0131ndan &#8220;System.Configuration&#8221;, &#8220;LightMessagingCore.Boilerplate.Common&#8221; ve &#8220;LightMessagingCore.Boilerplate.Messaging&#8221; library&#8217;lerini referans olarak ekleyelim. Son olarak da Nuget Package Manager \u00fczerinden &#8220;MassTransit.RabbitMQ&#8221; paketini kural\u0131m. Art\u0131k consumer k\u0131sm\u0131n\u0131 kodlamak i\u00e7in haz\u0131r durumday\u0131z.<\/p>\n<p>&#8220;OrderProcessedConsumer&#8221; isminde bir class ekleyelim ve a\u015fa\u011f\u0131daki gibi kodlayal\u0131m.<\/p>\n<pre class=\"lang:c# decode:true \">using System;\r\nusing System.Threading.Tasks;\r\nusing LightMessagingCore.Boilerplate.Messaging;\r\nusing MassTransit;\r\n\r\nnamespace LightMessagingCore.Boilerplate.BillingService\r\n{\r\n    public class OrderProcessedConsumer : IConsumer\r\n    {\r\n        public async Task Consume(ConsumeContext context)\r\n        {\r\n            var orderCommand = context.Message;\r\n\r\n            await Console.Out.WriteLineAsync($\"Order id: {orderCommand.OrderId} is being billed.\");\r\n        }\r\n    }\r\n}<\/pre>\n<p>Bu k\u0131s\u0131mda daha \u00f6nceki \u00f6rnekte de oldu\u011fu gibi, yeni olu\u015fturmu\u015f oldu\u011fumuz &#8220;IOrderProcessedEvent&#8221; \u0131n\u0131 consume ediyoruz ve ilgili business i\u015flemlerini burada ger\u00e7ekle\u015ftiriyoruz. Olu\u015fturdu\u011fumuz consumer&#8217;\u0131 &#8220;Program.cs&#8221; de a\u015fa\u011f\u0131daki gibi initialize edelim.<\/p>\n<pre class=\"lang:c# decode:true\">using System;\r\nusing System.Configuration;\r\nusing LightMessagingCore.Boilerplate.Common;\r\nusing MassTransit;\r\n\r\nnamespace LightMessagingCore.Boilerplate.BillingService\r\n{\r\n    class Program\r\n    {\r\n        static void Main(string[] args)\r\n        {\r\n            Console.Title = \"BillingService\";\r\n\r\n            var bus = BusConfigurator.Instance\r\n                .ConfigureBus((cfg, host) =&gt;\r\n                {\r\n                    cfg.ReceiveEndpoint(host, ConfigurationManager.AppSettings[\"OrderQueueName\"], e =&gt;\r\n                    {\r\n                        e.Consumer();\r\n                    });\r\n                });\r\n\r\n            bus.StartAsync();\r\n\r\n            Console.WriteLine(\"Listening order processed event..\");\r\n            Console.ReadLine();\r\n        }\r\n    }\r\n}<\/pre>\n<p>Console application \u00fczerinden host olacak olan bus, &#8220;OrderQueueName&#8221; \u00fczerinden gelecek olan receive endpoint&#8217;e g\u00f6re consume i\u015flemlerini &#8220;OrderProcessedConsumer&#8221; \u00fczerinden\u00a0ger\u00e7ekle\u015ftirecektir.<\/p>\n<p>&#8220;App.config&#8221;\u00a0bilgisini de a\u015fa\u011f\u0131daki\u00a0ger\u00e7ekle\u015ftirelim.<\/p>\n<pre class=\"lang:c# decode:true\">  &lt;appSettings&gt;\r\n    &lt;add key=\"RabbitMQUri\" value=\"rabbitmq:\/\/localhost\/\" \/&gt;\r\n    &lt;add key=\"RabbitMQUserName\" value=\"guest\" \/&gt;\r\n    &lt;add key=\"RabbitMQPassword\" value=\"guest\" \/&gt;\r\n    &lt;add key=\"OrderQueueName\" value=\"lightmessagingcore.boilerplate.order\" \/&gt;\r\n  &lt;\/appSettings&gt;<\/pre>\n<p>&#8220;BillingService&#8221; ide &#8220;OrderService&#8221; den sonraki business step&#8217;i olarak sisteme dahil ettik.<\/p>\n<p>\u015eimdi saga state machine ile birlikte workflow k\u0131sm\u0131n\u0131 olu\u015fturmaya ba\u015flayabiliriz.<\/p>\n<p>Solution&#8217;a &#8220;LightMessagingCore.Boilerplate.Saga&#8221; isminde yeni bir console application daha ekleyelim. Ekleme i\u015fleminin ard\u0131ndan &#8220;System.Configuration&#8221;, &#8220;LightMessagingCore.Boilerplate.Common&#8221; ve &#8220;LightMessagingCore.Boilerplate.Messaging&#8221; library&#8217;lerini referans olarak ekleyelim. Referans ekleme i\u015flemini tamamlad\u0131ktan sonra Nuget Package Manager \u00fczerinden &#8220;MassTransit.RabbitMQ&#8221; ve &#8220;MassTransit.Automatonymous&#8221; paketlerini kural\u0131m.<\/p>\n<p>T\u00fcm i\u015flemleri tamamlad\u0131ktan sonra \u015fimdi &#8220;OrderSagaState&#8221; isminde yeni bir class olu\u015ftural\u0131m ve &#8220;SagaStateMachineInstance&#8221; interface&#8217;ini implemente edelim.<\/p>\n<pre class=\"lang:c# decode:true\">using Automatonymous;\r\nusing System;\r\n\r\nnamespace LightMessagingCore.Boilerplate.Saga\r\n{\r\n    public class OrderSagaState : SagaStateMachineInstance\r\n    {\r\n        public Guid CorrelationId { get; set; }\r\n        public State CurrentState { get; set; }\r\n\r\n        public int OrderId { get; set; }\r\n        public string OrderCode { get; set; }\r\n    }\r\n}<\/pre>\n<p>Bu class order&#8217;\u0131n state&#8217;inin tutulaca\u011f\u0131 class d\u0131r. &#8220;CorrelationId&#8221; property&#8217;sine message&#8217;lar\u0131 business flow&#8217;u s\u0131ras\u0131nda correlate edebilmek i\u00e7in ihtiya\u00e7 duymaktad\u0131r Saga State Machine. &#8220;CurrentState&#8221; property&#8217;si ile ise mevcut state tutulmaktad\u0131r ve type&#8217;\u0131n\u0131 &#8220;Automatonymous&#8221; namespace&#8217;i alt\u0131ndan almaktad\u0131r. Geriye kalan &#8220;OrderId&#8221; ve &#8220;OrderCode&#8221; gibi property&#8217;ler ise, state&#8217;ini tutaca\u011f\u0131m\u0131z message&#8217;\u0131n\u00a0property&#8217;leridir.<\/p>\n<p>&#8220;OrderController&#8221; \u0131 refactor ederken art\u0131k message&#8217;lar\u0131 direkt olarak &#8220;SagaQueue&#8221; ya produce edece\u011fimizden ve initialize i\u015flemini saga taraf\u0131ndan ger\u00e7ekle\u015ftirece\u011fimizi s\u00f6ylemi\u015ftik. Saga i\u00e7erisinden ilgili message\u00a0command&#8217;\u0131 i\u00e7in state ve correlation i\u015flemlerini ger\u00e7ekle\u015ftirdikten sonra order business&#8217;\u0131n\u0131n ger\u00e7ekle\u015febilmesi i\u00e7in &#8220;OrderService&#8221; e bir &#8220;IOrderReceivedEvent&#8221; \u0131 publish edece\u011fimizi s\u00f6ylemi\u015ftik. Bunun i\u00e7in yine &#8220;LightMessagingCore.Boilerplate.Saga&#8221; projesi i\u00e7erisine &#8220;Messages&#8221; klas\u00f6r\u00fc ekleyelim ve i\u00e7erisine &#8220;OrderReceivedEvent&#8221; isminde bir event tan\u0131mlayal\u0131m ve &#8220;IOrderReceivedEvent&#8221; interface&#8217;ini implemente edelim.<\/p>\n<pre class=\"lang:c# decode:true\">using System;\r\nusing LightMessagingCore.Boilerplate.Messaging;\r\n\r\nnamespace LightMessagingCore.Boilerplate.Saga.Messages\r\n{\r\n    public class OrderReceivedEvent : IOrderReceivedEvent\r\n    {\r\n        private readonly OrderSagaState _orderSagaState;\r\n\r\n        public OrderReceivedEvent(OrderSagaState orderSagaState)\r\n        {\r\n            _orderSagaState = orderSagaState;\r\n        }\r\n\r\n        public Guid CorrelationId =&gt; _orderSagaState.CorrelationId;\r\n\r\n        public string OrderCode =&gt; _orderSagaState.OrderCode;\r\n\r\n        public int OrderId =&gt; _orderSagaState.OrderId;\r\n    }\r\n}<\/pre>\n<p>Bu event i\u00e7erisinde constructor injection arac\u0131l\u0131\u011f\u0131 ile bir &#8220;orderSagaState&#8221; al\u0131yoruz ve order business&#8217;\u0131n\u0131n ger\u00e7ekle\u015febilmesi i\u00e7in &#8220;OrderService&#8221; e publish edece\u011fimiz property&#8217;leri tan\u0131ml\u0131yoruz.<\/p>\n<p>&#8220;OrderSaga&#8221; y\u0131 olu\u015fturmak i\u00e7in art\u0131k haz\u0131r\u0131z. Burada \u00f6nce order&#8217;\u0131n business gere\u011fi sahip olabilece\u011fi state&#8217;leri tan\u0131mlayaca\u011f\u0131z ve ard\u0131ndan ger\u00e7ekle\u015febilecek event&#8217;lar\u0131 tan\u0131mlay\u0131p, bir workflow olu\u015fturaca\u011f\u0131z. \u00d6ncelikle &#8220;OrderSaga&#8221; isminde bir class ekleyelim ve &#8220;MassTransitStateMachine&#8221; class&#8217;\u0131n\u0131 a\u015fa\u011f\u0131daki gibi implemente edelim.<\/p>\n<pre class=\"lang:c# decode:true\">using Automatonymous;\r\nusing System;\r\nusing LightMessagingCore.Boilerplate.Messaging;\r\nusing LightMessagingCore.Boilerplate.Saga.Messages;\r\n\r\nnamespace LightMessagingCore.Boilerplate.Saga\r\n{\r\n    public class OrderSaga : MassTransitStateMachine\r\n    {\r\n        public State Received { get; set; }\r\n        public State Processed { get; set; }\r\n\r\n        public Event OrderCommand { get; set; }\r\n        public Event OrderProcessed { get; set; }\r\n    }\r\n}<\/pre>\n<p>&#8220;MassTransitStateMachine&#8221; class&#8217;\u0131na implemente ederken type olarak state tutmak i\u00e7in olu\u015fturmu\u015f oldu\u011fumuz &#8220;OrderSagaState&#8221; i ge\u00e7iyoruz.<\/p>\n<p>\u00d6rnek senaryomuz gere\u011fince order, ilk &#8220;OrderService&#8221; e d\u00fc\u015ft\u00fc\u011f\u00fcnde bir &#8220;Received&#8221; stat\u00fcs\u00fcne ve ilgili order business&#8217;\u0131 ger\u00e7ekle\u015ftirildikten sonra da bir &#8220;Processed&#8221; stat\u00fcs\u00fcne sahip olabilir. Bu y\u00fczden &#8220;State&#8221; type&#8217;\u0131na sahip, &#8220;Received&#8221; ve &#8220;Processed&#8221; property&#8217;lerini tan\u0131ml\u0131yoruz. Event olarak ise ilk message &#8220;IOrderCommand&#8221; interface&#8217;i ile &#8220;OrderUI&#8221; taraf\u0131ndan produce edildi\u011finde, \u015fuan olu\u015fturuyor oldu\u011fumuz saga taraf\u0131ndan consume edilecektir. Bu y\u00fczden event olarak bir &#8220;OrderCommand&#8221; bulunmaktad\u0131r. &#8220;OrderService&#8221; taraf\u0131ndan ilgili i\u015flemler ger\u00e7ekle\u015ftirildikten sonra publish edilen bir di\u011fer event olarak da &#8220;IOrderProcessedEvent&#8221; bulunmaktad\u0131r. Bunun i\u00e7in birde &#8220;OrderProcessed&#8221; event&#8217;\u0131 tan\u0131ml\u0131yoruz.<\/p>\n<p>\u015eimdi &#8220;OrderSaga&#8221; n\u0131n constructor&#8217;\u0131 i\u00e7erisinde initial state&#8217;i tan\u0131mlayarak, state machine \u00fczerine event&#8217;lar\u0131 a\u015fa\u011f\u0131daki gibi tan\u0131mlayal\u0131m.<\/p>\n<pre class=\"lang:c# decode:true \">    public class OrderSaga : MassTransitStateMachine\r\n    {\r\n        public State Received { get; set; }\r\n        public State Processed { get; set; }\r\n\r\n        public Event OrderCommand { get; set; }\r\n        public Event OrderProcessed { get; set; }\r\n\r\n        public OrderSaga()\r\n        {\r\n            InstanceState(s =&gt; s.CurrentState);\r\n\r\n            Event(() =&gt; OrderCommand,\r\n                cec =&gt;\r\n                        cec.CorrelateBy(state =&gt; state.OrderCode, context =&gt; context.Message.OrderCode)\r\n                        .SelectId(selector =&gt; Guid.NewGuid()));\r\n\r\n            Event(() =&gt; OrderProcessed, cec =&gt; cec.CorrelateById(selector =&gt;\r\n                        selector.Message.CorrelationId));\r\n        }\r\n}<\/pre>\n<p>&#8220;InstanceState&#8221; method&#8217;u ile &#8220;OrderSagaState&#8221; i, state property&#8217;si \u00fczerinden initialize\u00a0ediyoruz. Ard\u0131ndan &#8220;Event&#8221; method&#8217;u ile i\u00e7erisinde s\u0131ralamas\u0131 \u00f6nemli olarak ilk\u00a0gelecek olan event&#8217;in &#8220;OrderCommand&#8221; olaca\u011f\u0131n\u0131 ve bu message flow&#8217;unu, state&#8217;in &#8220;OrderCode&#8221; property&#8217;si \u00fczerinden(unique olmas\u0131 \u00f6nemli) <strong>correlate<\/strong> edilece\u011fini s\u00f6yl\u00fcyoruz. Buradaki &#8220;state&#8221; state&#8217;ini tutmak i\u00e7in olu\u015fturmu\u015f oldu\u011fumuz yeni &#8220;OrderSagaState&#8221; objesidir ve &#8220;context&#8221; expression&#8217;\u0131 ise o an consume edilen &#8220;IOrderCommand&#8221; event&#8217;\u0131d\u0131r.<\/p>\n<p>&#8220;SelectId&#8221; method&#8217;u ile ise correlation i\u015flemleri i\u00e7in bir id se\u00e7imi yap\u0131yoruz. Bu i\u015flem i\u00e7in e\u011fer kullanabilece\u011finiz unique bir correlation id&#8217;niz yok ise, yeni bir guid olu\u015fturup ger\u00e7ekle\u015ftirmek m\u00fcmk\u00fcnd\u00fcr. Di\u011fer stepler aras\u0131nda workflow taraf\u0131ndan bu correlation id otomatik olarak set edilecektir. Bir di\u011fer event olan &#8220;OrderProcessed&#8221; \u0131 tan\u0131ml\u0131yoruz ve correlation i\u015fleminin &#8220;Message&#8221; \u00fczerindeki &#8220;CorrelationId&#8221; den sa\u011flanaca\u011f\u0131n\u0131 belirtiyoruz.<\/p>\n<p>Gerekli event tan\u0131mlamalar\u0131n\u0131 da tamamlad\u0131ktan sonra, \u015fimdi workflow i\u00e7in activity&#8217;leri\u00a0tan\u0131mlamaya ba\u015flayabiliriz.<\/p>\n<pre class=\"lang:c# decode:true\">using Automatonymous;\r\nusing System;\r\nusing LightMessagingCore.Boilerplate.Messaging;\r\nusing LightMessagingCore.Boilerplate.Saga.Messages;\r\n\r\nnamespace LightMessagingCore.Boilerplate.Saga\r\n{\r\n    public class OrderSaga : MassTransitStateMachine\r\n    {\r\n        public State Received { get; set; }\r\n        public State Processed { get; set; }\r\n\r\n        public Event OrderCommand { get; set; }\r\n        public Event OrderProcessed { get; set; }\r\n\r\n        public OrderSaga()\r\n        {\r\n            InstanceState(s =&gt; s.CurrentState);\r\n\r\n            Event(() =&gt; OrderCommand,\r\n                cec =&gt;\r\n                        cec.CorrelateBy(state =&gt; state.OrderCode, context =&gt; context.Message.OrderCode)\r\n                        .SelectId(selector =&gt; Guid.NewGuid()));\r\n\r\n            Event(() =&gt; OrderProcessed, cec =&gt; cec.CorrelateById(selector =&gt;\r\n                        selector.Message.CorrelationId));\r\n\r\n            Initially(\r\n                When(OrderCommand)\r\n                    .Then(context =&gt;\r\n                    {\r\n                        context.Instance.OrderCode = context.Data.OrderCode;\r\n                        context.Instance.OrderId = context.Data.OrderId;\r\n                    })\r\n                    .ThenAsync(\r\n                        context =&gt; Console.Out.WriteLineAsync($\"{context.Data.OrderId} order id is received..\")\r\n                    )\r\n                    .TransitionTo(Received)\r\n                    .Publish(context =&gt; new OrderReceivedEvent(context.Instance))\r\n                );\r\n\r\n\r\n            During(Received,\r\n                When(OrderProcessed)\r\n                .ThenAsync(\r\n                    context =&gt; Console.Out.WriteLineAsync($\"{context.Data.OrderId} order id is processed..\"))\r\n                .Finalize()\r\n                );\r\n\r\n            SetCompletedWhenFinalized();\r\n        }\r\n    }\r\n}<\/pre>\n<p>&#8220;Initially&#8221; method&#8217;u ile ilk ba\u015fta ne i\u015flemi ger\u00e7ekle\u015ftirece\u011fimizi tan\u0131mlayaca\u011f\u0131z. Bunun i\u00e7in fluent bir \u015fekilde &#8220;When&#8221; ve &#8220;Then&#8221; method&#8217;lar\u0131n\u0131 kullan\u0131yoruz. Yani bir &#8220;OrderCommand&#8221; geldi\u011finde sonra &#8220;context.Instance&#8221; a, consume edilen data \u00fczerindeki de\u011feri\u00a0almas\u0131 gerekti\u011fini s\u00f6yl\u00fcyoruz. Bu noktada &#8220;context.Instance&#8221; state&#8217;i tuttu\u011fumuz &#8220;OrderSagaState&#8221; objesi oluyor. &#8220;ThenAsync&#8221; method&#8217;u ile yapmak istedi\u011finiz farkl\u0131 business i\u015flemleri var ise async bir \u015fekilde burada ger\u00e7ekle\u015ftirebilmek m\u00fcmk\u00fcnd\u00fcr.<\/p>\n<p>Bunlara ek olarak fluently bir \u015fekilde kullanabilece\u011finiz, baz\u0131 harika method&#8217;lar da mevcuttur. \u00d6rnek vermek gerekirse\u00a0<strong>conditional<\/strong> i\u015flemler ger\u00e7ekle\u015ftirebilmek i\u00e7in &#8220;If&#8221; method&#8217;u, saga k\u0131sm\u0131na\u00a0<strong>compensation chain<\/strong> olu\u015fturabilmek i\u00e7in &#8220;Catch&#8221; method&#8217;u gibi\u00a0baz\u0131 method&#8217;lar mevcuttur. Bunlara ek olarak state ba\u011fl\u0131ml\u0131 <strong>scheduled<\/strong> i\u015flemler de ger\u00e7ekle\u015ftirebilmek i\u00e7in &#8220;Schedule&#8221; method&#8217;uda vard\u0131r.<\/p>\n<pre class=\"lang:c# decode:true\">.TransitionTo(Received)\r\n.Publish(context =&gt; new OrderReceivedEvent(context.Instance))<\/pre>\n<p>&#8220;Initially&#8221; method&#8217;unda yukar\u0131daki son iki sat\u0131rda da, &#8220;TransitionTo&#8221; method&#8217;u ile state&#8217;in art\u0131k &#8220;Received&#8221; oldu\u011funu ve ard\u0131ndan &#8220;Publish&#8221; method&#8217;u ile i\u015flenmek \u00fczere order command&#8217;\u0131 bir &#8220;OrderReceivedEvent&#8221; olarak queue&#8217;ya f\u0131rlat\u0131yoruz.<\/p>\n<p>Art\u0131k order saga&#8217;n\u0131n ilk ba\u015fta ne yapmas\u0131 gerekti\u011fi haz\u0131r durumdad\u0131r. Art\u0131k workflow \u00fczerine di\u011fer activity&#8217;leri de tan\u0131mlamaya ba\u015flayabiliriz. &#8220;During&#8221; method&#8217;u ile objenin belirtilen state&#8217;e sahip oldu\u011funda ger\u00e7ekle\u015fmesini istedi\u011fimiz i\u015flemleri tan\u0131ml\u0131yoruz. Biz ise \u00f6rnek senaryomuz gere\u011fi obje &#8220;Received&#8221; state&#8217;ine sahipse ve &#8220;BillingService&#8221; i\u00e7in &#8220;OrderProcessed&#8221; event&#8217;\u0131 yakaland\u0131ysa, console ekran\u0131na ilgili i\u015flem bilgisini yazmas\u0131n\u0131 s\u00f6yl\u00fcyoruz. Yani bu k\u0131s\u0131mda &#8220;BillingService&#8221; devreye girip \u00e7al\u0131\u015fmaya ba\u015flamas\u0131d\u0131r. ve &#8220;Finalize&#8221; method&#8217;u ile ilgili state&#8217;i tamaml\u0131yoruz.<\/p>\n<pre class=\"lang:c# decode:true\">SetCompletedWhenFinalized();<\/pre>\n<p>T\u00fcm bu i\u015flemlerin sonunda art\u0131k yukar\u0131daki sat\u0131r ile, ilgili saga state instance&#8217;\u0131n\u0131n sonland\u0131\u011f\u0131n\u0131 s\u00f6yl\u00fcyoruz ve MassTransit taraf\u0131ndan art\u0131k bu instance bilgileri repository \u00fczerinden otomatik olarak silinecektir. (bu k\u0131sma birazdan de\u011finece\u011fim)<\/p>\n<p>Art\u0131k bir order workflow&#8217;una ve state machine&#8217;ine sahibiz. \u00d6rnek senaryomuz gere\u011fi her bir servis ilgili i\u015flemini process ederken, saga console&#8217;u \u00fczerinden gelen bilgilerini g\u00f6rebiliyor olaca\u011f\u0131z.<\/p>\n<p>\u015eimdi &#8220;Program.cs&#8221; e gelelim ve burada saga host&#8217;unu a\u015fa\u011f\u0131daki gibi initialize edelim.<\/p>\n<pre class=\"lang:c# decode:true \">using System;\r\nusing System.Configuration;\r\nusing Automatonymous;\r\nusing LightMessagingCore.Boilerplate.Common;\r\nusing MassTransit.Saga;\r\n\r\nnamespace LightMessagingCore.Boilerplate.Saga\r\n{\r\n    class Program\r\n    {\r\n        static void Main(string[] args)\r\n        {\r\n            Console.Title = \"Saga\";\r\n            var orderSaga = new OrderSaga();\r\n            var repo = new InMemorySagaRepository();\r\n\r\n            var bus = BusConfigurator.Instance\r\n                .ConfigureBus((cfg, host) =&gt;\r\n                {\r\n                    cfg.ReceiveEndpoint(host, ConfigurationManager.AppSettings[\"SagaQueueName\"], e =&gt;\r\n                    {\r\n                        e.StateMachineSaga(orderSaga, repo);\r\n                    });\r\n                });\r\n\r\n            bus.StartAsync();\r\n\r\n            Console.WriteLine(\"Order saga started..\");\r\n            Console.ReadLine();\r\n        }\r\n    }\r\n}<\/pre>\n<p>Burada \u00f6ncelikle az \u00f6nce yukar\u0131da bahsetmi\u015f oldu\u011fum repository kavram\u0131ndan bahsetmek istiyorum. MassTransit ilgili state&#8217;leri repository&#8217;de tutmaktad\u0131r. Ben \u00f6rnek gere\u011fi &#8220;InMemorySagaRepository&#8221; ile state i\u015flemlerimi isminden de anla\u015f\u0131labilece\u011fi gibi in memory olarak ger\u00e7ekle\u015ftirdim. Bu i\u015flemleri database \u00fczerinde entityframework repository&#8217;si ile de ger\u00e7ekle\u015ftirebilmek m\u00fcmk\u00fcnd\u00fcr. Bu i\u015flemlerin ard\u0131ndan di\u011fer host&#8217;lardan tek fark\u0131 ise, &#8220;Consumer&#8221; method&#8217;u yerine &#8220;StateMachineSaga&#8221; method&#8217;unu kullanmam\u0131zd\u0131r.<\/p>\n<p>Test i\u015flemlerinden \u00f6nce&#8221;App.config&#8221; bilgilerini a\u015fa\u011f\u0131daki gibi g\u00fcncelleyelim.<\/p>\n<pre class=\"lang:c# decode:true \">  &lt;appSettings&gt;\r\n    &lt;add key=\"RabbitMQUri\" value=\"rabbitmq:\/\/localhost\/\" \/&gt;\r\n    &lt;add key=\"RabbitMQUserName\" value=\"guest\" \/&gt;\r\n    &lt;add key=\"RabbitMQPassword\" value=\"guest\" \/&gt;\r\n    &lt;add key=\"SagaQueueName\" value=\"lightmessagingcore.boilerplate.saga\" \/&gt;\r\n  &lt;\/appSettings&gt;<\/pre>\n<p>Dilerseniz \u015fimdi solution&#8217;\u0131n multiple startup project k\u0131sm\u0131na &#8220;LightMessagingCore.Boilerplate.BillingService&#8221; ve &#8220;LightMessagingCore.Boilerplate.Saga&#8221; y\u0131 da dahil edelim ve start&#8217;a basal\u0131m.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2017\/01\/order_saga_ui_.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-1029 lazyload\" data-src=\"\/wp-content\/uploads\/2017\/01\/order_saga_ui_.jpg\" alt=\"order_saga_ui_\" width=\"363\" height=\"571\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2017\/01\/order_saga_ui_.jpg 363w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2017\/01\/order_saga_ui_-191x300.jpg 191w\" data-sizes=\"(max-width: 363px) 100vw, 363px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 363px; --smush-placeholder-aspect-ratio: 363\/571;\" \/><\/a><\/p>\n<p>A\u00e7\u0131lan &#8220;OrderUI&#8221; \u00fczerinden yukar\u0131daki bilgileri girelim ve &#8220;Create&#8221; butonuna basal\u0131m.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2017\/01\/saga-state.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-1027 lazyload\" data-src=\"\/wp-content\/uploads\/2017\/01\/saga-state.jpg\" alt=\"saga-state\" width=\"1026\" height=\"683\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2017\/01\/saga-state.jpg 1026w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2017\/01\/saga-state-300x200.jpg 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2017\/01\/saga-state-1024x682.jpg 1024w\" data-sizes=\"(max-width: 1026px) 100vw, 1026px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1026px; --smush-placeholder-aspect-ratio: 1026\/683;\" \/><\/a><\/p>\n<p>Dikkat ederseniz &#8220;IOrderCommand&#8221; message&#8217;\u0131 ilk olarak &#8220;Saga&#8221; ya geldi ve &#8220;IOrderReceivedEvent&#8221; \u0131 olarak publish edildi. Ard\u0131ndan &#8220;OrderService&#8221; publish edilen event&#8217;\u0131 handle ettikten sonra &#8220;IOrderProcessedEvent&#8221; \u0131n\u0131 publish etti. &#8220;BillingService&#8221; de ilgili event&#8217;\u0131 consume ederek, handle i\u015flemini ger\u00e7ekle\u015ftirdi.<\/p>\n<p>Her bir step, state&#8217;ler do\u011frultusunda g\u00f6rd\u00fc\u011f\u00fcm\u00fcz gibi saga workflow&#8217;u \u00fczerinden ge\u00e7erek ger\u00e7ekle\u015fti. Sizlerde ihtiya\u00e7lar\u0131n\u0131z do\u011frultusunda workflow \u00fczerinde activity&#8217;leri\u00a0tan\u0131mlarken, ger\u00e7ekle\u015ftirmek istedi\u011finiz eylemleri de tan\u0131mlayabilirsiniz.<\/p>\n<p>Bir s\u00fcrelik aradan sonra umar\u0131m faydal\u0131 bir makale i\u00e7eri\u011fi olmu\u015ftur. \u00d6rnek uygulaman\u0131n t\u00fcm\u00fcne a\u015fa\u011f\u0131dan eri\u015febilirsiniz.<\/p>\n<p><a href=\"https:\/\/github.com\/GokGokalp\/lightmessagingcore-boilerplate-with-saga\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/GokGokalp\/lightmessagingcore-boilerplate-with-saga<\/a><\/p>\n<p>Referanslar:<\/p>\n<blockquote>\n<pre class=\"\">http:\/\/automatonymous.readthedocs.io\/en\/latest\/overview\/saga.html?highlight=StateMachineSaga\r\nhttp:\/\/vasters.com\/archive\/Sagas.html\r\n<blockquote class=\"wp-embedded-content\" data-secret=\"HLmo4A861m\"><a href=\"https:\/\/arnon.me\/2013\/01\/saga-pattern-architecture-design\/\">The Saga pattern and that architecture vs. design thing<\/a><\/blockquote><iframe class=\"wp-embedded-content lazyload\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\" title=\"&#8220;The Saga pattern and that architecture vs. design thing&#8221; &#8212; Cirrus Minor\" data-src=\"https:\/\/arnon.me\/2013\/01\/saga-pattern-architecture-design\/embed\/#?secret=INsReI1cXe#?secret=HLmo4A861m\" data-secret=\"HLmo4A861m\" width=\"500\" height=\"282\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\"><\/iframe>\r\nhttps:\/\/medium.com\/@roman01la\/confusion-about-saga-pattern-bbaac56e622#.npnnb9una<\/pre>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Merhaba arkada\u015flar. Bir s\u00fcredir gerek yo\u011fun i\u015f temposu gerekse de sosyal hayat\u0131mdaki baz\u0131\u00a0yo\u011funluklardan dolay\u0131, yeni bir makale yazmaya f\u0131rsat bulamam\u0131\u015ft\u0131m. Sizlerde fark ederseniz bir s\u00fcredir makale konular\u0131m\u0131 microservice ve messaging yap\u0131lar\u0131 \u00fczerine yo\u011funla\u015ft\u0131rd\u0131m. Bu makale i\u00e7eri\u011finde ise geli\u015ftiriyor oldu\u011fumuz microservice ve messaging yap\u0131lar\u0131nda, long-running business&#8230;<\/p>\n<div class=\"more-link-wrapper\"><a class=\"more-link\" href=\"https:\/\/gokhan-gokalp.com\/tr\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/\">Devam\u0131n\u0131 okuyun<span class=\"screen-reader-text\">MassTransit Saga State Machine ile Model Workflow&#8217;u Olu\u015fturmak<\/span><\/a><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,68,285,152],"tags":[356,353,290,287,354,355,333,286,153,349,352,357,350,494,351],"class_list":["post-1003","post","type-post","status-publish","format-standard","hentry","category-net","category-architectural","category-messaging","category-rabbitmq","tag-c-distributed-transaction","tag-c-masstransit","tag-c-messaging","tag-c-rabbitmq","tag-c-saga","tag-distributed-transaction","tag-masstransit","tag-messaging","tag-rabbitmq","tag-saga","tag-saga-net","tag-saga-kullanimi","tag-saga-nedir","tag-saga-state-machine","tag-saga-workflow","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"tr","enabled_languages":["en","tr"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"tr":{"title":false,"content":false,"excerpt":false}}},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>MassTransit Saga State Machine ile Model Workflow&#039;u Olu\u015fturmak - G\u00f6khan G\u00f6kalp<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/\" \/>\n<meta property=\"og:locale\" content=\"tr_TR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"MassTransit Saga State Machine ile Model Workflow&#039;u Olu\u015fturmak - G\u00f6khan G\u00f6kalp\" \/>\n<meta property=\"og:url\" content=\"https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/\" \/>\n<meta property=\"og:site_name\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n<meta property=\"article:published_time\" content=\"2017-01-07T22:20:13+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-09-26T11:03:34+00:00\" \/>\n<meta name=\"author\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Yazan:\" \/>\n\t<meta name=\"twitter:data1\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tahmini okuma s\u00fcresi\" \/>\n\t<meta name=\"twitter:data2\" content=\"16 dakika\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\\\/\"},\"author\":{\"name\":\"G\u00f6khan G\u00f6kalp\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"headline\":\"MassTransit Saga State Machine ile Model Workflow&#8217;u Olu\u015fturmak\",\"datePublished\":\"2017-01-07T22:20:13+00:00\",\"dateModified\":\"2018-09-26T11:03:34+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\\\/\"},\"wordCount\":2556,\"commentCount\":9,\"publisher\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"keywords\":[\"c# distributed transaction\",\"c# masstransit\",\"c# messaging\",\"C# RabbitMQ\",\"c# saga\",\"distributed transaction\",\"masstransit\",\"Messaging\",\"RabbitMQ\",\"Saga\",\"Saga .net\",\"saga kullan\u0131m\u0131\",\"Saga Nedir\",\"saga state machine\",\"Saga workflow\"],\"articleSection\":[\".NET\",\"Architectural\",\"Messaging\",\"RabbitMQ\"],\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/gokhan-gokalp.com\\\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\\\/\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\\\/\",\"name\":\"MassTransit Saga State Machine ile Model Workflow'u Olu\u015fturmak - G\u00f6khan G\u00f6kalp\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#website\"},\"datePublished\":\"2017-01-07T22:20:13+00:00\",\"dateModified\":\"2018-09-26T11:03:34+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\\\/#breadcrumb\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/gokhan-gokalp.com\\\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/gokhan-gokalp.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"MassTransit Saga State Machine ile Model Workflow&#8217;u Olu\u015fturmak\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#website\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/\",\"name\":\"G\u00f6khan G\u00f6kalp\",\"description\":\"C# &amp; Python lover\",\"publisher\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/gokhan-gokalp.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"tr\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\",\"name\":\"G\u00f6khan G\u00f6kalp\",\"pronouns\":\"he\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659\",\"contentUrl\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659\",\"caption\":\"G\u00f6khan G\u00f6kalp\"},\"logo\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659\"},\"sameAs\":[\"https:\\\/\\\/gokhan-gokalp.com\"],\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/tr\\\/author\\\/gok-gokalp\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"MassTransit Saga State Machine ile Model Workflow'u Olu\u015fturmak - G\u00f6khan G\u00f6kalp","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/","og_locale":"tr_TR","og_type":"article","og_title":"MassTransit Saga State Machine ile Model Workflow'u Olu\u015fturmak - G\u00f6khan G\u00f6kalp","og_url":"https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/","og_site_name":"G\u00f6khan G\u00f6kalp","article_published_time":"2017-01-07T22:20:13+00:00","article_modified_time":"2018-09-26T11:03:34+00:00","author":"G\u00f6khan G\u00f6kalp","twitter_card":"summary_large_image","twitter_misc":{"Yazan:":"G\u00f6khan G\u00f6kalp","Tahmini okuma s\u00fcresi":"16 dakika"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/#article","isPartOf":{"@id":"https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/"},"author":{"name":"G\u00f6khan G\u00f6kalp","@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"headline":"MassTransit Saga State Machine ile Model Workflow&#8217;u Olu\u015fturmak","datePublished":"2017-01-07T22:20:13+00:00","dateModified":"2018-09-26T11:03:34+00:00","mainEntityOfPage":{"@id":"https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/"},"wordCount":2556,"commentCount":9,"publisher":{"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"keywords":["c# distributed transaction","c# masstransit","c# messaging","C# RabbitMQ","c# saga","distributed transaction","masstransit","Messaging","RabbitMQ","Saga","Saga .net","saga kullan\u0131m\u0131","Saga Nedir","saga state machine","Saga workflow"],"articleSection":[".NET","Architectural","Messaging","RabbitMQ"],"inLanguage":"tr","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/","url":"https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/","name":"MassTransit Saga State Machine ile Model Workflow'u Olu\u015fturmak - G\u00f6khan G\u00f6kalp","isPartOf":{"@id":"https:\/\/gokhan-gokalp.com\/#website"},"datePublished":"2017-01-07T22:20:13+00:00","dateModified":"2018-09-26T11:03:34+00:00","breadcrumb":{"@id":"https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/#breadcrumb"},"inLanguage":"tr","potentialAction":[{"@type":"ReadAction","target":["https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/gokhan-gokalp.com\/masstransit-saga-state-machine-ile-model-workflow-u-olusturmak\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/gokhan-gokalp.com\/"},{"@type":"ListItem","position":2,"name":"MassTransit Saga State Machine ile Model Workflow&#8217;u Olu\u015fturmak"}]},{"@type":"WebSite","@id":"https:\/\/gokhan-gokalp.com\/#website","url":"https:\/\/gokhan-gokalp.com\/","name":"G\u00f6khan G\u00f6kalp","description":"C# &amp; Python lover","publisher":{"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/gokhan-gokalp.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"tr"},{"@type":["Person","Organization"],"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe","name":"G\u00f6khan G\u00f6kalp","pronouns":"he","image":{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659","url":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659","contentUrl":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659","caption":"G\u00f6khan G\u00f6kalp"},"logo":{"@id":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659"},"sameAs":["https:\/\/gokhan-gokalp.com"],"url":"https:\/\/gokhan-gokalp.com\/tr\/author\/gok-gokalp\/"}]}},"_links":{"self":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/1003","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/comments?post=1003"}],"version-history":[{"count":22,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/1003\/revisions"}],"predecessor-version":[{"id":2306,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/1003\/revisions\/2306"}],"wp:attachment":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/media?parent=1003"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/categories?post=1003"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/tags?post=1003"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}