﻿
{"id":22629,"date":"2021-08-31T00:30:35","date_gmt":"2021-08-31T00:30:35","guid":{"rendered":"https:\/\/www.gencayyildiz.com\/blog\/?p=22629"},"modified":"2021-08-31T00:30:35","modified_gmt":"2021-08-31T00:30:35","slug":"microservice-saga-commands-orchestration-implemantasyonu-ile-transaction-yonetimi","status":"publish","type":"post","link":"https:\/\/www.gencayyildiz.com\/blog\/microservice-saga-commands-orchestration-implemantasyonu-ile-transaction-yonetimi\/","title":{"rendered":"Microservice &#8211; Saga &#8211; Commands\/Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi"},"content":{"rendered":"<div id=\"fb-root\"><\/div>\n<p>Merhaba,<\/p>\n<p>\u00d6nceki i\u00e7eriklerimizden <a href=\"https:\/\/www.gencayyildiz.com\/blog\/microservice-saga-events-choreography-implemantasyonu-ile-transaction-yonetimi\/\" rel=\"noopener\" target=\"_blank\">Microservice &#8211; Saga &#8211; Events\/Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi<\/a> ba\u015fl\u0131kl\u0131 yaz\u0131m\u0131zda Saga pattern&#8217;\u0131n\u0131n iki implemantasyonundan biri olan choreography y\u00f6ntemini t\u00fcm detaylar\u0131yla ve pratik olarak incelemi\u015ftik. Bu i\u00e7eri\u011fimizde ise yine Saga pattern&#8217;\u0131n\u0131n ikinci implemantasyon y\u00f6ntemi olan Orchestration&#8217;\u0131 pratik olarak tam teferruatl\u0131 inceliyor olaca\u011f\u0131z. Yaz\u0131m\u0131z uzun ve me\u015fakkatli olaca\u011f\u0131ndan dolay\u0131 vakit kaybetmeksizin buyrun hemen ba\u015flayal\u0131m&#8230;<\/p>\n<p><em style=\"font-size:12px;\"><strong>Events\/Choreography y\u00f6ntemi varken Commands\/Orchestration implemantasyonunun amac\u0131 nedir?<\/strong><\/em><br \/>\nHer\u015feyden \u00f6nce bu soruyu sorarak ba\u015flamakta fayda g\u00f6rmekteyim. Choreography implementasyonu neyimize yetmedi de Orchestration y\u00f6nteminden medet umar hale geldik konu\u015fmakta fayda var \ud83d\ude42<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi.png\" alt=\"Microservice - Saga - Commands-Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"160\" height=\"254\" class=\"alignleft size-full wp-image-22637\" \/><\/a><br \/>\nYandaki \u015femaya g\u00f6z atarsak e\u011fer Choreography implementasyonunun stratejik yap\u0131lanmas\u0131n\u0131 g\u00f6rmekteyiz. Dikkat ederseniz Choreography&#8217;de t\u00fcm servisler Event Broker ile ileti\u015fim kurmak ve hangi servisten hangi event&#8217;in gelece\u011fiyle ilgilenmek mecburiyetindedir. Haliyle Choreography ad\u0131 \u00fczerinde b\u00fct\u00fcnsel bir koreografiye ayak uydurulabilir bir in\u015fa gerektirmekte ve bazen ayn\u0131 i\u015fi birden fazla farkl\u0131 servisten gelebilecek \u015fekilde koordine ederek farkl\u0131 consumer&#8217;lar da yap\u0131lmas\u0131n\u0131 gerektirebilmektedir.<\/p>\n<p>Bunu daha da somutla\u015ft\u0131rabilmek i\u00e7in basit bir e-ticaret senaryosundan \u00f6rneklendirme yapabiliriz. Misal; bir e-ticaret sisteminde m\u00fc\u015fteriler taraf\u0131ndan yap\u0131lan sipari\u015fler s\u0131ras\u0131yla <em style=\"color:purple;font-size:11px;\">Order.API<\/em>, <em style=\"color:purple;font-size:11px;\">Stock.API<\/em> ve <em style=\"color:purple;font-size:11px;\">Payment.API<\/em> servisleri \u00fczerinden y\u00fcr\u00fct\u00fcld\u00fc\u011f\u00fcn\u00fc varsayal\u0131m. Bu sistemde <em style=\"color:purple;font-size:11px;\">Stock.API<\/em> ya da <em style=\"color:purple;font-size:11px;\">Payment.API<\/em> servislerinde olu\u015fabilecek herhangi bir aksi duruma istinaden <em style=\"color:purple;font-size:11px;\">Order.API<\/em> gerekli m\u00fcdahalede bulunabilmek ve sipari\u015fi iptal edebilmek i\u00e7in her iki servisten gelecek ba\u015far\u0131s\u0131zl\u0131k durumunu ifade eden event&#8217;leri consume etmek mecburiyetindedir. Haliyle hangi servisten gelirse gelsin bir sipari\u015fin iptal edilmesi ayn\u0131 i\u015fi gerektirece\u011finden dolay\u0131 Choreography implementasyonu burada bu operasyonun \u00e7oklat\u0131lmas\u0131n\u0131 mecburi k\u0131lmakta ve ayn\u0131 i\u015fi hem <em style=\"color:purple;font-size:11px;\">Stock.API<\/em> i\u00e7in hem de <em style=\"color:purple;font-size:11px;\">Payment.API<\/em> i\u00e7in consume edecek \u015fekilde kodlan\u0131labilir olmay\u0131 gerektirmektedir.<\/p>\n<p>Bu y\u00fczden Choreography implementasyonu  hem l\u00fczumsuz kod israf\u0131, hem de y\u00f6netilebilirlik a\u00e7\u0131s\u0131ndan 2 ile 4 servisin s\u00f6z konusu oldu\u011fu \u00e7al\u0131\u015fmalarda ideal olarak s\u0131n\u0131rland\u0131r\u0131lmaktad\u0131r. Daha fazla servisin s\u00f6z konusu oldu\u011fu durumlarda ise art\u0131k servisler aras\u0131 y\u00f6netilebilirlik maliyetini d\u00fc\u015f\u00fcrmek i\u00e7in merkezi bir y\u00f6netime olan ihtiya\u00e7 olduk\u00e7a artmaktad\u0131r. \u0130\u015fte art\u0131k bu s\u0131n\u0131rda yeni implementasyon kabul\u00fc olarak Orchestration devreye girmektedir.<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-1.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-1.png\" alt=\"Microservice - Saga - Commands-Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"149\" height=\"258\" class=\"alignright size-full wp-image-22645\" \/><\/a><br \/>\nOrchestration implementasyonu, servisler aras\u0131ndaki distributed transaction&#8217;\u0131 sa\u011flayabilmek i\u00e7in merkezi bir orchestrator \u00fczerinden t\u00fcm trafi\u011fi y\u00f6netmekte ve b\u00f6ylece servisler a\u00e7\u0131s\u0131ndan eylemlerin kayna\u011f\u0131yla ilgilenmeksizin hangi event&#8217;ler de hangi aksiyonlar\u0131n al\u0131nmas\u0131 gerekti\u011finden ba\u015fka ekstra bir bilginin bilinmesine gerek duyulmaks\u0131z\u0131n \u00e7al\u0131\u015f\u0131lmas\u0131n\u0131 sa\u011flamaktad\u0131r.<\/p>\n<p>Yukar\u0131daki \u00f6rnek e-ticaret senaryosundan devam edersek e\u011fer bu sefer <em style=\"color:purple;font-size:11px;\">Order.API<\/em>, <em style=\"color:purple;font-size:11px;\">Stock.API<\/em> ve <em style=\"color:purple;font-size:11px;\">Payment.API<\/em> servislerinin yan\u0131nda bu servisler aras\u0131nda ileti\u015fimi yani gerekli organizasyonu sa\u011flayacak olan bir <em style=\"color:purple;font-size:11px;\">Orchestrator<\/em> servisi olacakt\u0131r. Art\u0131k <em style=\"color:purple;font-size:11px;\">Stock.API<\/em> veya <em style=\"color:purple;font-size:11px;\">Payment.API<\/em> servislerinden herhangi birinde olabilecek olas\u0131 bir aksilik durumunda gerekli event&#8217;lerle bu <em style=\"color:purple;font-size:11px;\">Orchestrator<\/em> servisi tetiklenecek ve yine bu servis \u00fczerinden gerekli sipari\u015f iptal event&#8217;i <em style=\"color:purple;font-size:11px;\">Order.API<\/em>&#8216;ya iletilecektir. Haliyle burada <em style=\"color:purple;font-size:11px;\">Order.API<\/em> art\u0131k <em style=\"color:purple;font-size:11px;\">Orchesrator<\/em> kanad\u0131n\u0131 dinleyece\u011finden dolay\u0131 iptal durumunun hangi servisten geldi\u011fiyle ilgilenmeksizin sadece ilgili event&#8217;in consume edilmesine odaklanacak ve b\u00f6ylece servisler aras\u0131 ileti\u015fimde y\u00f6netilebilirlik hat safhaya \u00e7\u0131kacakt\u0131r.<\/p>\n<p><em style=\"font-size:12px;\"><strong>Orchestration implementasyonu nas\u0131l ger\u00e7ekle\u015ftirilmektedir?<\/strong><\/em><br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-2.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-2.png\" alt=\"Microservice - Saga - Commands-Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"1465\" height=\"608\" class=\"aligncenter size-full wp-image-22654\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-2.png 1465w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-2-300x125.png 300w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-2-1024x425.png 1024w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-2-768x319.png 768w\" sizes=\"auto, (max-width: 1465px) 100vw, 1465px\" \/><\/a><br \/>\nOrchestration, yukar\u0131daki paragrafta da de\u011finildi\u011fi gibi merkezi bir orchestrator arac\u0131l\u0131\u011f\u0131yla distributed transaction&#8217;\u0131n y\u00f6netilmesini sa\u011flayan bir stratejiye dayanmaktad\u0131r. Haliyle bu strateji kabul edildikten sonra geriye yap\u0131lmas\u0131 gereken bu orchestrator sorumlulu\u011funu \u00fcstlenecek olan servisin bir \u015fekilde geli\u015ftirilmesi kalmaktad\u0131r. Misal olarak bu servis yukar\u0131daki \u015femada oldu\u011fu gibi salt bir \u015fekilde message broker \u00fczerinden merkezi haberle\u015fme birimi olabilecekken, bir yandan da <a href=\"https:\/\/www.gencayyildiz.com\/blog\/finite-state-machine-nedir\/\" rel=\"noopener\" target=\"_blank\">State Machine<\/a> olarak tasarlanm\u0131\u015f ayr\u0131 bir worker servis olabilir. Ki bizler Saga pattern operasyonlar\u0131nda orchestrator olarak state machine olu\u015fturarak operasyon s\u00fcrecindeki her bir ad\u0131m\u0131 ayr\u0131 ayr\u0131 kontrol edecek ve o anki duruma g\u00f6re event y\u00f6netimini sa\u011flayarak distributed transaction y\u00f6netimini state yap\u0131lanmas\u0131 \u00fczerinden y\u00f6netmeyi tercih edece\u011fiz. Neden mi? \u00c7\u00fcnk\u00fc bu y\u00f6ntem best practices olarak kabul g\u00f6rmektedir. Ve bu makalede de bu y\u00f6ntem \u00fczerinden pratik bir senaryo icra edilecektir.<\/p>\n<p><em style=\"font-size:12px;\"><strong>Peki neden ve nas\u0131l state machine kullanaca\u011f\u0131z?<\/strong><\/em><br \/>\nYine bu soruya yukar\u0131daki \u00f6rnek e-ticaret senaryosu \u00fczerinden cevap vermeye \u00e7al\u0131\u015f\u0131rsak e\u011fer kullan\u0131c\u0131 taraf\u0131ndan yap\u0131lan sipari\u015f iste\u011fi <em style=\"color:purple;font-size:11px;\">Order.API<\/em> taraf\u0131ndan kar\u015f\u0131land\u0131ktan sonra state machine <em style=\"color:green;font-size:11px;\">OrderCreated<\/em> durumunu tutacakt\u0131r. Ard\u0131ndan sipari\u015fteki \u00fcr\u00fcnlerin stok bilgileri <em style=\"color:purple;font-size:11px;\">Stock.API<\/em> taraf\u0131ndan g\u00fcncellendikten sonra state machine&#8217;de ilgili sipari\u015fe dair durum de\u011feri <em style=\"color:green;font-size:11px;\">StockReserved<\/em> olarak g\u00fcncellenecektir. Benzer mant\u0131kla stok bilgilerinde herhangi bir tutars\u0131zl\u0131k s\u00f6z konusu olursa e\u011fer <em style=\"color:green;font-size:11px;\">StockNotReserved<\/em> durumu tutulacak yok e\u011fer \u00f6deme s\u00fcrecinde bir hata s\u00f6z konusuysa <em style=\"color:green;font-size:11px;\">PaymentFailed<\/em> durumu tutulacakt\u0131r. \u00d6deme ba\u015far\u0131l\u0131ysa e\u011fer ilgili sipari\u015fe dair state <em style=\"color:green;font-size:11px;\">Finalize<\/em> yap\u0131larak nihayetinde silinecektir. Haliyle bir sipari\u015f s\u00fcrecindeki t\u00fcm ad\u0131mlar state machine taraf\u0131ndan tutulaca\u011f\u0131 i\u00e7in ilgili sipari\u015fin hangi durumlarda aksad\u0131\u011f\u0131n\u0131(ya da aksi durum ya\u015fad\u0131\u011f\u0131n\u0131) veya tamamlan\u0131p tamamlanmad\u0131\u011f\u0131n\u0131 rahatl\u0131kla g\u00f6rebilecek ve y\u00f6netebilece\u011fiz. <\/p>\n<p>State machine&#8217;i nas\u0131l kullanaca\u011f\u0131z sorusuna gelirsek e\u011fer, burada State Design Pattern ile kendi state&#8217;lerimizi y\u00f6netebilecek bir tasar\u0131m ortaya koyabilece\u011fimiz gibi daha efektif ve h\u0131zl\u0131 \u00e7\u00f6z\u00fcm getirebilmek i\u00e7in bizlere haz\u0131r bir state machine yap\u0131lanmas\u0131 sunan open source <a href=\"https:\/\/automatonymous.readthedocs.io\/en\/latest\/configuration\/quickstart.html\" rel=\"noopener\" target=\"_blank\">Automatonymous<\/a> k\u00fct\u00fcphanesinden de istifade edebiliriz. Bu k\u00fct\u00fcphaneyi salt bir \u015fekilde belirtilen adresindeki y\u00f6nergeler do\u011frultusunda uygulayabilir ve kullanabiliriz. Lakin bizler genellikle message broker \u00e7al\u0131\u015fmalar\u0131nda Enterprise Service Bus(ESB) kullanmay\u0131 tercih etti\u011fimiz ve bundan dolay\u0131 genellikle <a href=\"https:\/\/www.gencayyildiz.com\/blog\/net-core-masstransit-kullanarak-rabbitmq-ile-messaging\/\" rel=\"noopener\" target=\"_blank\">MassTransit<\/a> k\u00fct\u00fcphanesini kulland\u0131\u011f\u0131m\u0131zdan dolay\u0131 MassTransit geli\u015ftiricileri state machine yap\u0131lanmas\u0131n\u0131 da destekleyebilmek i\u00e7in ayr\u0131ca bir geli\u015ftirme yapmak yerine direkt olarak Automatonymous k\u00fct\u00fcphanesini b\u00fcnyelerine katm\u0131\u015f olduklar\u0131ndan bizler bu Automatonymous k\u00fct\u00fcphanesini MassTransit k\u00fct\u00fcphanesi \u00fczerinden kullan\u0131yor olaca\u011f\u0131z.<\/p>\n<p>Haliyle art\u0131k \u00e7ok uzatmadan \u00f6rnek senaryomuzun s\u0131n\u0131rlar\u0131n\u0131 \u00e7izelim ve gerekli projelerin olu\u015fturulmas\u0131n\u0131 ve kullan\u0131lacak k\u00fct\u00fcphanelerin kurulumunu ger\u00e7ekle\u015ftirelim.<\/p>\n<h3>Servislerin Olu\u015fturulmas\u0131<\/h3>\n<p>Bu senaryomuzda esas\u0131nda bir \u00f6nceki <a href=\"https:\/\/www.gencayyildiz.com\/blog\/microservice-saga-events-choreography-implemantasyonu-ile-transaction-yonetimi\/\" rel=\"noopener\" target=\"_blank\">Microservice &#8211; Saga &#8211; Events\/Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi<\/a> ba\u015fl\u0131kl\u0131 makalemizde ele ald\u0131\u011f\u0131m\u0131z senaryoyla birebir ayn\u0131 olacakt\u0131r. Lakin implementasyon olarak choreography yerine orchestration kullanaca\u011f\u0131m\u0131zdan dolay\u0131 ayr\u0131ca bir state machine g\u00f6revi g\u00f6recek servisimiz daha olacakt\u0131r.<\/p>\n<p>Yine de senaryonun genel hatt\u0131n\u0131 tekrar \u00e7izmemiz icap ederse; bir e-ticaret sistemindeki sipari\u015f s\u00fcrecini modelliyor olaca\u011f\u0131z. Bu s\u00fcre\u00e7te <span style=\"color:purple;\">Order.API<\/span>, <span style=\"color:purple;\">Stock.API<\/span> ve <span style=\"color:purple;\">Payment.API<\/span> isimli servisler sorumlu olacak ve s\u0131ras\u0131yla i\u015flevsellik g\u00f6stereceklerdir. Ayr\u0131ca bu servisler aras\u0131nda Saga orchestration implementasyonuna uygun bir \u015fekilde merkezi distributed transaction y\u00f6netimini sa\u011flayacak olan <span style=\"color:purple;\">SagaStateMachine.Service<\/span> ad\u0131nda worker service olu\u015fturulacakt\u0131r. T\u00fcm bu servisleri olu\u015fturabilmek i\u00e7in a\u015fa\u011f\u0131daki dotnet cli komutlar\u0131ndan istifade edebilirsiniz;<\/p>\n<div style=\"text-align:center;\">\n<span style=\"color:red;font-size:11px;\">(Service)<\/span> <code style=\"color:green;\"><strong>dotnet new webapi --name Order.API<\/strong><\/code><br \/>\n<span style=\"color:red;font-size:11px;\">(Service)<\/span> <code style=\"color:green;\"><strong>dotnet new webapi --name Stock.API<\/strong><\/code><br \/>\n<span style=\"color:red;font-size:11px;\">(Service)<\/span> <code style=\"color:green;\"><strong>dotnet new webapi --name Payment.API<\/strong><\/code><br \/>\n<span style=\"color:red;font-size:11px;\">(Worker Service)<\/span> <code style=\"color:green;\"><strong>dotnet new worker --name SagaStateMachine.Service<\/strong><\/code><br \/>\n<span style=\"color:red;font-size:11px;\">(Class Library)<\/span> <code style=\"color:green;\"><strong>dotnet new classlib --name Shared<\/strong><\/code> : <span style=\"font-size:9px;\">Servisler aras\u0131 mesajla\u015fma i\u00e7in event\u2019leri vs. bu class library\u2019de olu\u015fturaca\u011f\u0131z.<\/span>\n<\/div>\n<h3>Servislere MassTransit Kurulumu ve Temel Konfig\u00fcrasyonlar<\/h3>\n<p>Saga pattern kullan\u0131yorsak e\u011fer asenkron bir ileti\u015fim modeli olmazsa olmaz bir yakla\u015f\u0131md\u0131r. <span style=\"font-size:9px;\">(bknz: <a href=\"https:\/\/www.gencayyildiz.com\/blog\/microservice-saga-events-choreography-implemantasyonu-ile-transaction-yonetimi\/\" rel=\"noopener\" target=\"_blank\">\u00f6nceki makale\/choreography<\/a>)<\/span> Dolay\u0131s\u0131yla bizler bu asenkron ileti\u015fim s\u00fcrecinde message broker olarak <a href=\"https:\/\/www.gencayyildiz.com\/blog\/rabbitmq-yazi-dizisi\/\" rel=\"noopener\" target=\"_blank\">RabbitMQ<\/a>&#8216;yu tercih edece\u011fiz ve yukar\u0131daki paragraflarda bahsedildi\u011fi \u00fczere bu message broker&#8217;\u0131 daha efektif hale getirebilmek i\u00e7in MassTransit Enterprise Service Bus k\u00fct\u00fcphanesinden istifade edece\u011fiz. Dolay\u0131s\u0131yla yukar\u0131da olu\u015fturulan t\u00fcm servislere bahsi ge\u00e7en ilgili k\u00fct\u00fcphaneleri a\u015fa\u011f\u0131daki dotnet cli komutlar\u0131 e\u015fli\u011finde y\u00fckleyelim.<\/p>\n<div style=\"text-align:center;\">\n<span style=\"color:red;font-size:11px;\">(Library)<\/span> <a href=\"https:\/\/www.nuget.org\/packages\/MassTransit\/\" target=\"_blank\" rel=\"noopener\"><code style=\"color:green;\"><strong>dotnet add package MassTransit<\/strong><\/code><\/a><br \/>\n<span style=\"color:red;font-size:11px;\">(Library)<\/span> <a href=\"https:\/\/www.nuget.org\/packages\/MassTransit.AspNetCore\/\" target=\"_blank\" rel=\"noopener\"><code style=\"color:green;\"><strong>dotnet add package MassTransit.AspNetCore<\/strong><\/code><\/a><br \/>\n<span style=\"color:red;font-size:11px;\">(Library)<\/span> <a href=\"https:\/\/www.nuget.org\/packages\/MassTransit.RabbitMQ\/\" target=\"_blank\" rel=\"noopener\"><code style=\"color:green;\"><strong>dotnet add package MassTransit.RabbitMQ<\/strong><\/code><\/a>\n<\/div>\n<p>Akabinde <span style=\"color:purple;\">SagaStateMachine.Service<\/span> haricindeki t\u00fcm servislerin &#8216;Startup.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki konfig\u00fcrasyonlar\u0131 ger\u00e7ekle\u015ftirerek, RabbitMQ entegrasyonunu sa\u011flayal\u0131m.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class Startup\r\n    {\r\n        .\r\n        .\r\n        .\r\n        public void ConfigureServices(IServiceCollection services)\r\n        {\r\n            .\r\n            .\r\n            .\r\n            services.AddMassTransit(configure =&gt;\r\n            {\r\n                configure.UsingRabbitMq((context, configurator) =&gt;\r\n                {\r\n                    configurator.Host(Configuration.GetConnectionString(&quot;RabbitMQ&quot;));\r\n                });\r\n            });\r\n\r\n            services.AddMassTransitHostedService();\r\n            .\r\n            .\r\n            .\r\n        }\r\n        .\r\n        .\r\n        .\r\n    }\r\n<\/pre>\n<p>\u0130deal olarak her zaman yapt\u0131\u011f\u0131m\u0131z gibi gerekli connection bilgilerini yap\u0131land\u0131rma dosyas\u0131ndan yahut environment&#8217;tan talep etmekteyiz. Misal ilgili bilgilerin &#8216;appsettings.json&#8217; dosyas\u0131ndan geldi\u011fini varsayarsak e\u011fer ilgili dosyan\u0131n i\u00e7eri\u011fi a\u015fa\u011f\u0131daki gibi olacakt\u0131r.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n{\r\n  &quot;Logging&quot;: {\r\n    &quot;LogLevel&quot;: {\r\n      &quot;Default&quot;: &quot;Information&quot;,\r\n      &quot;Microsoft&quot;: &quot;Warning&quot;,\r\n      &quot;Microsoft.Hosting.Lifetime&quot;: &quot;Information&quot;\r\n    }\r\n  },\r\n  &quot;AllowedHosts&quot;: &quot;*&quot;,\r\n  &quot;ConnectionStrings&quot;: {\r\n    &quot;RabbitMQ&quot;: &quot;amqps:\/\/l***kqjU9x1bR@cattle.rmq**om\/ll***&quot;\r\n  }\r\n}\r\n<\/pre>\n<p>Burada RabbitMQ i\u00e7in <a href=\"https:\/\/www.cloudamqp.com\/\" rel=\"noopener\" target=\"_blank\">cloudamqp.com<\/a> adresindeki cloud ortamda yarat\u0131lan instance&#8217;a uygun bir ba\u011flant\u0131 verilmi\u015ftir. <a href=\"https:\/\/www.gencayyildiz.com\/blog\/microservice-saga-events-choreography-implemantasyonu-ile-transaction-yonetimi\/\" rel=\"noopener\" target=\"_blank\">\u00d6nceki makalede<\/a> de RabbitMQ&#8217;nun cloud ortam\u0131na dair herhangi bir fikriniz olmad\u0131\u011f\u0131 taktirde <a href=\"https:\/\/www.gencayyildiz.com\/blog\/rabbitmq-cloud-ortamdacloudamqp-kurulumu\/\" rel=\"noopener\" target=\"_blank\">RabbitMQ \u2013 Cloud Ortamda(CloudAMQP) Kurulumu<\/a> ba\u015fl\u0131kl\u0131 makaleye g\u00f6z atman\u0131z\u0131 tavsiye etmi\u015ftim. Ve hala ediyorum \ud83d\ude42<\/p>\n<h3><span style=\"color:purple;\">SagaStateMachine.Service<\/span>&#8216;in Yap\u0131land\u0131r\u0131lmas\u0131<\/h3>\n<p>\u015eimdi Saga orchestration implementasyonunun merkezi olan ve state machine&#8217;e kar\u015f\u0131l\u0131k gelen <span style=\"color:purple;\">SagaStateMachine.Service<\/span>&#8216;in konfig\u00fcrasyonlar\u0131n\u0131 ger\u00e7ekle\u015ftirelim. <\/p>\n<p><span style=\"color:purple;\">SagaStateMachine.Service<\/span>, \u00f6z\u00fcnde bir worker service&#8217;dir. Yani arkaplanda \u00e7al\u0131\u015fan bir servis. Haliyle i\u00e7erisinde &#8216;Startup.cs&#8217; olmayan bu worker servis uygulamas\u0131n\u0131n konfig\u00fcrasyonlar\u0131n\u0131 yapabilmek i\u00e7in &#8216;Program.cs&#8217; dosyas\u0131ndaki &#8216;CreateHostBuilder&#8217; isimli metot i\u00e7erisinde \u00e7a\u011fr\u0131lan &#8216;ConfigureService&#8217; metoduna a\u015fa\u011f\u0131daki m\u00fcdahalelerde bulunmam\u0131z gerekmektedir.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class Program\r\n    {\r\n        .\r\n        .\r\n        .\r\n        public static IHostBuilder CreateHostBuilder(string&#x5B;] args) =&gt;\r\n            Host.CreateDefaultBuilder(args)\r\n                .ConfigureServices((hostContext, services) =&gt;\r\n                {\r\n                    .\r\n                    .\r\n                    .\r\n                    services.AddMassTransit(configure =&gt;\r\n                    {\r\n                        configure.AddBus(provider =&gt; Bus.Factory.CreateUsingRabbitMq(cfg =&gt;\r\n                        {\r\n                            cfg.Host(hostContext.Configuration.GetConnectionString(&quot;RabbitMQ&quot;));\r\n                        }));\r\n                    });\r\n\r\n                    services.AddMassTransitHostedService();\r\n                    .\r\n                    .\r\n                    .\r\n                });\r\n    }\r\n<\/pre>\n<p>Evet&#8230; Asl\u0131nda yukar\u0131daki verilen t\u00fcm kodlar ilerideki ad\u0131mlarda mevcut hallerinden daha da geli\u015ferek bizlere e\u015flik edecektir. \u015eimdilik en temel konfig\u00fcrasyonlarla yolculu\u011fumuza ba\u015flamaya haz\u0131r\u0131z.<\/p>\n<h3>Servislerin Geli\u015ftirilmesi<\/h3>\n<p>\u015eimdi i\u00e7eri\u011fimizde s\u0131n\u0131rlar\u0131 \u00e7izilen senaryoya uygun bir \u015fekilde Saga orchestration implementasyonu e\u015fli\u011finde servislerimizi ad\u0131m ad\u0131m geli\u015ftirebiliriz. Tabi ki de burada pratiksel olarak ele al\u0131nan uygulaman\u0131n herhangi bir ad\u0131m\u0131n\u0131n makalenin seyrine g\u00f6re farkl\u0131 ihtiya\u00e7lara istinaden de\u011fi\u015fikli\u011fe u\u011frayabilme ihtimali vard\u0131r. Dolay\u0131s\u0131yla iyi takip etmenizi ve hatta gerekirse s\u00fcrece sizlerin de pratiksel olarak e\u015flik etmenizi tavsiye ederim&#8230; Buyrun ba\u015flayal\u0131m&#8230;<\/p>\n<ul>\n<li><strong><em>Ad\u0131m 1<\/em> <\/strong><em style=\"color:green;font-size:11px;\">Order.API |<\/em> <em style=\"color:purple;font-size:11px;\">Temel Altyap\u0131n\u0131n At\u0131lmas\u0131<\/em><br \/>\n\u00d6nceki makalemizde oldu\u011fu gibi burada da servislerimizin temel yap\u0131lanmas\u0131n\u0131 in\u015fa ederek ba\u015flayaca\u011f\u0131z. Tabi ki de sizler bu k\u0131s\u0131mlar\u0131 h\u0131zl\u0131ca ge\u00e7ebilir yahut bir \u00f6nceki makalede olu\u015fturulan projenin altyap\u0131s\u0131n\u0131 burada kullanabilirsiniz.<\/p>\n<p>\u015eimdi ilk olarak <span style=\"color:purple;\">Order.API<\/span> servisinde, yap\u0131lacak sipari\u015fleri temsil edecek olan &#8216;Order&#8217; ve &#8216;OrderItem&#8217; nesnelerini modelleyerek ba\u015flayal\u0131m;<\/p>\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<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<p>Ayr\u0131ca bir sipari\u015fin durumunu ifade eden &#8216;OrderStatus&#8217; enum&#8217;unu da tasarlayal\u0131m;<\/p>\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<p>Ard\u0131ndan bu modelleri veritaban\u0131na migrate edebilmek i\u00e7in bir a\u015fa\u011f\u0131daki gibi context nesnesi modelleyelim;<\/p>\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        public DbSet&lt;Order&gt; Orders { get; set; }\r\n        public DbSet&lt;OrderItem&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<p><span style=\"color:purple;\">Order.API<\/span>&#8216;da \u00f6nceki makalede oldu\u011fu gibi MS SQL Server kullanaca\u011f\u0131m\u0131zdan dolay\u0131 <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.SqlServer\/\" rel=\"noopener\" target=\"_blank\">Microsoft.EntityFrameworkCore.SqlServer<\/a> k\u00fct\u00fcphanesini y\u00fckleyerek akabinde ilgili servisin &#8216;Startup.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki entegrasyonlar\u0131 ger\u00e7ekle\u015ftirelim.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n        public void ConfigureServices(IServiceCollection services)\r\n        {\r\n            .\r\n            .\r\n            .\r\n            services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;\r\n                options.UseSqlServer(Configuration.GetConnectionString(&quot;SQLServer&quot;)));\r\n            .\r\n            .\r\n            .\r\n        }\r\n<\/pre>\n<p>Yukar\u0131daki kod blo\u011funa dikkat ederseniz yap\u0131land\u0131rma dosyas\u0131ndan(appsettings.json) &#8216;SQLServer&#8217; de\u011feri \u00e7ekilmektedir. Haliyle ilgili dosyaya da a\u015fa\u011f\u0131daki gibi de\u011ferlerimizi ekliyoruz;<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n  &quot;ConnectionStrings&quot;: {\r\n    &quot;RabbitMQ&quot;: &quot;amqps:\/\/llgi****dDpOkqjU9x1bR@ca**e.rmq2.cloudamqp.com\/l*****&quot;,\r\n    &quot;SQLServer&quot;: &quot;Server=***;Database=SagaOrchestrationDB;User Id=sa;Password=***;&quot;\r\n  }\r\n<\/pre>\n<p>B\u00f6ylece olmas\u0131 gereken temeli yap\u0131sal olarak in\u015fa etmi\u015f bulunmaktay\u0131z. Art\u0131k s\u0131rada in\u015fa edilen bu altyap\u0131n\u0131n migrate edilmesi vard\u0131r. Bunun i\u00e7in ilgili servise <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Tools\" rel=\"noopener\" target=\"_blank\">Microsoft.EntityFrameworkCore.Tools<\/a> k\u00fct\u00fcphanesini y\u00fckleyerek akabinde &#8216;Package Manager Console&#8217; \u00fczerinden <code>add-migration mig_1<\/code> talimat\u0131n\u0131 vererek bir migration olu\u015fturabilir ve ard\u0131ndan <code>update-database<\/code> talimat\u0131 ile bu migration&#8217;\u0131 hedef SQL sunucusuna migrate edebiliriz. Tabi bu i\u015flemleri <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Design\/\" rel=\"noopener\" target=\"_blank\">Microsoft.EntityFrameworkCore.Design<\/a> k\u00fct\u00fcphanesi e\u015fli\u011finde dotnet cli ile de ger\u00e7ekle\u015ftirebiliriz. Bunun i\u00e7inde powershell \u00fczerinden s\u0131ras\u0131yla <code>dotnet ef migrations add mig_1<\/code> ve <code>dotnet ef database update<\/code> talimatlar\u0131n\u0131n verilmesi yeterli olacakt\u0131r.\n<\/li>\n<li><strong><em>Ad\u0131m 2<\/em> <\/strong><em style=\"color:green;font-size:11px;\">Order.API | <\/em><em style=\"color:purple;font-size:11px;\">Sipari\u015f Olu\u015fturma<\/em><br \/>\n<span style=\"color:purple;\">Order.API<\/span> servisinde sipari\u015f talebini kar\u015f\u0131layabilmek i\u00e7in <em>OrdersController.cs<\/em> isminde bir controller s\u0131n\u0131f\u0131 olu\u015ftural\u0131m ve i\u00e7eri\u011fini a\u015fa\u011f\u0131daki gibi geli\u015ftirelim.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    &#x5B;Route(&quot;api\/&#x5B;controller]&quot;)]\r\n    &#x5B;ApiController]\r\n    public class OrdersController : ControllerBase\r\n    {\r\n        readonly ApplicationDbContext _applicationDbContext;\r\n        public OrdersController(ApplicationDbContext applicationDbContext)\r\n        {\r\n            _applicationDbContext = applicationDbContext;\r\n        }\r\n        &#x5B;HttpPost]\r\n        public async Task&lt;IActionResult&gt; CreateOrder(OrderVM model)\r\n        {\r\n            Order.API.Models.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 _applicationDbContext.AddAsync&lt;Order.API.Models.Order&gt;(order);\r\n\r\n            await _applicationDbContext.SaveChangesAsync();\r\n            return Ok(true);\r\n        }\r\n    }\r\n<\/pre>\n<p>Yukar\u0131daki kod blo\u011funu incelerseniz e\u011fer &#8216;CreateOrder&#8217; action metodunda gelen sipari\u015f bilgilerine g\u00f6re bir &#8216;Order&#8217; nesnesi olu\u015fturulmakta ve veritaban\u0131na eklenmektedir. Burada olu\u015fturulan bu &#8216;Order&#8217; nesnesinin durum bilgisine ilk etapta <span style=\"color:orange;\">Suspend<\/span> verildi\u011fine dikkatinizi \u00e7ekerim. Nihayetinde bir sipari\u015f s\u00fcreci farkl\u0131 servislerde yap\u0131lan periyodik i\u015flemler s\u00fcrecinden ge\u00e7mekte ve nihai olarak distributed transaction sa\u011flanana kadar bu sipari\u015f durumu hakk\u0131nda kesin karar verilememektedir.<\/p>\n<p>Yukar\u0131daki yap\u0131lanmada viewmodel olarak kullan\u0131lan &#8216;OrderVM&#8217; nesnesinin i\u00e7eri\u011fini de merak ederseniz a\u015fa\u011f\u0131daki gibi olacakt\u0131r:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderVM\r\n    {\r\n        public int BuyerId { get; set; }\r\n        public List&lt;OrderItemVM&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderItemVM\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<\/li>\n<li><strong><em>Ad\u0131m 3<\/em> <\/strong><em style=\"color:green;font-size:11px;\">Stock.API | <\/em><em style=\"color:purple;font-size:11px;\">Temel Altyap\u0131n\u0131n At\u0131lmas\u0131<\/em><br \/>\n\u015eimdi s\u0131ra <span style=\"color:purple;\">Stock.API<\/span>&#8216;\u0131n altyap\u0131s\u0131n\u0131 olu\u015fturmaya gelmi\u015ftir. Burada \u015fimdilik yap\u0131lacak i\u015flem olduk\u00e7a basit olacakt\u0131r. \u00c7e\u015fitlili\u011fi artt\u0131rmak i\u00e7in bu servisimizde \u00f6nceki <a href=\"https:\/\/www.gencayyildiz.com\/blog\/microservice-saga-events-choreography-implemantasyonu-ile-transaction-yonetimi\/\" rel=\"noopener\" target=\"_blank\">choreography implemantasyonu<\/a>nu ele ald\u0131\u011f\u0131m\u0131z makalemizde oldu\u011fu gibi MongoDB kullanaca\u011f\u0131z. Haliyle MongoDB altyap\u0131s\u0131n\u0131n at\u0131lmas\u0131 gerekmektedir.<\/p>\n<p>Bunun i\u00e7in ilgili serviste &#8216;Services&#8217; isimli bir klas\u00f6r olu\u015ftural\u0131m ve i\u00e7erisine <em>MongoDbService<\/em> ad\u0131nda bir s\u0131n\u0131f olu\u015ftural\u0131m ve i\u00e7eri\u011fini a\u015fa\u011f\u0131daki gibi in\u015fa edelim.<\/p>\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:DBName&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<p>Yukar\u0131daki kodu in\u015fa edebilmek i\u00e7in <a href=\"https:\/\/www.nuget.org\/packages\/MongoDB.Driver\/\" rel=\"noopener\" target=\"_blank\">MongoDB.Driver<\/a> k\u00fct\u00fcphanesinin y\u00fcklenmesi gerekmektedir. Kod i\u00e7erisindeki yap\u0131land\u0131rma dosyas\u0131ndan(appsettings.json) gelen de\u011ferler a\u015fa\u011f\u0131daki gibi olacakt\u0131r;<\/p>\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;DBName&quot;: &quot;SagaOrchestrationDB&quot;\r\n  }\r\n<\/pre>\n<p>Bu servisi in\u015fa ettikten sonra tek yap\u0131lmas\u0131 gereken istenildi\u011fi yerde dependency injection ile talep edebilmek i\u00e7in &#8216;Startup.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki gibi belirtilmesidir;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n        public void ConfigureServices(IServiceCollection services)\r\n        {\r\n            .\r\n            .\r\n            .\r\n            services.AddSingleton&lt;MongoDbService&gt;();\r\n            .\r\n            .\r\n            .\r\n        }\r\n<\/pre>\n<p>Ayr\u0131ca <span style=\"color:purple;\">Stock.API<\/span>&#8216;\u0131 aya\u011fa kald\u0131rd\u0131\u011f\u0131m\u0131zda MongoDB&#8217;de haz\u0131r dummy data bulunmas\u0131 i\u00e7in &#8216;Program.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki gibi \u00e7al\u0131\u015fma yap\u0131lmas\u0131 test s\u00fcrecinde i\u015fimizi olduk\u00e7a kolayla\u015ft\u0131racakt\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class Program\r\n    {\r\n        public static void Main(string&#x5B;] args)\r\n        {\r\n            var host = CreateHostBuilder(args).Build();\r\n\r\n            using IServiceScope scope = host.Services.CreateScope();\r\n            MongoDbService mongoDbService = scope.ServiceProvider.GetRequiredService&lt;MongoDbService&gt;();\r\n            if (!mongoDbService.GetCollection&lt;Models.Stock&gt;().FindSync(x =&gt; true).Any())\r\n            {\r\n                mongoDbService.GetCollection&lt;Stock.API.Models.Stock&gt;().InsertOne(new()\r\n                {\r\n                    ProductId = 21,\r\n                    Count = 200\r\n                });\r\n                mongoDbService.GetCollection&lt;Stock.API.Models.Stock&gt;().InsertOne(new()\r\n                {\r\n                    ProductId = 22,\r\n                    Count = 100\r\n                });\r\n                mongoDbService.GetCollection&lt;Stock.API.Models.Stock&gt;().InsertOne(new()\r\n                {\r\n                    ProductId = 23,\r\n                    Count = 50\r\n                });\r\n                mongoDbService.GetCollection&lt;Stock.API.Models.Stock&gt;().InsertOne(new()\r\n                {\r\n                    ProductId = 24,\r\n                    Count = 10\r\n                });\r\n                mongoDbService.GetCollection&lt;Stock.API.Models.Stock&gt;().InsertOne(new()\r\n                {\r\n                    ProductId = 25,\r\n                    Count = 30\r\n                });\r\n            }\r\n\r\n            host.Run();\r\n        }\r\n\r\n        public static IHostBuilder CreateHostBuilder(string&#x5B;] args) =&gt;\r\n            Host.CreateDefaultBuilder(args)\r\n                .ConfigureWebHostDefaults(webBuilder =&gt;\r\n                {\r\n                    webBuilder.UseStartup&lt;Startup&gt;();\r\n                });\r\n    }\r\n<\/pre>\n<p>Yukar\u0131daki yap\u0131lanman\u0131n in\u015fa edilebilmesi i\u00e7in a\u015fa\u011f\u0131daki &#8216;Stock&#8217; modeline ihtiya\u00e7 vard\u0131r;<\/p>\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        &#x5B;BsonRepresentation(BsonType.DateTime)]\r\n        &#x5B;BsonDateTimeOptions(Kind = DateTimeKind.Utc)]\r\n        &#x5B;BsonElement(Order = 3)]\r\n        public DateTime CreatedDate { get; set; } = DateTime.Now;\r\n    }\r\n<\/pre>\n<\/li>\n<li><strong><em>Ad\u0131m 4<\/em> <\/strong><em style=\"color:green;font-size:11px;\">SagaStateMachine.Service | <\/em><em style=\"color:purple;font-size:11px;\">State Machine Altyap\u0131s\u0131n\u0131n Kurulmas\u0131<\/em><br \/>\nOrchestration implementasyonunda en kritik noktalardan biri State Machine&#8217;i iyi kavrayabilmekten ge\u00e7mektedir. Makalemizin \u00f6nceki paragraflar\u0131nda State Machine olarak MassTransit k\u00fct\u00fcphanesinin dahili olarak getirdi\u011fi Automatonymous k\u00fct\u00fcphanesinden faydalanaca\u011f\u0131m\u0131zdan bahsetmi\u015ftik. Bu k\u00fct\u00fcphane bizlere state machine yap\u0131lanmas\u0131 i\u00e7in belli ba\u015fl\u0131 akt\u00f6rler sunmaktad\u0131r. \u015eimdi gelin \u00f6ncelikle bu akt\u00f6rlerin kimler oldu\u011funu tan\u0131mlayal\u0131m;<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"width:150px;\">Akt\u00f6r<\/th>\n<th>A\u00e7\u0131klama<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align:left;\"><strong>State Machine<\/strong><\/td>\n<td style=\"text-align:left;\">State Machine yap\u0131lanmas\u0131n\u0131 bizlere sunan s\u0131n\u0131ft\u0131r. Sorumluluk olarak state&#8217;leri, event&#8217;leri ve belirli davran\u0131\u015flar\u0131 belirleyen merkezi bir rol oynar. Yani distributed transaction&#8217;\u0131 y\u00f6netecek olan s\u0131n\u0131ft\u0131r diyebiliriz. Bir s\u0131n\u0131f\u0131n state machine olabilmesi i\u00e7in <code style=\"color:purple;font-size:13px;\">MassTransitStateMachine&lt;T&gt;<\/code> aray\u00fcz\u00fcn\u00fc uygulamas\u0131 gerekmektedir. Generic olarak belirtilen <code style=\"color:purple;font-size:13px;\">T<\/code> ise bir State Instance almaktad\u0131r.<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left;\"><strong>State Instance<\/strong><\/td>\n<td style=\"text-align:left;\">Bir state machine verisini temsil eden s\u0131n\u0131ft\u0131r. Misal olarak ayn\u0131 anda yap\u0131lan iki farkl\u0131 sipari\u015f iste\u011finin her biri bir state instance olarak veritaban\u0131nda tutulacakt\u0131r. Haliyle gelen bu istekleri birbirlerinden ay\u0131rt edebilmek i\u00e7in CorrelationId de\u011feri kullan\u0131lmaktad\u0131r. Bu de\u011fer tekille\u015ftirici niteli\u011fe sahiptir. Bir s\u0131n\u0131f\u0131n state instance olabilmesi i\u00e7in <code style=\"color:purple;font-size:13px;\">SagaStateMachineInstance<\/code> aray\u00fcz\u00fcn\u00fc uygulamas\u0131 gerekmektedir.<\/p>\n<p>Orchestration implementasyonunda d\u00f6rt temel event vard\u0131r. Bu event&#8217;ler;<\/p>\n<ul>\n<li><em style=\"color:orange;\">Tetikleyici Event<\/em>: \u0130lgili service&#8217;i tetikleyecek\/\u00e7al\u0131\u015ft\u0131racak\/i\u015flevsel hale getirecek olan event&#8217;tir.<\/li>\n<li><em style=\"color:orange;\">Ba\u015far\u0131l\u0131 Event<\/em>: \u0130\u015flevin ba\u015far\u0131yla sonu\u00e7land\u0131\u011f\u0131n\u0131 ifade eden event&#8217;tir.<\/li>\n<li><em style=\"color:orange;\">Ba\u015far\u0131s\u0131z Event<\/em>: \u0130\u015flevin ba\u015far\u0131s\u0131zl\u0131kla sonu\u00e7land\u0131\u011f\u0131n\u0131 ifade eden event&#8217;tir.<\/li>\n<li><em style=\"color:orange;\">Compensable Event<\/em>: Yap\u0131lan i\u015flemlerin geri al\u0131nmas\u0131n\u0131 bildiren event&#8217;tir.<\/li>\n<\/ul>\n<p>Dolay\u0131s\u0131yla gelen her tetikleyici event i\u00e7in yeni bir state instance olu\u015fturulacak ve di\u011fer event&#8217;ler de ise \u00f6nceden olu\u015fturulmu\u015f state instance CorrelationId \u00fczerinden tespit edilip \u00fczerinden i\u015flem ger\u00e7ekle\u015ftirilecektir.\n<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left;\"><strong>State DbContext<\/strong><\/td>\n<td style=\"text-align:left;\">State Machine&#8217;in verilerinin tutulaca\u011f\u0131 veritaban\u0131n\u0131 y\u00f6netecek olan context nesnesidir. <code style=\"color:purple;font-size:13px;\">SagaDbContext<\/code> abstract class&#8217;\u0131ndan t\u00fcremektedir.<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left;\"><strong>State Map<\/strong><\/td>\n<td style=\"text-align:left;\">Veritaban\u0131na kaydedilen State Instance&#8217;da ki property&#8217;lerin validasyon ayarlar\u0131n\u0131n yap\u0131lmas\u0131n\u0131 sa\u011flayan s\u0131n\u0131ft\u0131r. <code style=\"color:purple;font-size:13px;\">SagaClassMap&lt;T&gt;<\/code> abstract class&#8217;\u0131ndan t\u00fcremektedir. Generic olan <code style=\"color:purple;font-size:13px;\">T<\/code> parametresi bir State Instance al\u0131r.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u015eimdi gelin bu akt\u00f6rleri tek tek olu\u015ftural\u0131m ve <span style=\"color:purple;\">SagaStateMachine.Service<\/span>&#8216;in altyap\u0131s\u0131n\u0131 kurmu\u015f olal\u0131m. Tabi burada ilgili s\u0131n\u0131flar\u0131n olu\u015fturulmas\u0131 yukar\u0131daki tablodaki s\u0131ray\u0131 seyretmeyecek, \u00f6ncelik dikkate al\u0131narak ger\u00e7ekle\u015ftirilecektir.<\/p>\n<ul>\n<li><em style=\"color:pink;\"><strong>State Instance<\/strong><\/em>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    \/\/\/ &lt;summary&gt;\r\n    \/\/\/ OrderStateInstance : Her bir sipari\u015f eklendi\u011finde(tetikleyici event geldi\u011finde)\r\n    \/\/\/ bu sipari\u015fe kar\u015f\u0131l\u0131k Saga State Machine'de tutulacak olan sat\u0131r\u0131 Order State Instance\r\n    \/\/\/ olarak tarif etmekteyiz.\r\n    \/\/\/ &lt;\/summary&gt;\r\n    public class OrderStateInstance : SagaStateMachineInstance\r\n    {\r\n        \/\/\/ &lt;summary&gt;\r\n        \/\/\/ Her bir State Instance \u00f6z\u00fcnde bir sipari\u015fe \u00f6zeldir. Haliyle bu State Instance'lar\u0131\r\n        \/\/\/ birbirinden ay\u0131rabilmek i\u00e7in CorrelationId(yani bildi\u011finiz unique id) kullan\u0131lmaktad\u0131r\r\n        \/\/\/ &lt;\/summary&gt;\r\n        public Guid CorrelationId { get; set; }\r\n        public string CurrentState { 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 DateTime CreatedDate { get; set; }\r\n    }\r\n<\/pre>\n<p>Dikkat ederseniz e\u011fer State Machine&#8217;in herbir sat\u0131r\u0131na kar\u015f\u0131l\u0131k gelecek olan State Instance&#8217;da bizlere laz\u0131m olan verileri de tutmaktay\u0131z. Bu demek oluyor ki, choreography implementasyonundaki event&#8217;lerin ta\u015f\u0131yaca\u011f\u0131 datalarla orchestration implementasyonundaki state machine&#8217;de ki event&#8217;lerin a\u015f\u0131yaca\u011f\u0131 datalar farkl\u0131d\u0131r&#8230;\n<\/li>\n<li><em style=\"color:pink;\"><strong>State Map<\/strong><\/em>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderStateMap : SagaClassMap&lt;OrderStateInstance&gt;\r\n    {\r\n        protected override void Configure(EntityTypeBuilder&lt;OrderStateInstance&gt; entity, ModelBuilder model)\r\n        {\r\n            entity.Property(x =&gt; x.BuyerId).IsRequired();\r\n            entity.Property(x =&gt; x.OrderId).IsRequired();\r\n            entity.Property(x =&gt; x.TotalPrice).HasDefaultValue(0);\r\n        }\r\n    }\r\n<\/pre>\n<p><code style=\"color:purple;font-size:13px;\">SagaClassMap&lt;T&gt;<\/code>&#8216;e eri\u015febilmek i\u00e7in <a href=\"https:\/\/www.nuget.org\/packages\/MassTransit.EntityFrameworkCore\/7.1.8\" rel=\"noopener\" target=\"_blank\">MassTransit.EntityFrameworkCore<\/a> k\u00fct\u00fcphanesini uygulamaya y\u00fcklemeniz gerekmektedir.\n<\/li>\n<li><em style=\"color:pink;\"><strong>State DbContext<\/strong><\/em>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderStateDbContext : SagaDbContext\r\n    {\r\n        public OrderStateDbContext(DbContextOptions options) : base(options)\r\n        {\r\n        }\r\n\r\n        \/\/\/ &lt;summary&gt;\r\n        \/\/\/ ISagaClassMap : Order State Instance'da ki property'lerin\r\n        \/\/\/ validasyon ayarlar\u0131n\u0131n yap\u0131lmas\u0131n\u0131 sa\u011flar.\r\n        \/\/\/ &lt;\/summary&gt;\r\n        protected override IEnumerable&lt;ISagaClassMap&gt; Configurations\r\n        {\r\n            get\r\n            {\r\n                yield return new OrderStateMap();\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<\/li>\n<li><em style=\"color:pink;\"><strong>State Machine<\/strong><\/em>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderStateMachine : MassTransitStateMachine&lt;OrderStateInstance&gt;\r\n    {\r\n        public OrderStateMachine()\r\n        {\r\n            \/\/State Instance'da ki hangi property'nin sipari\u015f s\u00fcrecindeki state'i tutaca\u011f\u0131 bildiriliyor.\r\n            \/\/Yani art\u0131k t\u00fcm event'ler CurrentState property'sin de tutulacakt\u0131r!\r\n            InstanceState(instance =&gt; instance.CurrentState);\r\n\r\n\r\n            \/*\r\n                .\r\n                Event \u00e7al\u0131\u015fmalar\u0131\r\n                .\r\n             *\/\r\n        }\r\n    }\r\n<\/pre>\n<p>State Machine \u015fimdilik en sade haliyle olu\u015fturulmu\u015ftur. Burada dikkat edilmesi gereken \u00f6ncelikli husus veritaban\u0131na durum kayd\u0131 yapan State Instance property&#8217;lerinden hangisinin ger\u00e7ek state bilgisini tuttu\u011fu ayar\u0131d\u0131r. Bunu yukar\u0131daki koda dikkat ederseniz &#8216;InstanceState&#8217; ile ger\u00e7ekle\u015ftirmekteyiz.\n<\/li>\n<\/ul>\n<p>Son olarak yap\u0131lan bu in\u015falar\u0131n uygulamaya entegre edilmesi gerekmektedir. Bunun i\u00e7in, <span style=\"color:purple;\">SagaStateMachine.Service<\/span> bir worker service oldu\u011fundan dolay\u0131 &#8216;Program.cs&#8217; dosyas\u0131ndaki &#8216;CreateHostBuilder&#8217; metodunda a\u015fa\u011f\u0131daki gibi \u00e7al\u0131\u015fmam\u0131z gerekmektedir.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n        public static IHostBuilder CreateHostBuilder(string&#x5B;] args) =&gt;\r\n            Host.CreateDefaultBuilder(args)\r\n                .ConfigureServices((hostContext, services) =&gt;\r\n                {\r\n                    services.AddMassTransit(configure =&gt;\r\n                    {\r\n                        configure.AddSagaStateMachine&lt;OrderStateMachine, OrderStateInstance&gt;()\r\n                          .EntityFrameworkRepository(options =&gt;\r\n                          {\r\n                              options.AddDbContext&lt;DbContext, OrderStateDbContext&gt;((provider, builder) =&gt;\r\n                              {\r\n                                  builder.UseSqlServer(hostContext.Configuration.GetConnectionString(&quot;SQLServer&quot;));\r\n                              });\r\n                          });\r\n\r\n                        configure.AddBus(provider =&gt; Bus.Factory.CreateUsingRabbitMq(cfg =&gt;\r\n                        {\r\n                            cfg.Host(hostContext.Configuration.GetConnectionString(&quot;RabbitMQ&quot;));\r\n                        }));\r\n                    });\r\n\r\n                    services.AddMassTransitHostedService();\r\n                });\r\n<\/pre>\n<p>Yukar\u0131daki kod blo\u011funda 7 ile 14. sat\u0131rlara g\u00f6z atarsan\u0131z e\u011fer &#8216;AddSagaStateMachine&#8217; metodu ile MassTransit servisine state machine entegre edilmekte ve generic olarak hangi state machine nesnesine hangi state instance nesnesinin olaca\u011f\u0131 bildirilmektedir. Ayr\u0131ca &#8216;EntityFrameworkRepository&#8217; metodu ile state&#8217;lerin hangi veritaban\u0131nda depolanaca\u011f\u0131 bildirilmektedir. Malum burada da SQL Server kullanaca\u011f\u0131m\u0131zdan dolay\u0131 <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.SqlServer\/\" rel=\"noopener\" target=\"_blank\">Microsoft.EntityFrameworkCore.SqlServer<\/a> k\u00fct\u00fcphanesini y\u00fcklemeniz gerekmektedir.<\/p>\n<p>16 ile 19. sat\u0131rlar aras\u0131nda ise bu servisin RabbitMQ ile entegrasyonu ger\u00e7ekle\u015ftirilmi\u015ftir. Bunuda makalemizin yukar\u0131 paragraflar\u0131nda <em>-SagaStateMachine.Service\u2018in Yap\u0131land\u0131r\u0131lmas\u0131-<\/em> ba\u015fl\u0131\u011f\u0131 alt\u0131nda \u00f6nceden ger\u00e7ekle\u015ftirmi\u015ftik.<\/p>\n<p>\u015eimdi <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Tools\" rel=\"noopener\" target=\"_blank\">Microsoft.EntityFrameworkCore.Tools<\/a> k\u00fct\u00fcphanesi e\u015fli\u011finde <code>add-migration mig_1<\/code> ve <code>update-database<\/code> talimatlar\u0131n\u0131 vererek gerekli migrate i\u015flemlerini ger\u00e7ekle\u015ftirelim.<\/p>\n<p>Son olarak ba\u011flant\u0131 c\u00fcmlecikleri &#8216;appsettings.json&#8217; dosyas\u0131ndan gelece\u011finden dolay\u0131 ilgili dosya i\u00e7eri\u011fide a\u015fa\u011f\u0131daki gibi olmal\u0131d\u0131r;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n  &quot;ConnectionStrings&quot;: {\r\n    &quot;SQLServer&quot;: &quot;Server=***;Database=SagaStateMachineDB;User Id=sa;Password=***;&quot;,\r\n    &quot;RabbitMQ&quot;: &quot;amqps:\/\/*kqjU9x**ttle.rmq2.cloudamqp**.com\/ll**ys&quot;\r\n  }\r\n<\/pre>\n<\/li>\n<li><strong><em>Ad\u0131m 5<\/em> <\/strong><em style=\"color:green;font-size:11px;\">Shared | <\/em><em style=\"color:purple;font-size:11px;\">Event ve Message&#8217;lar\u0131n Olu\u015fturulmas\u0131<\/em><br \/>\n\u015eimdi State Machine i\u00e7in gerekli event ve message&#8217;lar\u0131 i\u015flevsel a\u00e7\u0131dan kullan\u0131m s\u0131ras\u0131na uygun \u015fekilde olu\u015ftural\u0131m.<\/p>\n<p><span style=\"font-size:10px;color:gray;\">Event ile Message aras\u0131ndaki temel fark; event&#8217;ler olmu\u015f olaylar\u0131, message&#8217;lar ise olacak olaylar\u0131 temsil eder!<\/span><\/p>\n<p><em>Event&#8217;ler<\/em>;<\/p>\n<ol>\n<li><em style=\"color:green;\">OrderStartedEvent <span style=\"color:red;font-size:10px;\"><u>(Tetikleyici Event)<\/u><\/span><\/em><br \/>\nTetikleyici event&#8217;tir. Bir sipari\u015f s\u00fcrecinin ba\u015flad\u0131\u011f\u0131n\u0131 ifade eder. Haliyle tetikleyici oldu\u011fu i\u00e7in State Machine&#8217;de bir Order&#8217;a kar\u015f\u0131l\u0131k State Instance sat\u0131r\u0131 ekleyecektir. Dikkat ederseniz i\u00e7erisinde &#8216;OrderId&#8217;, &#8216;BuyerId&#8217; gibi State Instance&#8217;da belirtti\u011fimiz verilere kar\u015f\u0131l\u0131k property&#8217;ler mevcuttur. Haliyle bir sipari\u015f s\u00fcreci bu veriler e\u015fli\u011finde ba\u015flat\u0131lacakt\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderStartedEvent\r\n    {\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;OrderItemMessage&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/li>\n<li><em style=\"color:green;\">OrderCreatedEvent <span style=\"color:red;font-size:10px;\"><u>(Ba\u015far\u0131l\u0131 Event)<\/u><\/span><\/em><br \/>\nSipari\u015f olu\u015fturulduktan sonra <span style=\"color:purple;\">Stock.API<\/span>&#8216;\u0131 tetikleyebilmek i\u00e7in yay\u0131nlanacak event&#8217;tir.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderCreatedEvent : CorrelatedBy&lt;Guid&gt;\r\n    {\r\n        public OrderCreatedEvent(Guid correlationId)\r\n        {\r\n            CorrelationId = correlationId;\r\n        }\r\n        public Guid CorrelationId { get; }\r\n        public List&lt;OrderItemMessage&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<p>Burada dikkatinizi \u00e7ekmek istedi\u011fim iki husus mevcuttur. Bunlardan ilki, bu event&#8217;in art\u0131k hangi kullan\u0131c\u0131n\u0131n hangi sipari\u015fi i\u00e7in yay\u0131nland\u0131\u011f\u0131na dair bir veri ta\u015f\u0131mas\u0131na gerek yoktur! \u00c7\u00fcnk\u00fc bu veriler zaten State Machine&#8217;de tutulmaktad\u0131r. \u0130kinci husus ise, ilgili sipari\u015fin State Machine&#8217;de hangi korelasyon de\u011ferine sahip olan instance&#8217;a kar\u015f\u0131l\u0131k geldi\u011fini ifade edecek olan &#8216;CorrelationId&#8217; property&#8217;sini uygulayacak <code style=\"color:purple;font-size:13px;\">CorrelatedBy&lt;Guid&gt;<\/code> aray\u00fcz\u00fcd\u00fcr!<\/p>\n<p>Bu property&#8217;de ki korelasyon de\u011feri sayesinde tetikleyici event(OrderStartedEvent) geldikten sonra olu\u015fturulan State Instance hangisiyse onun \u00fczerinde state bilgisi de\u011fi\u015ftirilecektir.\n<\/li>\n<li><em style=\"color:green;\">OrderCompletedEvent <span style=\"color:red;font-size:10px;\"><u>(Ba\u015far\u0131l\u0131 Event)<\/u><\/span><\/em><br \/>\nSipari\u015fin ba\u015far\u0131yla sonland\u0131\u011f\u0131n\u0131 ifade eden event&#8217;tir. Bu event State Machine taraf\u0131ndan yay\u0131ld\u0131\u011f\u0131nda <span style=\"color:purple;\">Order.API<\/span> servisinde ilgili &#8216;OrderId&#8217;ye kar\u015f\u0131l\u0131k gelen sipari\u015fin durumu <span style=\"color:orange;\">Completed<\/span>&#8216;a \u00e7ekilecektir. Ayr\u0131ca dikkat ederseniz bu event&#8217;te herhangi bir korelasyon de\u011feri bulunmamaktad\u0131r. \u00c7\u00fcnk\u00fc bu event, sipari\u015fin sonland\u0131\u011f\u0131n\u0131 ifade etti\u011finden dolay\u0131 State Machine taraf\u0131ndan da bu event geldi\u011finde ilgili sipari\u015fe dair kay\u0131t sonland\u0131r\u0131lm\u0131\u015f(Finilize) olacakt\u0131r.<\/p>\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<\/li>\n<li><em style=\"color:green;\">OrderFailedEvent <span style=\"color:red;font-size:10px;\"><u>(Ba\u015far\u0131s\u0131z Event)<\/u><\/span><\/em><br \/>\nSipari\u015fin ba\u015far\u0131s\u0131z oldu\u011funu ifade eden event&#8217;tir. <span style=\"color:purple;\">Order.API<\/span> taraf\u0131ndan ilgili sipari\u015fin <span style=\"color:orange;\">Fail<\/span> durumuna \u00e7ekilmesini sa\u011flayacakt\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderFailedEvent\r\n    {\r\n        public int OrderId { get; set; }\r\n        public string Message { get; set; }\r\n    }\r\n<\/pre>\n<\/li>\n<li><em style=\"color:green;\">StockReservedEvent <span style=\"color:red;font-size:10px;\"><u>(Ba\u015far\u0131l\u0131 Event)<\/u><\/span><\/em><br \/>\n<span style=\"color:purple;\">Stock.API<\/span>&#8216;da ilgili stok i\u015flemlerinin ba\u015far\u0131yla ger\u00e7ekle\u015ftirildi\u011fini ifade eden event&#8217;tir. <code style=\"color:purple;font-size:13px;\">CorrelatedBy&lt;Guid&gt;<\/code> aray\u00fcz\u00fc sayesinde &#8216;CorrelationId&#8217; property&#8217;si uygulanarak State Machine&#8217;de hangi instance&#8217;\u0131n mevzu bahis oldu\u011funu ta\u015f\u0131maktad\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockReservedEvent : CorrelatedBy&lt;Guid&gt;\r\n    {\r\n        public StockReservedEvent(Guid correlationId)\r\n        {\r\n            CorrelationId = correlationId;\r\n        }\r\n        public Guid CorrelationId { get; }\r\n        public List&lt;OrderItemMessage&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/li>\n<li><em style=\"color:green;\">StockNotReservedEvent <span style=\"color:red;font-size:10px;\"><u>(Ba\u015far\u0131s\u0131z Event)<\/u><\/span><\/em><br \/>\nStok i\u015flemlerinin ba\u015far\u0131s\u0131z oldu\u011funu ifade eden event&#8217;tir.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockNotReservedEvent : CorrelatedBy&lt;Guid&gt;\r\n    {\r\n        public StockNotReservedEvent(Guid correlationId)\r\n        {\r\n            CorrelationId = correlationId;\r\n        }\r\n        public Guid CorrelationId { get; }\r\n        public string Message { get; set; }\r\n    }\r\n<\/pre>\n<\/li>\n<li><em style=\"color:green;\">PaymentStartedEvent <span style=\"color:red;font-size:10px;\"><u>(Ba\u015far\u0131l\u0131 Event)<\/u><\/span><\/em><br \/>\n<span style=\"color:purple;\">Stock.API<\/span>&#8216;da i\u015flemler ba\u015far\u0131l\u0131ysa e\u011fer <span style=\"color:purple;\">Payment.API<\/span>&#8216;\u0131 tetikletecek olan event&#8217;tir.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentStartedEvent : CorrelatedBy&lt;Guid&gt;\r\n    {\r\n        public PaymentStartedEvent(Guid correlationId)\r\n        {\r\n            CorrelationId = correlationId;\r\n        }\r\n        public Guid CorrelationId { get; }\r\n        public decimal TotalPrice { get; set; }\r\n        public List&lt;OrderItemMessage&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/li>\n<li><em style=\"color:green;\">PaymentCompletedEvent <span style=\"color:red;font-size:10px;\"><u>(Ba\u015far\u0131l\u0131 Event)<\/u><\/span><\/em><br \/>\n\u00d6deme i\u015fleminin ba\u015far\u0131l\u0131 oldu\u011funu ifade eden event&#8217;tir.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentCompletedEvent : CorrelatedBy&lt;Guid&gt;\r\n    {\r\n        public PaymentCompletedEvent(Guid correlationId)\r\n        {\r\n            CorrelationId = correlationId;\r\n        }\r\n        public Guid CorrelationId { get; }\r\n    }\r\n<\/pre>\n<\/li>\n<li><em style=\"color:green;\">PaymentFailedEvent <span style=\"color:red;font-size:10px;\"><u>(Ba\u015far\u0131s\u0131z Event)<\/u><\/span><\/em><br \/>\n\u00d6deme i\u015fleminin ba\u015far\u0131s\u0131z oldu\u011funu ifade eden event&#8217;tir.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentFailedEvent : CorrelatedBy&lt;Guid&gt;\r\n    {\r\n        public PaymentFailedEvent(Guid correlationId)\r\n        {\r\n            CorrelationId = correlationId;\r\n        }\r\n        public Guid CorrelationId { get; }\r\n        public List&lt;OrderItemMessage&gt; OrderItems { get; set; }\r\n        public string Message { get; set; }\r\n    }\r\n<\/pre>\n<\/li>\n<\/ol>\n<p><em>Message&#8217;lar<\/em>;<\/p>\n<ol>\n<li><em style=\"color:green;\">StockRollBackMessage <span style=\"color:red;font-size:10px;\"><u>(Compensable Event)<\/u><\/span><\/em><br \/>\n&#8216;PaymentFailedEvent&#8217; event&#8217;i yay\u0131nlan\u0131rsa e\u011fer <span style=\"color:purple;\">Stock.API<\/span>&#8216;da yap\u0131lan i\u015flemlerin geri al\u0131nmas\u0131n\u0131 sa\u011flayacak olan message&#8217;d\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockRollBackMessage\r\n    {\r\n        public List&lt;OrderItemMessage&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<\/li>\n<\/ol>\n<p>Bu arada yukar\u0131da kod aralar\u0131nda kullan\u0131lan &#8216;OrderItemMessage&#8217; nesnesinin i\u00e7eri\u011fi ise a\u015fa\u011f\u0131daki gibi olacakt\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderItemMessage\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<\/li>\n<li><strong><em>Ad\u0131m 6<\/em> <\/strong><em style=\"color:green;font-size:11px;\">Shared | <\/em><em style=\"color:purple;font-size:11px;\">Kuyruk \u0130simlerinin Ayarlanmas\u0131<\/em><br \/>\n\u015eimdi s\u0131rada servislerimizle State Machine aras\u0131ndaki ileti\u015fimleri sa\u011flayacak olan kuyruk isimlerinin ayarlanmas\u0131 var. Bunun i\u00e7in <span style=\"color:purple;\">Shared<\/span> class library&#8217;sinde &#8216;RabbitMQSettings.cs&#8217; isminde bir static class olu\u015ftural\u0131m ve i\u00e7erisini a\u015fa\u011f\u0131daki gibi doldural\u0131m.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public static class RabbitMQSettings\r\n    {\r\n        public const string StateMachine = &quot;state-machine-queue&quot;;\r\n        public const string Stock_OrderCreatedEventQueue = &quot;stock-order-created-queue&quot;;\r\n        public const string Payment_StartedEventQueue = &quot;payment-started-queue&quot;;\r\n        public const string Order_OrderCompletedEventQueue = &quot;order-order-completed-queue&quot;;\r\n        public const string Order_OrderFailedEventQueue = &quot;order-order-failed-queue&quot;;\r\n        public const string Stock_RollbackMessageQueue = &quot;stock-roolback-queue&quot;;\r\n    }\r\n<\/pre>\n<p>Yukar\u0131daki kuyruklar\u0131 tan\u0131mlad\u0131\u011f\u0131m\u0131z kodlara g\u00f6z atarsan\u0131z e\u011fer hususi bir standart belirledi\u011fimizi g\u00f6rebileceksiniz. Burada standard\u0131m\u0131z g\u00f6nderilecek event ya da message&#8217;\u0131n hangi servis taraf\u0131ndan t\u00fcketilece\u011fi ile ili\u015fkilendirilmektedir! \u015e\u00f6yle ki; <span style=\"color:orange;font-size:10px;\">[Consumer]_[Event\/MessageName]Queue<\/span>\n<\/li>\n<li><strong><em>Ad\u0131m 7<\/em> <\/strong><em style=\"color:green;font-size:11px;\">Order.API | <\/em><em style=\"color:purple;font-size:11px;\">Sipari\u015fin Kuyru\u011fa At\u0131lmas\u0131 ve B\u00f6ylece Tetikleyici Event&#8217;in Yay\u0131lmas\u0131<\/em><br \/>\nHat\u0131rlarsan\u0131z e\u011fer 2. ad\u0131mda <span style=\"color:purple;\">Order.API<\/span> servisinde &#8216;OrdersController.cs&#8217; controller&#8217;\u0131 \u00fczerinde bir sipari\u015f olu\u015fturmu\u015ftuk. \u0130\u015fte \u015fimdi olu\u015fturulan o sipari\u015fi State Machine kuyru\u011funa g\u00f6ndermemiz gerekmektedir. Bunun i\u00e7in ilgili controller i\u00e7erisindeki &#8216;CreateOrder&#8217; action metodu i\u00e7erisinde a\u015fa\u011f\u0131daki eklentilerin yap\u0131lmas\u0131 gerekmektedir;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    &#x5B;Route(&quot;api\/&#x5B;controller]&quot;)]\r\n    &#x5B;ApiController]\r\n    public class OrdersController : ControllerBase\r\n    {\r\n        readonly ApplicationDbContext _applicationDbContext;\r\n        readonly ISendEndpointProvider _sendEndpointProvider;\r\n        public OrdersController(ApplicationDbContext applicationDbContext,\r\n            ISendEndpointProvider sendEndpointProvider)\r\n        {\r\n            _applicationDbContext = applicationDbContext;\r\n            _sendEndpointProvider = sendEndpointProvider;\r\n        }\r\n        &#x5B;HttpPost]\r\n        public async Task&lt;IActionResult&gt; CreateOrder(OrderVM model)\r\n        {\r\n            Order.API.Models.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 _applicationDbContext.AddAsync&lt;Order.API.Models.Order&gt;(order);\r\n\r\n            await _applicationDbContext.SaveChangesAsync();\r\n\r\n            OrderStartedEvent orderStartedEvent = new()\r\n            {\r\n                BuyerId = model.BuyerId,\r\n                OrderId = order.Id,\r\n                TotalPrice = model.OrderItems.Sum(oi =&gt; oi.Count * oi.Price),\r\n                OrderItems = model.OrderItems.Select(oi =&gt; new Shared.OrderItemMessage\r\n                {\r\n                    Price = oi.Price,\r\n                    Count = oi.Count,\r\n                    ProductId = oi.ProductId\r\n                }).ToList()\r\n            };\r\n\r\n            ISendEndpoint sendEndpoint = await _sendEndpointProvider.GetSendEndpoint(new($&quot;queue:{RabbitMQSettings.StateMachine}&quot;));\r\n            await sendEndpoint.Send&lt;OrderStartedEvent&gt;(orderStartedEvent);\r\n            return Ok(true);\r\n        }\r\n    }\r\n<\/pre>\n<p>34 ile 48. sat\u0131r aral\u0131\u011f\u0131na g\u00f6z atarsan\u0131z e\u011fer sonradan in\u015fa etti\u011fimiz &#8216;OrderStartedEvent&#8217; nesnesi sayesinde bir tetikleyici event olu\u015fturulmakta ve bu state machine kuyru\u011funa g\u00f6nderilmektedir. Art\u0131k state machine ile bu ve di\u011fer event&#8217;lerin kontrol\u00fcn\u00fc sa\u011flayarak sipari\u015fin durumu hakk\u0131nda distributed transaction&#8217;\u0131 sa\u011flayabiliriz.\n<\/li>\n<li><strong><em>Ad\u0131m 8<\/em> <\/strong><em style=\"color:green;font-size:11px;\">SagaStateMachine.Service | <\/em><em style=\"color:purple;font-size:11px;\">State Machine&#8217;de Event&#8217;lerin Tan\u0131mlanmas\u0131<\/em><br \/>\nEvet&#8230; \u015eimdi en zor ve kritik noktaya gelmi\u015f bulunmaktay\u0131z. Art\u0131k State Machine&#8217;imiz \u00fczerinde gerekli geli\u015ftirmeyi sa\u011flayacak ve uygulama state&#8217;ini b\u00fct\u00fcnsel olarak y\u00f6netiyor olaca\u011f\u0131z. Bunun i\u00e7in &#8216;OrderStateMachine&#8217; s\u0131n\u0131f\u0131n\u0131 a\u00e7al\u0131m ve ilk olarak tetikleyici event geldi\u011finde yeni bir State Instance nesnesi olu\u015fturmas\u0131 gerekti\u011fini ifade edelim.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderStateMachine : MassTransitStateMachine&lt;OrderStateInstance&gt;\r\n    {\r\n        public Event&lt;OrderStartedEvent&gt; OrderStartedEvent { get; set; }\r\n\r\n        public OrderStateMachine()\r\n        {\r\n            \/\/State Instance'da ki hangi property'nin sipari\u015f s\u00fcrecindeki state'i tutaca\u011f\u0131 bildiriliyor.\r\n            \/\/Yani art\u0131k t\u00fcm event'ler CurrentState property'sin de tutulacakt\u0131r!\r\n            InstanceState(instance =&gt; instance.CurrentState);\r\n\r\n            \/\/E\u011fer gelen event OrderStartedEvent ise CorrelateBy metodu ile veritaban\u0131nda(database)\r\n            \/\/tutulan Order State Instance'da ki OrderId'si ile gelen event'te ki(@event) OrderId'yi\r\n            \/\/k\u0131yasla. Bu k\u0131yas neticesinde e\u011fer ilgili instance varsa kaydetme. Yani yeni bir korelasyon\r\n            \/\/\u00fcretme! Yok e\u011fer yoksa yeni bir korelasyon \u00fcret(SelectId)\r\n            Event(() =&gt; OrderStartedEvent,\r\n                orderStateInstance =&gt;\r\n                orderStateInstance.CorrelateBy&lt;int&gt;(database =&gt; database.OrderId, @event =&gt; @event.Message.OrderId)\r\n                .SelectId(e =&gt; Guid.NewGuid()));\r\n        }\r\n    }\r\n<\/pre>\n<p>State Machine \u00fczerinde bir event&#8217;i temsil edebilmek i\u00e7in 3. sat\u0131rdaki gibi <code style=\"color:purple;font-size:13px;\">Event&lt;T&gt;<\/code> t\u00fcr\u00fcnden bir property olu\u015fturulmal\u0131d\u0131r. Bunu t\u00fcm event&#8217;ler i\u00e7in yapaca\u011f\u0131z. \u015eimdilik sembolik olarak &#8216;OrderStartedEvent&#8217; i\u00e7in yapm\u0131\u015f olduk. Aceleye gerek yok \ud83d\ude42 Ad\u0131m ad\u0131m hepsi gelecek&#8230;<\/p>\n<p>Ard\u0131ndan 11. sat\u0131ra g\u00f6z atarsan\u0131z e\u011fer &#8216;Event&#8217; metodu ile gelen event&#8217;in &#8216;OrderStartedEvent&#8217; olmas\u0131 durumunda ne yapaca\u011f\u0131n\u0131 belirliyoruz. Evet, &#8216;Event&#8217; metodu ise gelen event&#8217;e g\u00f6re aksiyon almam\u0131z\u0131 sa\u011flayan bir fonksiyondur. Burada gelen event &#8216;OrderStartedEvent&#8217; ise State Machine veritaban\u0131ndaki(database) &#8216;OrderId&#8217; de\u011feri ile event&#8217;te(OrderStartedEvent) gelen &#8216;OrderId&#8217; de\u011ferini k\u0131yasl\u0131yoruz. E\u011fer bu k\u0131yaslama neticesinde herhangi bir State Instance geliyorsa bu yeni bir sipari\u015f de\u011fildir! Haliyle kaydetmeyece\u011fiz. Amma velakin bu gelen event bir tetikleyici yani ba\u015flat\u0131c\u0131 bir event oldu\u011fu i\u00e7in yeni bir State Instance \u00fcretilecektir. Haliyle yeni bir korelasyon de\u011feri olan ve bundan sonraki ayn\u0131 korelasyon de\u011ferine sahip olan event&#8217;lerin durum bilgisini tutacak olan state instance eklenecektir. Bu yeni state instance ekleme i\u015fleminden de &#8216;SelectId&#8217; metodu sorumludur.<\/p>\n<p>\u015eimdi di\u011fer event&#8217;lerin hepsini ekleyelim;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderStateMachine : MassTransitStateMachine&lt;OrderStateInstance&gt;\r\n    {\r\n        public Event&lt;OrderStartedEvent&gt; OrderStartedEvent { get; set; }\r\n        public Event&lt;StockReservedEvent&gt; StockReservedEvent { get; set; }\r\n        public Event&lt;PaymentCompletedEvent&gt; PaymentCompletedEvent { get; set; }\r\n        public Event&lt;PaymentFailedEvent&gt; PaymentFailedEvent { get; set; }\r\n        public Event&lt;StockNotReservedEvent&gt; StockNotReservedEvent { get; set; }\r\n\r\n        public OrderStateMachine()\r\n        {\r\n            \/\/State Instance'da ki hangi property'nin sipari\u015f s\u00fcrecindeki state'i tutaca\u011f\u0131 bildiriliyor.\r\n            \/\/Yani art\u0131k t\u00fcm event'ler CurrentState property'sin de tutulacakt\u0131r!\r\n            InstanceState(instance =&gt; instance.CurrentState);\r\n\r\n            \/\/E\u011fer gelen event OrderStartedEvent ise CorrelateBy metodu ile veritaban\u0131nda(database)\r\n            \/\/tutulan Order State Instance'da ki OrderId'si ile gelen event'te ki(@event) OrderId'yi\r\n            \/\/k\u0131yasla. Bu k\u0131yas neticesinde e\u011fer ilgili instance varsa kaydetme. Yani yeni bir korelasyon\r\n            \/\/\u00fcretme! Yok e\u011fer yoksa yeni bir korelasyon \u00fcret(SelectId)\r\n            Event(() =&gt; OrderStartedEvent,\r\n                orderStateInstance =&gt;\r\n                orderStateInstance.CorrelateBy&lt;int&gt;(database =&gt; database.OrderId, @event =&gt; @event.Message.OrderId)\r\n                .SelectId(e =&gt; Guid.NewGuid()));\r\n\r\n            \/\/StockReservedEvent f\u0131rlat\u0131ld\u0131\u011f\u0131nda veritaban\u0131ndaki hangi correlationid de\u011ferine sahip state\r\n            \/\/instance'\u0131n state'ini de\u011fi\u015ftirecek bunu belirtmi\u015f olduk!\r\n            Event(() =&gt; StockReservedEvent,\r\n                orderStateInstance =&gt;\r\n                orderStateInstance.CorrelateById(@event =&gt; @event.Message.CorrelationId));\r\n\r\n            \/\/StockNotReservedEvent f\u0131rlat\u0131ld\u0131\u011f\u0131nda veritaban\u0131ndaki hangi correlationid de\u011ferine sahip state\r\n            \/\/instance'\u0131n state'ini de\u011fi\u015ftirecek bunu belirtmi\u015f olduk!\r\n            Event(() =&gt; StockNotReservedEvent,\r\n                orderStateInstance =&gt;\r\n                orderStateInstance.CorrelateById(@event =&gt; @event.Message.CorrelationId));\r\n\r\n            \/\/PaymentCompletedEvent f\u0131rlat\u0131ld\u0131\u011f\u0131nda veritaban\u0131ndaki hangi correlationid de\u011ferine sahip state\r\n            \/\/instance'\u0131n state'ini de\u011fi\u015ftirecek bunu belirtmi\u015f olduk!\r\n            Event(() =&gt; PaymentCompletedEvent,\r\n                orderStateInstance =&gt;\r\n                orderStateInstance.CorrelateById(@event =&gt; @event.Message.CorrelationId));\r\n\r\n            \/\/PaymentFailedEvent f\u0131rlat\u0131ld\u0131\u011f\u0131nda veritaban\u0131ndaki hangi correlationid de\u011ferine sahip state\r\n            \/\/instance'\u0131n state'ini de\u011fi\u015ftirecek bunu belirtmi\u015f olduk!\r\n            Event(() =&gt; PaymentFailedEvent,\r\n                orderStateInstance =&gt;\r\n                orderStateInstance.CorrelateById(@event =&gt; @event.Message.CorrelationId));\r\n        }\r\n    }\r\n<\/pre>\n<p>Dikkat ederseniz tetikleyici event d\u0131\u015f\u0131ndaki t\u00fcm event&#8217;ler ta\u015f\u0131d\u0131klar\u0131 korelasyon de\u011feri ile e\u015fle\u015fen veritaban\u0131ndaki State Instance sat\u0131r\u0131 \u00fczerinde i\u015flem ger\u00e7ekle\u015ftirmektedir\/ger\u00e7ekle\u015ftirecektir. \u00c7\u00fcnk\u00fc State Instance olu\u015fturmak sade ve sadece tetikleyici event&#8217;in sorumlulu\u011fundad\u0131r. Di\u011fer event&#8217;ler art\u0131k bu olu\u015fturulmu\u015f state instance \u00fczerinde durum bilgisinin de\u011fi\u015fmesini sa\u011flamaktad\u0131rlar.<\/p>\n<p>Ayr\u0131ca 5. ad\u0131mda olu\u015fturulan t\u00fcm event&#8217;lerin tan\u0131mlanmad\u0131\u011f\u0131na dikkatinizi \u00e7ekerim. Nihayetinde State Machine&#8217;e gelecek olan event&#8217;ler sadece bunlar olacakt\u0131r. Di\u011ferleri ise State Machine taraf\u0131ndan Publish yahut Send edilecek event&#8217;lerdir. Buradaki temel ayr\u0131m budur. State Machine taraf\u0131ndan consume edilecek event&#8217;ler yukar\u0131daki gibi tan\u0131mlan\u0131rken, g\u00f6nderilecek event&#8217;ler tan\u0131mlanmazlar!\n<\/li>\n<li><strong><em>Ad\u0131m 9<\/em> <\/strong><em style=\"color:green;font-size:11px;\">SagaStateMachine.Service | <\/em><em style=\"color:purple;font-size:11px;\">State Machine&#8217;de State&#8217;lerin Tan\u0131mlanmas\u0131<\/em><br \/>\n\u015eimdi ise State Machine&#8217;de durumlar\u0131n(state) tan\u0131mlanmas\u0131n\u0131 sa\u011flayal\u0131m. Bunun i\u00e7in yine <span style=\"color:purple;\">SagaStateMachine.Service<\/span> i\u00e7erisindeki &#8216;OrderStateMachine&#8217; s\u0131n\u0131f\u0131n\u0131 a\u00e7al\u0131m ve a\u015fa\u011f\u0131daki eklentilerde bulunal\u0131m.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderStateMachine : MassTransitStateMachine&lt;OrderStateInstance&gt;\r\n    {\r\n        .\r\n        .\r\n        .\r\n        public State OrderCreated { get; set; }\r\n        public State StockReserved { get; set; }\r\n        public State PaymentCompleted { get; set; }\r\n        public State PaymentFailed { get; set; }\r\n        public State StockNotReserved { get; set; }\r\n\r\n        public OrderStateMachine()\r\n        {\r\n            InstanceState(instance =&gt; instance.CurrentState);\r\n\r\n            .\r\n            . Event(..)\r\n            .\r\n        }\r\n    }\r\n<\/pre>\n<p>Yukar\u0131daki kod blo\u011funa bakarsan\u0131z \u00f6nceden yap\u0131lm\u0131\u015f &#8216;Event&#8217; tan\u0131mlamalar\u0131n\u0131 her ne kadar yans\u0131tmasakta onlarla birlikte o anki state&#8217;lerin durumunu ifade edecek olan &#8216;State&#8217; property&#8217;leri tan\u0131mlanm\u0131\u015ft\u0131r. Bu property&#8217;ler State Machine taraf\u0131ndan kullan\u0131lacak durumlar\u0131 ifade etmektedir.<\/p>\n<p>Bu state&#8217;leri a\u015fa\u011f\u0131daki gibi kullanmakta ve gerekli kontrolleri sa\u011flayabilmekteyiz.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderStateMachine : MassTransitStateMachine&lt;OrderStateInstance&gt;\r\n    {\r\n        .\r\n        .\r\n        .\r\n\r\n        public State OrderCreated { get; set; }\r\n        public State StockReserved { get; set; }\r\n        public State PaymentCompleted { get; set; }\r\n        public State PaymentFailed { get; set; }\r\n        public State StockNotReserved { get; set; }\r\n        public OrderStateMachine()\r\n        {\r\n            InstanceState(instance =&gt; instance.CurrentState);\r\n\r\n            .\r\n            . Event(..)\r\n            .\r\n\r\n            \/\/\u0130lgili instance'\u0131n state'i initial\/ba\u015flang\u0131\u00e7 a\u015famas\u0131ndayken(Initially) 'OrderStartedEvent'\r\n            \/\/tetikleyici event'i geldiyse(When) \u015fu i\u015flemleri yap(Then). Ard\u0131ndan bu i\u015flemler yap\u0131ld\u0131ktan\r\n            \/\/sonra ilgili instance'\u0131 'OrderCreated' state'ine ge\u00e7ir(TransitionTo). Ard\u0131ndan 'Stock.API'\u0131\r\n            \/\/tetikleyebilmek\/haberdar edebilmek i\u00e7in 'OrderCreatedEvent' event'ini g\u00f6nder(Publish\/Send)\r\n            Initially(When(OrderStartedEvent)\r\n                .Then(context =&gt;\r\n                {\r\n                    context.Instance.BuyerId = context.Data.BuyerId;\r\n                    context.Instance.OrderId = context.Data.OrderId;\r\n                    context.Instance.TotalPrice = context.Data.TotalPrice;\r\n                    context.Instance.CreatedDate = DateTime.Now;\r\n                })\r\n                .Then(context =&gt; Console.WriteLine(&quot;Ara i\u015flem 1&quot;))\r\n                .Then(context =&gt; Console.WriteLine(&quot;Ara i\u015flem 2&quot;))\r\n                .TransitionTo(OrderCreated)\r\n                .Then(context =&gt; Console.WriteLine(&quot;Ara i\u015flem 3&quot;))\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Stock_OrderCreatedEventQueue}&quot;), context =&gt; new OrderCreatedEvent(context.Instance.CorrelationId)\r\n                {\r\n                    OrderItems = context.Data.OrderItems\r\n                }));\r\n\r\n            \/\/E\u011fer state 'OrderCreated' ise(During) ve o anda 'StockReservedEvent' event'i geldiyse(When)\r\n            \/\/o zaman state'i 'StockReserved' olarak de\u011fi\u015ftir(TransitionTo) ve belirtilen kuyru\u011fa \r\n            \/\/'PaymentStartedEvent' event'ini g\u00f6nder(Send)\r\n            During(OrderCreated,\r\n                When(StockReservedEvent)\r\n                .TransitionTo(StockReserved)\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Payment_StartedEventQueue}&quot;), context =&gt; new PaymentStartedEvent(context.Instance.CorrelationId)\r\n                {\r\n                    OrderItems = context.Data.OrderItems,\r\n                    TotalPrice = context.Instance.TotalPrice\r\n                }),\r\n\r\n                \/\/Yok e\u011fer State 'OrderCreated' iken(During) 'StockNotReservedEvent' event'i geldiyse(When)\r\n                \/\/o zaman state'i 'StockNotReserved' olarak de\u011fi\u015ftir(TransitionTo) ve belirtilen\r\n                \/\/kuyru\u011fa 'OrderFailedEvent' event'ini g\u00f6nder.\r\n                When(StockNotReservedEvent)\r\n                .TransitionTo(StockNotReserved)\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Order_OrderFailedEventQueue}&quot;), context =&gt; new OrderFailedEvent()\r\n                {\r\n                    OrderId = context.Instance.OrderId,\r\n                    Message = context.Data.Message\r\n                }));\r\n\r\n            \/\/E\u011fer ilgili sipari\u015f 'StockReserved' durumunda iken(During) 'PaymentCompletedEvent' event'i geldiyse(When)\r\n            \/\/'PaymentCompleted' state'i olarak de\u011fi\u015ftir(TransitionTo) ve ard\u0131ndan belirtilen kuyru\u011fa \r\n            \/\/'OrderCompletedEvent' event'ini g\u00f6nder. Ayr\u0131ca art\u0131k bu sipari\u015f ba\u015far\u0131l\u0131 olaca\u011f\u0131ndan dolay\u0131\r\n            \/\/State Machine taraf\u0131ndan bu State Instance'\u0131 ba\u015far\u0131yla sonland\u0131r(Finalize) Haliyle b\u00f6ylece sonu\u00e7 olarak\r\n            \/\/ilgili instance'\u0131n state'inde 'Final' yazacakt\u0131r!\r\n\r\n            During(StockReserved,\r\n                When(PaymentCompletedEvent)\r\n                .TransitionTo(PaymentCompleted)\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Order_OrderCompletedEventQueue}&quot;), context =&gt; new OrderCompletedEvent\r\n                {\r\n                    OrderId = context.Instance.OrderId\r\n                })\r\n                .Finalize(),\r\n\r\n                \/\/Yok e\u011fer mevcut state 'StockReserved' iken(During) 'PaymentFailedEvent' event'i gelirse(When)\r\n                \/\/o zaman state'i 'PaymentFailed' olarak de\u011fi\u015ftir(TransitionTo) ve belirtilen kuyruklara \r\n                \/\/'OrderFailedEvent' ve 'StockRollBackMessage' event'lerini g\u00f6nder(Send).\r\n                When(PaymentFailedEvent)\r\n                .TransitionTo(PaymentFailed)\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Order_OrderFailedEventQueue}&quot;), context =&gt; new OrderFailedEvent()\r\n                {\r\n                    OrderId = context.Instance.OrderId,\r\n                    Message = context.Data.Message\r\n                })\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Stock_RollbackMessageQueue}&quot;), context =&gt; new StockRollBackMessage\r\n                {\r\n                    OrderItems = context.Data.OrderItems\r\n                }));\r\n\r\n            \/\/Finalize olan instance'lar\u0131 veritaban\u0131ndan kald\u0131r\u0131yoruz!\r\n            SetCompletedWhenFinalized();\r\n        }\r\n    }\r\n<\/pre>\n<p>\u015eimdi yukar\u0131daki kod blo\u011funu sat\u0131r sat\u0131r izah etmemiz gerekirse e\u011fer ilk olarak 24 ile 39. sat\u0131r aral\u0131\u011f\u0131na g\u00f6z atmam\u0131z gerekmektedir. Tetikleyici event geldi\u011finde State Machine&#8217;de ilk kar\u015f\u0131lay\u0131c\u0131 state <code style=\"color:purple;font-size:13px;\">Initially<\/code> fonksiyonu taraf\u0131ndan tan\u0131mlanm\u0131\u015f olan &#8216;Initial&#8217; olacakt\u0131r. Burada <code style=\"color:purple;font-size:13px;\">When<\/code> fonksiyonu ile o anki gelen event &#8216;OrderStartedEvent&#8217; ise <code style=\"color:purple;font-size:13px;\">Then<\/code> fonksiyonu i\u00e7erisindeki i\u015flemleri yapt\u0131r\u0131yoruz. <code style=\"color:purple;font-size:13px;\">Then<\/code> fonksiyonuna bakarsan\u0131z e\u011fer olu\u015fturulacak olan State Instance&#8217;\u0131n hangi property&#8217;sine tetikleyici event&#8217;te gelen hangi property&#8217;lerin atanaca\u011f\u0131 belirtilmektedir. Zaten \u00f6rnek mahiyetinde olmas\u0131 i\u00e7in ara i\u015flem olarak ifade edilen <code style=\"color:purple;font-size:13px;\">Then<\/code> fonksiyonlar\u0131 ile araya girip farkl\u0131 i\u015flemler yapabildi\u011fimizi g\u00f6stermekteyim. Devam\u0131nda ise bu i\u015flemlerden sonra <code style=\"color:purple;font-size:13px;\">TransitionTo<\/code> fonksiyonu arac\u0131l\u0131\u011f\u0131yla ilgili sipari\u015fe ait instance&#8217;\u0131n durumunu &#8216;OrderCreated&#8217;a \u00e7ekiyoruz ve ard\u0131ndan <code style=\"color:purple;font-size:13px;\">Send<\/code> fonksiyonu ile <span style=\"color:purple;\">Stock.API<\/span>&#8216;a &#8216;OrderCreatedEvent&#8217;ini g\u00f6ndererek haber veriyoruz.<\/p>\n<p>Di\u011fer sat\u0131rlara bakmadan \u00f6nce burada ayd\u0131nlatmam\u0131z gereken bir kod mant\u0131\u011f\u0131 vard\u0131r. O da <code style=\"color:purple;font-size:13px;\">context.Instance<\/code> ile <code style=\"color:purple;font-size:13px;\">context.Data<\/code> aras\u0131ndaki farkt\u0131r. <code style=\"color:purple;font-size:13px;\">context.Instance<\/code>, veritaban\u0131ndaki ilgili sipari\u015fe kar\u015f\u0131l\u0131k gelen instance sat\u0131r\u0131n\u0131 temsil etmektedir. <code style=\"color:purple;font-size:13px;\">context.Data<\/code> ise o anki ilgili event&#8217;tan gelen datay\u0131 temsil eder.<\/p>\n<p>Devam edersek e\u011fer 44 ile 62. sat\u0131r aral\u0131\u011f\u0131n\u0131 ele alal\u0131m. Burada ise art\u0131k tetikleyici event&#8217;ten sonraki durumlar\u0131 kontrol etti\u011fimiz <code style=\"color:purple;font-size:13px;\">During<\/code> fonksiyonu ile o anki durumun &#8216;OrderCreated&#8217; olup olmad\u0131\u011f\u0131n\u0131 denetliyoruz. E\u011fer &#8216;OrderCreated&#8217; ise ve gelen event &#8216;StockReservedEvent&#8217; ise(<code style=\"color:purple;font-size:13px;\">When<\/code>) durumu &#8216;StockReserved&#8217; olarak g\u00fcncelliyoruz(<code style=\"color:purple;font-size:13px;\">TransitionTo<\/code>) ve <span style=\"color:purple;\">Payment.API<\/span>&#8216;a &#8216;PaymentStartedEvent&#8217;i g\u00f6nderiyoruz(<code style=\"color:purple;font-size:13px;\">Send<\/code>). <\/p>\n<p>56. sat\u0131rda ise gelen event&#8217;in &#8216;StockNotReservedEvent&#8217; olma durumuna istinaden(<code style=\"color:purple;font-size:13px;\">When<\/code>) state&#8217;i &#8216;StockNotReserved&#8217; olarak g\u00fcncelliyoruz(<code style=\"color:purple;font-size:13px;\">TransitionTo<\/code>) ve bu sefer de bu sipari\u015fin ba\u015far\u0131s\u0131z oldu\u011funu haber edebilmek i\u00e7in <span style=\"color:purple;\">Order.API<\/span>&#8216;\u0131 &#8216;OrderFailedEvent&#8217; ile uyar\u0131yoruz(<code style=\"color:purple;font-size:13px;\">Send<\/code>).<\/p>\n<p>70 ile 92. sat\u0131r aral\u0131\u011f\u0131na bakarsak e\u011fer yine benzer mant\u0131kta &#8216;StockReserverd&#8217; durumunda iken(<code style=\"color:purple;font-size:13px;\">During<\/code>) &#8216;PaymentCompletedEvent&#8217; geliyorsa e\u011fer(<code style=\"color:purple;font-size:13px;\">When<\/code>) durumu &#8216;PaymentCompleted&#8217;e \u00e7ekiyoruz(<code style=\"color:purple;font-size:13px;\">TransitionTo<\/code>) ve ard\u0131ndan sipari\u015fin ba\u015far\u0131yla tamamland\u0131\u011f\u0131na dair <span style=\"color:purple;\">Order.API<\/span>&#8216;a &#8216;OrderCompletedEvent&#8217; ile haber veriyoruz. Tabi burada \u00f6nemli olan husus sipari\u015f tamamland\u0131ysa bunu <code style=\"color:purple;font-size:13px;\">Finalize<\/code> ile bildiriyor olmam\u0131zd\u0131r.<\/p>\n<p>82. sat\u0131rda ise yine ayn\u0131 durumda &#8216;PaymentFailedEvent&#8217; geliyor ise(<code style=\"color:purple;font-size:13px;\">When<\/code>) durumu &#8216;PaymentFailed&#8217;e \u00e7ekiyoruz(<code style=\"color:purple;font-size:13px;\">TransitionTo<\/code>) ve sipari\u015fte bir hata oldu\u011funa dair bir yandan <span style=\"color:purple;\">Order.API<\/span>&#8216;a &#8216;OrderFailedEvent&#8217; bir yandan da <span style=\"color:purple;\">Stock.API<\/span>&#8216;a ise &#8216;StockRollBackMessage&#8217; event&#8217;leri g\u00f6nderiliyor.<\/p>\n<p>Son olarak finalize olan instance&#8217;lar\u0131 veritaban\u0131nda tutman\u0131n l\u00fczumu olmad\u0131\u011f\u0131 i\u00e7in <code style=\"color:purple;font-size:13px;\">SetCompletedWhenFinalized<\/code> fonksiyonu ile instance&#8217;\u0131 fiziksel olarak veritaban\u0131ndan kald\u0131r\u0131yoruz.<\/p>\n<p>Tabi burada yap\u0131lan \u00e7al\u0131\u015fmalar neticesinde State Machine&#8217;in en son halini payla\u015fmam\u0131z gerekirse e\u011fer a\u015fa\u011f\u0131daki gibi olacakt\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderStateMachine : MassTransitStateMachine&lt;OrderStateInstance&gt;\r\n    {\r\n        public Event&lt;OrderStartedEvent&gt; OrderStartedEvent { get; set; }\r\n        public Event&lt;StockReservedEvent&gt; StockReservedEvent { get; set; }\r\n        public Event&lt;PaymentCompletedEvent&gt; PaymentCompletedEvent { get; set; }\r\n        public Event&lt;PaymentFailedEvent&gt; PaymentFailedEvent { get; set; }\r\n        public Event&lt;StockNotReservedEvent&gt; StockNotReservedEvent { get; set; }\r\n\r\n\r\n        public State OrderCreated { get; set; }\r\n        public State StockReserved { get; set; }\r\n        public State PaymentCompleted { get; set; }\r\n        public State PaymentFailed { get; set; }\r\n        public State StockNotReserved { get; set; }\r\n        public OrderStateMachine()\r\n        {\r\n            \/\/State Instance'da ki hangi property'nin sipari\u015f s\u00fcrecindeki state'i tutaca\u011f\u0131 bildiriliyor.\r\n            \/\/Yani art\u0131k t\u00fcm event'ler CurrentState property'sin de tutulacakt\u0131r!\r\n            InstanceState(instance =&gt; instance.CurrentState);\r\n\r\n            \/\/E\u011fer gelen event OrderStartedEvent ise CorrelateBy metodu ile veritaban\u0131nda(database)\r\n            \/\/tutulan Order State Instance'da ki OrderId'si ile gelen event'te ki(@event) OrderId'yi\r\n            \/\/k\u0131yasla. Bu k\u0131yas neticesinde e\u011fer ilgili instance varsa kaydetme. Yani yeni bir korelasyon\r\n            \/\/\u00fcretme! Yok e\u011fer yoksa yeni bir korelasyon \u00fcret(SelectId)\r\n            Event(() =&gt; OrderStartedEvent,\r\n                orderStateInstance =&gt;\r\n                orderStateInstance.CorrelateBy&lt;int&gt;(database =&gt; database.OrderId, @event =&gt; @event.Message.OrderId)\r\n                .SelectId(e =&gt; Guid.NewGuid()));\r\n\r\n            \/\/StockReservedEvent f\u0131rlat\u0131ld\u0131\u011f\u0131nda veritaban\u0131ndaki hangi correlationid de\u011ferine sahip state\r\n            \/\/instance'\u0131n state'ini de\u011fi\u015ftirecek bunu belirtmi\u015f olduk!\r\n            Event(() =&gt; StockReservedEvent,\r\n                orderStateInstance =&gt;\r\n                orderStateInstance.CorrelateById(@event =&gt; @event.Message.CorrelationId));\r\n\r\n            \/\/StockNotReservedEvent f\u0131rlat\u0131ld\u0131\u011f\u0131nda veritaban\u0131ndaki hangi correlationid de\u011ferine sahip state\r\n            \/\/instance'\u0131n state'ini de\u011fi\u015ftirecek bunu belirtmi\u015f olduk!\r\n            Event(() =&gt; StockNotReservedEvent,\r\n                orderStateInstance =&gt;\r\n                orderStateInstance.CorrelateById(@event =&gt; @event.Message.CorrelationId));\r\n\r\n            \/\/PaymentCompletedEvent f\u0131rlat\u0131ld\u0131\u011f\u0131nda veritaban\u0131ndaki hangi correlationid de\u011ferine sahip state\r\n            \/\/instance'\u0131n state'ini de\u011fi\u015ftirecek bunu belirtmi\u015f olduk!\r\n            Event(() =&gt; PaymentCompletedEvent,\r\n                orderStateInstance =&gt;\r\n                orderStateInstance.CorrelateById(@event =&gt; @event.Message.CorrelationId));\r\n\r\n            \/\/PaymentFailedEvent f\u0131rlat\u0131ld\u0131\u011f\u0131nda veritaban\u0131ndaki hangi correlationid de\u011ferine sahip state\r\n            \/\/instance'\u0131n state'ini de\u011fi\u015ftirecek bunu belirtmi\u015f olduk!\r\n            Event(() =&gt; PaymentFailedEvent,\r\n                orderStateInstance =&gt;\r\n                orderStateInstance.CorrelateById(@event =&gt; @event.Message.CorrelationId));\r\n\r\n            \/\/\u0130lgili instance'\u0131n state'i initial\/ba\u015flang\u0131\u00e7 a\u015famas\u0131ndayken(Initially) 'OrderStartedEvent'\r\n            \/\/tetikleyici event'i geldiyse(When) \u015fu i\u015flemleri yap(Then). Ard\u0131ndan bu i\u015flemler yap\u0131ld\u0131ktan\r\n            \/\/sonra ilgili instance'\u0131 'OrderCreated' state'ine ge\u00e7ir(TransitionTo). Ard\u0131ndan 'Stock.API'\u0131\r\n            \/\/tetikleyebilmek\/haberdar edebilmek i\u00e7in 'OrderCreatedEvent' event'ini g\u00f6nder(Publish\/Send)\r\n            Initially(When(OrderStartedEvent)\r\n                .Then(context =&gt;\r\n                {\r\n                    context.Instance.BuyerId = context.Data.BuyerId;\r\n                    context.Instance.OrderId = context.Data.OrderId;\r\n                    context.Instance.TotalPrice = context.Data.TotalPrice;\r\n                    context.Instance.CreatedDate = DateTime.Now;\r\n                })\r\n                .Then(context =&gt; Console.WriteLine(&quot;Ara i\u015flem 1&quot;))\r\n                .Then(context =&gt; Console.WriteLine(&quot;Ara i\u015flem 2&quot;))\r\n                .TransitionTo(OrderCreated)\r\n                .Then(context =&gt; Console.WriteLine(&quot;Ara i\u015flem 3&quot;))\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Stock_OrderCreatedEventQueue}&quot;), context =&gt; new OrderCreatedEvent(context.Instance.CorrelationId)\r\n                {\r\n                    OrderItems = context.Data.OrderItems\r\n                }));\r\n\r\n            \/\/E\u011fer state 'OrderCreated' ise(During) ve o anda 'StockReservedEvent' event'i geldiyse(When)\r\n            \/\/o zaman state'i 'StockReserved' olarak de\u011fi\u015ftir(TransitionTo) ve belirtilen kuyru\u011fa \r\n            \/\/'PaymentStartedEvent' event'ini g\u00f6nder(Send)\r\n            During(OrderCreated,\r\n                When(StockReservedEvent)\r\n                .TransitionTo(StockReserved)\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Payment_StartedEventQueue}&quot;), context =&gt; new PaymentStartedEvent(context.Instance.CorrelationId)\r\n                {\r\n                    OrderItems = context.Data.OrderItems,\r\n                    TotalPrice = context.Instance.TotalPrice\r\n                }),\r\n\r\n                \/\/Yok e\u011fer State 'OrderCreated' iken(During) 'StockNotReservedEvent' event'i geldiyse(When)\r\n                \/\/o zaman state'i 'StockNotReserved' olarak de\u011fi\u015ftir(TransitionTo) ve belirtilen\r\n                \/\/kuyru\u011fa 'OrderFailedEvent' event'ini g\u00f6nder.\r\n                When(StockNotReservedEvent)\r\n                .TransitionTo(StockNotReserved)\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Order_OrderFailedEventQueue}&quot;), context =&gt; new OrderFailedEvent()\r\n                {\r\n                    OrderId = context.Instance.OrderId,\r\n                    Message = context.Data.Message\r\n                }));\r\n\r\n            \/\/E\u011fer ilgili sipari\u015f 'StockReserved' durumunda iken(During) 'PaymentCompletedEvent' event'i geldiyse(When)\r\n            \/\/'PaymentCompleted' state'i olarak de\u011fi\u015ftir(TransitionTo) ve ard\u0131ndan belirtilen kuyru\u011fa \r\n            \/\/'OrderCompletedEvent' event'ini g\u00f6nder. Ayr\u0131ca art\u0131k bu sipari\u015f ba\u015far\u0131l\u0131 olaca\u011f\u0131ndan dolay\u0131\r\n            \/\/State Machine taraf\u0131ndan bu State Instance'\u0131 ba\u015far\u0131yla sonland\u0131r(Finalize) Haliyle b\u00f6ylece sonu\u00e7 olarak\r\n            \/\/ilgili instance'\u0131n state'inde 'Final' yazacakt\u0131r!\r\n\r\n            During(StockReserved,\r\n                When(PaymentCompletedEvent)\r\n                .TransitionTo(PaymentCompleted)\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Order_OrderCompletedEventQueue}&quot;), context =&gt; new OrderCompletedEvent\r\n                {\r\n                    OrderId = context.Instance.OrderId\r\n                })\r\n                .Finalize(),\r\n\r\n                \/\/Yok e\u011fer mevcut state 'StockReserved' iken(During) 'PaymentFailedEvent' event'i gelirse(When)\r\n                \/\/o zaman state'i 'PaymentFailed' olarak de\u011fi\u015ftir(TransitionTo) ve belirtilen kuyruklara \r\n                \/\/'OrderFailedEvent' ve 'StockRollBackMessage' event'lerini g\u00f6nder(Send).\r\n                When(PaymentFailedEvent)\r\n                .TransitionTo(PaymentFailed)\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Order_OrderFailedEventQueue}&quot;), context =&gt; new OrderFailedEvent()\r\n                {\r\n                    OrderId = context.Instance.OrderId,\r\n                    Message = context.Data.Message\r\n                })\r\n                .Send(new Uri($&quot;queue:{RabbitMQSettings.Stock_RollbackMessageQueue}&quot;), context =&gt; new StockRollBackMessage\r\n                {\r\n                    OrderItems = context.Data.OrderItems\r\n                }));\r\n\r\n            \/\/Finalize olan instance'lar\u0131 veritaban\u0131ndan kald\u0131r\u0131yoruz!\r\n            SetCompletedWhenFinalized();\r\n        }\r\n    }\r\n<\/pre>\n<\/li>\n<li><strong><em>Ad\u0131m 10<\/em> <\/strong><em style=\"color:green;font-size:11px;\">Order.API | <\/em><em style=\"color:purple;font-size:11px;\">OrderCompletedEventConsumer ve OrderFailedEventConsumer \u0130simli Consumer&#8217;lar\u0131n Olu\u015fturulmas\u0131<\/em><br \/>\n\u015eimdi <span style=\"color:purple;\">Order.API<\/span> i\u00e7in consume edilmesi gereken event&#8217;leri t\u00fcketecek olan consumer&#8217;lar\u0131 olu\u015ftural\u0131m.<\/p>\n<p><em>OrderCompletedEventConsumer<\/em>, &#8216;OrderCompletedEvent&#8217; neticesinde sipari\u015fi ba\u015far\u0131yla sonland\u0131ran consumer&#8217;d\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderCompletedEventConsumer : IConsumer&lt;OrderCompletedEvent&gt;\r\n    {\r\n        readonly ApplicationDbContext _applicationDbContext;\r\n        public OrderCompletedEventConsumer(ApplicationDbContext applicationDbContext)\r\n        {\r\n            _applicationDbContext = applicationDbContext;\r\n        }\r\n        public async Task Consume(ConsumeContext&lt;OrderCompletedEvent&gt; context)\r\n        {\r\n            Models.Order order = await _applicationDbContext.Orders.FindAsync(context.Message.OrderId);\r\n            if (order != null)\r\n            {\r\n                order.OrderStatus = OrderStatus.Completed;\r\n                await _applicationDbContext.SaveChangesAsync();\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<p><em>OrderFailedEventConsumer<\/em>, &#8216;OrderFailedEvent&#8217; neticesinde sipari\u015fi ba\u015far\u0131s\u0131z olarak sonland\u0131ran consumer&#8217;d\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderFailedEventConsumer : IConsumer&lt;OrderFailedEvent&gt;\r\n    {\r\n        readonly ApplicationDbContext _context;\r\n        public OrderFailedEventConsumer(ApplicationDbContext context)\r\n        {\r\n            _context = context;\r\n        }\r\n        public async Task Consume(ConsumeContext&lt;OrderFailedEvent&gt; context)\r\n        {\r\n            Models.Order order = await _context.FindAsync&lt;Models.Order&gt;(context.Message.OrderId);\r\n            if (order != null)\r\n            {\r\n                order.OrderStatus = OrderStatus.Fail;\r\n                await _context.SaveChangesAsync();\r\n                Console.WriteLine(context.Message.Message);\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<\/li>\n<li><strong><em>Ad\u0131m 11<\/em> <\/strong><em style=\"color:green;font-size:11px;\">Stock.API | <\/em><em style=\"color:purple;font-size:11px;\">OrderCreatedEventConsumer ve StockRollbackMessageConsumer \u0130simli Consumer&#8217;lar\u0131n Olu\u015fturulmas\u0131<\/em><br \/>\nAyn\u0131 \u015fekilde <span style=\"color:purple;\">Stock.API<\/span> i\u00e7in de consumer&#8217;lar\u0131 olu\u015ftural\u0131m.<br \/>\n<em>OrderCreatedEventConsumer<\/em>, &#8216;OrderCreatedEvent&#8217;e g\u00f6re stok bilgilerini g\u00fcncelleyen consumer&#8217;d\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderCreatedEventConsumer : IConsumer&lt;OrderCreatedEvent&gt;\r\n    {\r\n        readonly MongoDbService _mongoDbService;\r\n        readonly ISendEndpointProvider _sendEndpointProvider;\r\n        readonly IPublishEndpoint _publishEndpoint;\r\n\r\n        public OrderCreatedEventConsumer(\r\n            MongoDbService mongoDbService,\r\n            ISendEndpointProvider sendEndpointProvider,\r\n            IPublishEndpoint publishEndpoint)\r\n        {\r\n            _mongoDbService = mongoDbService;\r\n            _sendEndpointProvider = sendEndpointProvider;\r\n            _publishEndpoint = publishEndpoint;\r\n        }\r\n\r\n        public async Task Consume(ConsumeContext&lt;OrderCreatedEvent&gt; context)\r\n        {\r\n            List&lt;bool&gt; stockResult = new();\r\n            IMongoCollection&lt;Models.Stock&gt; collection = _mongoDbService.GetCollection&lt;Models.Stock&gt;();\r\n\r\n            \/\/Sipari\u015f edilen \u00fcr\u00fcnlerin stok miktar\u0131 sipari\u015f adedinden fazla m\u0131? de\u011fil mi?\r\n            foreach (OrderItemMessage orderItem in context.Message.OrderItems)\r\n                stockResult.Add((await collection.FindAsync(s =&gt; s.ProductId == orderItem.ProductId &amp;&amp; s.Count &gt; orderItem.Count)).Any());\r\n\r\n            \/\/E\u011fer fazlaysa sipari\u015f edilen \u00fcr\u00fcnlerin stok miktar\u0131 g\u00fcncelleniyor.\r\n            ISendEndpoint sendEndpoint = await _sendEndpointProvider.GetSendEndpoint(new Uri($&quot;queue:{RabbitMQSettings.StateMachine}&quot;));\r\n            if (stockResult.TrueForAll(sr =&gt; sr.Equals(true)))\r\n            {\r\n                foreach (OrderItemMessage orderItem in context.Message.OrderItems)\r\n                {\r\n                    Models.Stock stock = await (await collection.FindAsync(s =&gt; s.ProductId == orderItem.ProductId)).FirstOrDefaultAsync();\r\n                    stock.Count -= orderItem.Count;\r\n                    await collection.FindOneAndReplaceAsync(x =&gt; x.ProductId == orderItem.ProductId, stock);\r\n                }\r\n\r\n                StockReservedEvent stockReservedEvent = new(context.Message.CorrelationId)\r\n                {\r\n                    OrderItems = context.Message.OrderItems\r\n                };\r\n                await sendEndpoint.Send(stockReservedEvent);\r\n            }\r\n            \/\/E\u011fer az ise sipari\u015fin iptal edilmesi i\u00e7in gerekli event g\u00f6nderiliyor.\r\n            else\r\n            {\r\n                StockNotReservedEvent stockNotReservedEvent = new(context.Message.CorrelationId)\r\n                {\r\n                    Message = &quot;Stok yetersiz...&quot;\r\n                };\r\n\r\n                await sendEndpoint.Send(stockNotReservedEvent);\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<p><em>StockRollbackMessageConsumer<\/em>, &#8216;StockRollBackMessage&#8217;a g\u00f6re g\u00fcncellenmi\u015f stok bilgilerini tekrar geri alan consumer&#8217;d\u0131r. Bir ba\u015fka deyi\u015fle compensable i\u015flemini ger\u00e7ekle\u015ftirmektedir.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockRollbackMessageConsumer : IConsumer&lt;StockRollBackMessage&gt;\r\n    {\r\n        readonly MongoDbService _mongoDbService;\r\n        public StockRollbackMessageConsumer(MongoDbService mongoDbService)\r\n        {\r\n            _mongoDbService = mongoDbService;\r\n        }\r\n        public async Task Consume(ConsumeContext&lt;StockRollBackMessage&gt; context)\r\n        {\r\n            var collection = _mongoDbService.GetCollection&lt;Models.Stock&gt;();\r\n\r\n            foreach (var item in context.Message.OrderItems)\r\n            {\r\n                Models.Stock stock = await (await collection.FindAsync(s =&gt; s.ProductId == item.ProductId)).FirstOrDefaultAsync();\r\n                if (stock != null)\r\n                {\r\n                    stock.Count += item.Count;\r\n                    await collection.FindOneAndReplaceAsync(s =&gt; s.ProductId == item.ProductId, stock);\r\n                }\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<\/li>\n<li><strong><em>Ad\u0131m 12<\/em> <\/strong><em style=\"color:green;font-size:11px;\">Payment.API | <\/em><em style=\"color:purple;font-size:11px;\">PaymentStartedEventConsumer Consumer&#8217;\u0131n\u0131n Olu\u015fturulmas\u0131<\/em><br \/>\n<span style=\"color:purple;\">Payment.API<\/span>&#8216;da da &#8216;PaymentStartedEvent&#8217;i dinleyip tetiklenecek olan consumer&#8217;\u0131 olu\u015ftural\u0131m. Bu consumer&#8217;da gerekli \u00f6deme i\u015flemlerini farazi olarak ger\u00e7ekle\u015ftiriyor olaca\u011f\u0131z.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentStartedEventConsumer : IConsumer&lt;PaymentStartedEvent&gt;\r\n    {\r\n        readonly ISendEndpointProvider _sendEndpointProvider;\r\n        readonly IPublishEndpoint _publishEndpoint;\r\n        public PaymentStartedEventConsumer(ISendEndpointProvider sendEndpointProvider,\r\n            IPublishEndpoint publishEndpoint)\r\n        {\r\n            this._sendEndpointProvider = sendEndpointProvider;\r\n            _publishEndpoint = publishEndpoint;\r\n        }\r\n\r\n        public async Task Consume(ConsumeContext&lt;PaymentStartedEvent&gt; context)\r\n        {\r\n            ISendEndpoint sendEndpoint = await _sendEndpointProvider.GetSendEndpoint(new Uri($&quot;queue:{RabbitMQSettings.StateMachine}&quot;));\r\n            if (context.Message.TotalPrice &lt;= 100)\r\n                await sendEndpoint.Send(new PaymentCompletedEvent(context.Message.CorrelationId));\r\n            else\r\n                await sendEndpoint.Send(new PaymentFailedEvent(context.Message.CorrelationId)\r\n                {\r\n                    Message = &quot;Bakiye yetersiz!&quot;,\r\n                    OrderItems = context.Message.OrderItems\r\n                });\r\n        }\r\n    }\r\n<\/pre>\n<\/li>\n<li><strong><em>Ad\u0131m 13<\/em> <\/strong><em style=\"color:green;font-size:11px;\">T\u00fcm Servisler | <\/em><em style=\"color:purple;font-size:11px;\">Consumer&#8217;lar\u0131n Startup.cs&#8217;de Tan\u0131mlanmas\u0131<\/em><br \/>\n\u015eu ana kadar olu\u015fturulan t\u00fcm consumer&#8217;lar ilgili servislerin &#8216;Startup.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki gibi tan\u0131mlanmal\u0131d\u0131rlar;<br \/>\n<span style=\"color:purple;\">Order.API<\/span>;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n        public void ConfigureServices(IServiceCollection services)\r\n        {\r\n            .\r\n            .\r\n            .\r\n            services.AddMassTransit(configure =&gt;\r\n            {\r\n                configure.AddConsumer&lt;OrderCompletedEventConsumer&gt;();\r\n                configure.AddConsumer&lt;OrderFailedEventConsumer&gt;();\r\n\r\n                configure.UsingRabbitMq((context, configurator) =&gt;\r\n                {\r\n                    configurator.Host(Configuration.GetConnectionString(&quot;RabbitMQ&quot;));\r\n\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Order_OrderCompletedEventQueue, e =&gt;\r\n                    e.ConfigureConsumer&lt;OrderCompletedEventConsumer&gt;(context));\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Order_OrderFailedEventQueue, e =&gt;\r\n                    e.ConfigureConsumer&lt;OrderFailedEventConsumer&gt;(context));\r\n                });\r\n            });\r\n            .\r\n            .\r\n            .\r\n        }\r\n<\/pre>\n<p><span style=\"color:purple;\">Stock.API<\/span>;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n        public void ConfigureServices(IServiceCollection services)\r\n        {\r\n            .\r\n            .\r\n            .\r\n            services.AddMassTransit(configure =&gt;\r\n            {\r\n                configure.AddConsumer&lt;OrderCreatedEventConsumer&gt;();\r\n                configure.AddConsumer&lt;StockRollbackMessageConsumer&gt;();\r\n\r\n                configure.UsingRabbitMq((context, configurator) =&gt;\r\n                {\r\n                    configurator.Host(Configuration.GetConnectionString(&quot;RabbitMQ&quot;));\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Stock_OrderCreatedEventQueue, e =&gt; e.ConfigureConsumer&lt;OrderCreatedEventConsumer&gt;(context));\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Stock_RollbackMessageQueue, e =&gt; e.ConfigureConsumer&lt;StockRollbackMessageConsumer&gt;(context));\r\n                });\r\n            });\r\n            .\r\n            .\r\n            .\r\n        }\r\n<\/pre>\n<p><span style=\"color:purple;\">Payment.API<\/span>;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n        public void ConfigureServices(IServiceCollection services)\r\n        {\r\n            .\r\n            .\r\n            .\r\n            services.AddMassTransit(configure =&gt;\r\n            {\r\n                configure.AddConsumer&lt;PaymentStartedEventConsumer&gt;();\r\n\r\n                configure.UsingRabbitMq((context, configurator) =&gt;\r\n                {\r\n                    configurator.Host(Configuration.GetConnectionString(&quot;RabbitMQ&quot;));\r\n\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Payment_StartedEventQueue, e =&gt;\r\n                    e.ConfigureConsumer&lt;PaymentStartedEventConsumer&gt;(context));\r\n                });\r\n            });\r\n            .\r\n            .\r\n            .\r\n        }\r\n<\/pre>\n<p>Ayr\u0131ca <span style=\"color:purple;\">SagaStateMachine.Service<\/span>&#8216;de a\u015fa\u011f\u0131daki gibi &#8216;OrderStateInstance&#8217;\u0131 receive ederek State Machine&#8217;in hangi kuyru\u011fu dinleyece\u011fini bildirmek gerekmektedir;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class Program\r\n    {\r\n        .\r\n        .\r\n        .\r\n        public static IHostBuilder CreateHostBuilder(string&#x5B;] args) =&gt;\r\n            Host.CreateDefaultBuilder(args)\r\n                .ConfigureServices((hostContext, services) =&gt;\r\n                {\r\n                    services.AddMassTransit(configure =&gt;\r\n                    {\r\n                        .\r\n                        .\r\n                        .\r\n\r\n                        configure.AddBus(provider =&gt; Bus.Factory.CreateUsingRabbitMq(cfg =&gt;\r\n                        {\r\n                            cfg.Host(hostContext.Configuration.GetConnectionString(&quot;RabbitMQ&quot;));\r\n\r\n                            cfg.ReceiveEndpoint(RabbitMQSettings.StateMachine, e =&gt;\r\n                            e.ConfigureSaga&lt;OrderStateInstance&gt;(provider));\r\n                        }));\r\n                    });\r\n\r\n                    services.AddMassTransitHostedService();\r\n\r\n                });\r\n    }\r\n<\/pre>\n<p>\u0130\u015fte bu kadar \ud83d\ude42\n<\/li>\n<\/ul>\n<p>Vee t\u00fcm i\u015flemlerimiz bitti. Evet, biliyorum&#8230; \u0130n\u015fa s\u00fcreci biraz zahmetli ve me\u015fakkatli ge\u00e7mi\u015f olabilir. \u263a Ama \u015fimdi s\u0131ra, yap\u0131lan t\u00fcm bu \u00e7al\u0131\u015fmalar\u0131n meyvesini toplamaya gelmi\u015f bulunmaktad\u0131r. Ha gayret son kez test edelim \u015fu yap\u0131lanmay\u0131&#8230;<\/p>\n<h3>Test Edelim<\/h3>\n<p>\u0130lk olarak t\u00fcm servisleri aya\u011fa kald\u0131rd\u0131\u011f\u0131m\u0131zda RabbitMQ ile connection sa\u011flay\u0131p sa\u011flamad\u0131klar\u0131n\u0131 g\u00f6zlemleyelim.<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-3.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-3.png\" alt=\"Microservice - Saga - Commands-Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"715\" height=\"177\" class=\"aligncenter size-full wp-image-22787\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-3.png 715w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-3-300x74.png 300w\" sizes=\"auto, (max-width: 715px) 100vw, 715px\" \/><\/a>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere t\u00fcm servislerimiz ba\u015far\u0131yla ba\u011flant\u0131 sa\u011flam\u0131\u015f bulunmaktad\u0131r. \u015eimdi a\u015fa\u011f\u0131daki gibi tutarl\u0131 verilerle bir sipari\u015f iste\u011finde bulunal\u0131m ve s\u00fcreci ad\u0131m ad\u0131m takip edelim.<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-4.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-4.png\" alt=\"Microservice - Saga - Commands-Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"165\" height=\"400\" class=\"aligncenter size-full wp-image-22788\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-4.png 165w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-4-124x300.png 124w\" sizes=\"auto, (max-width: 165px) 100vw, 165px\" \/><\/a>Bu verilerle istek yap\u0131ld\u0131ktan sonra sipari\u015f olu\u015fturuluyor ve State Machine veritaban\u0131nda ilgili sipari\u015fin durumu &#8216;OrderCreated&#8217; olarak ayarlan\u0131yor.<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-5.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-5.png\" alt=\"Microservice - Saga - Commands-Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"670\" height=\"308\" class=\"aligncenter size-full wp-image-22790\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-5.png 670w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-5-300x138.png 300w\" sizes=\"auto, (max-width: 670px) 100vw, 670px\" \/><\/a>Ard\u0131ndan stok bilgileri MongoDB&#8217;de a\u015fa\u011f\u0131daki gibi g\u00fcncelleniyor.<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-6.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-6.png\" alt=\"Microservice - Saga - Commands-Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"415\" height=\"172\" class=\"aligncenter size-full wp-image-22792\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-6.png 415w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-6-300x124.png 300w\" sizes=\"auto, (max-width: 415px) 100vw, 415px\" \/><\/a>Tabi bu durumda State Machine veritaban\u0131nda ilgili sipari\u015fe dair durumumuz &#8216;StockReserved&#8217; olarak g\u00fcncelleniyor.<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-7.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-7.png\" alt=\"Microservice - Saga - Commands-Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"678\" height=\"38\" class=\"aligncenter size-full wp-image-22793\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-7.png 678w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-7-300x17.png 300w\" sizes=\"auto, (max-width: 678px) 100vw, 678px\" \/><\/a>Ve ard\u0131ndan ilgili sipari\u015f durumu &#8216;Orders&#8217; tablosunda <span style=\"color:orange;\">Completed<\/span> olarak g\u00fcncelleniyor ve ard\u0131ndan State Machine&#8217;den sipari\u015fe dair veri kald\u0131r\u0131l\u0131yor!<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-8.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-8.png\" alt=\"Microservice - Saga - Commands-Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"432\" height=\"307\" class=\"aligncenter size-full wp-image-22795\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-8.png 432w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-8-300x213.png 300w\" sizes=\"auto, (max-width: 432px) 100vw, 432px\" \/><\/a>Sipari\u015f iste\u011fini tutars\u0131z verilerle yaparsak e\u011fer burada hem sipari\u015f hatal\u0131 bir \u015fekilde sonland\u0131r\u0131lm\u0131\u015f olacak hemde State Machine veritaban\u0131nda bu sipari\u015fe dair sonlan\u0131\u015f\u0131n neden oldu\u011fu state bilgisini rahatl\u0131kla g\u00f6rebiliyor olaca\u011f\u0131z.<\/p>\n<p>Misal; tutars\u0131z stok bilgileriyle bir istek yaparsak e\u011fer son g\u00f6r\u00fcnt\u00fc a\u015fa\u011f\u0131daki gibi olacakt\u0131r;<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-9.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-9.png\" alt=\"Microservice - Saga - Commands-Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"660\" height=\"100\" class=\"aligncenter size-full wp-image-22797\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-9.png 660w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-9-300x45.png 300w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><\/a>Yahut \u00f6deme durumunda bir tutars\u0131zl\u0131k olu\u015fursa sistemin son hali a\u015fa\u011f\u0131daki gibi olacakt\u0131r;<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-10.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-10.png\" alt=\"Microservice - Saga - Commands-Orchestration Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"676\" height=\"133\" class=\"aligncenter size-full wp-image-22800\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-10.png 676w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Commands-Orchestration-Implemantasyonu-Ile-Transaction-Yonetimi-10-300x59.png 300w\" sizes=\"auto, (max-width: 676px) 100vw, 676px\" \/><\/a><\/p>\n<p>Nihai olarak;<br \/>\nOlduk\u00e7a uzun ve zahmetli bir i\u00e7erik ile Saga pattern&#8217;\u0131 Orchestration implementasyonu e\u015fli\u011finde ele alm\u0131\u015f bulunmaktay\u0131z. \u0130\u00e7eri\u011fimiz okunurken elbette yer yer zor yahut kar\u0131\u015f\u0131k geliyor olabilir. Lakin bu kadar kompleks ve bol kritik gerektiren bir konuyu yaz\u0131ya aktarabilmek sizlerin okurken sarfetti\u011finiz enerjiye e\u015fde\u011fer bir zahmet gerektirdi\u011fine emin olabilirsiniz. Elbet bir niyet ile ba\u015flayan bu u\u011fra\u015f\u0131n neticesini iyi k\u00f6t\u00fc getirebilmenin sevinciyle hepinize iyi okumalar diliyor ve bu noktaya kadar sabredip e\u015flik ettiyseniz t\u00fcm samimiyetimle te\u015fekk\u00fcr ediyorum&#8230;<\/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 projeyi indirebilmek i\u00e7in <a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/SagaOrchestrationExample.zip\" rel=\"noopener\" target=\"_blank\">buraya<\/a> t\u0131klay\u0131n\u0131z.<\/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, \u00d6nceki i\u00e7eriklerimizden Microservice &#8211; Saga &#8211; Events\/Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi ba\u015fl\u0131kl\u0131 yaz\u0131m\u0131zda Saga pattern&#8217;\u0131n\u0131n iki implemantasyonundan biri olan choreography y\u00f6ntemini t\u00fcm detaylar\u0131yla ve pratik olarak incelemi\u015ftik. Bu i\u00e7eri\u011fimizde ise yine Saga pattern&#8217;\u0131n\u0131n&#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":22654,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2739,153,3573],"tags":[4389,4388,4384,4356,4387,4386,4385],"class_list":["post-22629","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net-core","category-design-pattern","category-microservices","tag-net-core-automatonymous","tag-automatonymous","tag-commands-orchestration","tag-saga-pattern","tag-saga-pattern-nasil-uygulanir","tag-saga-pattern-nedir","tag-saga-pattern-orchestration"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/22629","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=22629"}],"version-history":[{"count":161,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/22629\/revisions"}],"predecessor-version":[{"id":22806,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/22629\/revisions\/22806"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media\/22654"}],"wp:attachment":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media?parent=22629"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/categories?post=22629"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/tags?post=22629"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}