﻿
{"id":22393,"date":"2021-08-15T22:29:40","date_gmt":"2021-08-15T22:29:40","guid":{"rendered":"https:\/\/www.gencayyildiz.com\/blog\/?p=22393"},"modified":"2021-08-15T22:29:40","modified_gmt":"2021-08-15T22:29:40","slug":"microservice-saga-events-choreography-implemantasyonu-ile-transaction-yonetimi","status":"publish","type":"post","link":"https:\/\/www.gencayyildiz.com\/blog\/microservice-saga-events-choreography-implemantasyonu-ile-transaction-yonetimi\/","title":{"rendered":"Microservice &#8211; Saga &#8211; Events\/Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi"},"content":{"rendered":"<div id=\"fb-root\"><\/div>\n<p>Merhaba,<\/p>\n<p>Bir \u00f6nceki <a href=\"https:\/\/www.gencayyildiz.com\/blog\/microservice-mimarilerde-saga-pattern-ile-transaction-yonetimi\/\" rel=\"noopener\" target=\"_blank\">Microservice Mimarilerde Saga Pattern \u0130le Transaction Y\u00f6netimi<\/a> ba\u015fl\u0131kl\u0131 makalemizde Saga pattern \u00fczerine detayl\u0131ca teorik incelemede bulunmu\u015ftuk. Bu i\u00e7eri\u011fimizde ise Saga pattern&#8217;\u0131n\u0131 Events\/Choreography implemantasyonu \u00e7er\u00e7evesinde ele alacak ve pratikte nas\u0131l bir in\u015fan\u0131n s\u00f6z konusu oldu\u011funu hep beraber ad\u0131m ad\u0131m inceliyor olaca\u011f\u0131z.<\/p>\n<h3>Senaryo<\/h3>\n<p>\u0130ncelememizde senaryo olarak basit bir e-ticaret s\u00fcrecini ele al\u0131yor 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> olmak \u00fczere \u00fc\u00e7 temel microservice bizlere e\u015flik ediyor olacak. \u0130\u015flevsel olarak kullan\u0131c\u0131dan gelen sipari\u015f talebi \u00fczerine <span style=\"color:purple;\">Order.API<\/span> bu talebi kar\u015f\u0131layacak ve state&#8217;i <span style=\"color:orange;\">Suspend<\/span> olan bir sipari\u015f olu\u015fturulacakt\u0131r. Ard\u0131ndan sipari\u015f edilen \u00fcr\u00fcnlerin stok verilerinin d\u00fc\u015f\u00fcr\u00fclmesi i\u00e7in <span style=\"color:purple;\">Stock.API<\/span> servisinde gerekli g\u00fcncelleme ger\u00e7ekle\u015ftirilecek ve bu i\u015flem ba\u015far\u0131l\u0131 olduktan sonra nihai olarak \u00f6deme i\u015flemi i\u00e7in devreye <span style=\"color:purple;\">Payment.API<\/span> girecek ve gerekli tahsilat ger\u00e7ekle\u015ftirilecektir. E\u011fer s\u00fcre\u00e7te herhangi bir hata meydana gelirse t\u00fcm transactionlar\u0131 compensable transaction ile telafi edece\u011fiz.<\/p>\n<p>Bu servislerin kendi aralar\u0131nda haberle\u015febilmeleri i\u00e7in publish edecekleri ve subscribe olacaklar\u0131 event \u015femas\u0131 a\u015fa\u011f\u0131daki gibi olacakt\u0131r.<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"width:21%;\">Service<\/th>\n<th>Publish<\/th>\n<th>Subscribe<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align:left;\"><strong>Order Service<\/strong><\/td>\n<td style=\"text-align:left;\"><code style=\"color:red;font-size:13px;\">OrderCreatedEvent<\/code> : <span style=\"font-size:11px;\">Sipari\u015f olu\u015fturuldu\u011funu ifade eden event&#8217;tir. Stock Service taraf\u0131ndan dinlenmektedir.<\/span><\/td>\n<td style=\"text-align:left;\">\n<code style=\"color:blue;font-size:13px;\">PaymentCompletedEvent<\/code><br \/>\n<code style=\"color:blue;font-size:13px;\">PaymentFailedEvent<\/code><br \/>\n<code style=\"color:blue;font-size:13px;\">StockNotReservedEvent<\/code>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left;\"><strong>Stock Service<\/strong><\/td>\n<td style=\"text-align:left;\">\n<code style=\"color:red;font-size:13px;\">StockReservedEvent<\/code> : <span style=\"font-size:11px;\">Stok g\u00fcncellemesi ba\u015far\u0131yla ger\u00e7ekle\u015ftirildi\u011finde yay\u0131lan event&#8217;tir. Payment Service taraf\u0131ndan dinlenmektedir.<\/span><br \/>\n<code style=\"color:red;font-size:13px;\">StockNotReservedEvent<\/code> : <span style=\"font-size:11px;\">Stok g\u00fcncellemesinde bir hata ya da tutars\u0131zl\u0131k meydana geldi\u011finde yay\u0131lan event&#8217;tir. Order Service taraf\u0131ndan dinlenmektedir ve f\u0131rlat\u0131ld\u0131\u011f\u0131 taktirde sipari\u015f <span style=\"color:orange;\">Fail<\/span> durumuna \u00e7ekilir.<\/span>\n<\/td>\n<td style=\"text-align:left;\">\n<code style=\"color:blue;font-size:13px;\">OrderCreatedEvent<\/code><br \/>\n<code style=\"color:blue;font-size:13px;\">PaymentFailedEvent<\/code>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align:left;\"><strong>Payment Service<\/strong><\/td>\n<td style=\"text-align:left;\">\n<code style=\"color:red;font-size:13px;\">PaymentCompletedEvent<\/code> : <span style=\"font-size:11px;\">\u00d6deme i\u015fleminin ba\u015far\u0131yla ger\u00e7ekle\u015ftirildi\u011fini ifade eden event&#8217;tir. Order Service taraf\u0131ndan dinlenmektedir ve sipari\u015f durumunun <span style=\"color:orange;\">Completed<\/span> olmas\u0131n\u0131 sa\u011flar.<\/span><br \/>\n<code style=\"color:red;font-size:13px;\">PaymentFailEvent<\/code> : <span style=\"font-size:11px;\">\u00d6deme s\u00fcrecinde bir hata meydana geldi\u011fini ifade eden event&#8217;tir. Order Service ve Stock Service taraf\u0131ndan dinlenmektedir. Sipari\u015f durumunun <span style=\"color:orange;\">Fail<\/span> olmas\u0131n\u0131 sa\u011flar.<\/span>\n<\/td>\n<td style=\"text-align:left;\">\n<code style=\"color:blue;font-size:13px;\">StockReservedEvent<\/code>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u0130\u015fte b\u00f6yle \ud83d\ude42 Pratik yapaca\u011f\u0131m\u0131z senaryo ve event \u015femas\u0131n\u0131 masaya yat\u0131rd\u0131ktan sonra s\u0131ra servislerimizi olu\u015fturmaya gelmi\u015ftir.<\/p>\n<h3>Servislerin Olu\u015fturulmas\u0131<\/h3>\n<p>Yukar\u0131daki paragraflarda bahsedildi\u011fi gibi <span style=\"color:purple;\">Order.API<\/span>, <span style=\"color:purple;\">Stock.API<\/span> ve <span style=\"color:purple;\">Payment.API<\/span> olmak \u00fczere \u00fc\u00e7 adet servis olu\u015fturaca\u011f\u0131z. Bunu herhangi bir y\u00f6ntemle ger\u00e7ekle\u015ftirebilece\u011finiz gibi a\u015fa\u011f\u0131daki dotnet cli komutlar\u0131n\u0131 kullanarak da ger\u00e7ekle\u015ftirebilirsiniz.<\/p>\n<div style=\"text-align:center;\">\n<p><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;\">(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&#8217;leri vs. bu class library&#8217;de olu\u015fturaca\u011f\u0131z.<\/span>\n<\/div>\n<h3>Servislere MassTransit Kurulumu ve Temel Konfig\u00fcrasyonlar<\/h3>\n<p>Saga pattern ile servisler aras\u0131 distributed transaction&#8217;\u0131 sa\u011flayabilmek i\u00e7in asenkron bir ileti\u015fim modeli kullanaca\u011f\u0131m\u0131z\u0131 makalemizin ilk paragraf\u0131nda referans edilen i\u00e7eri\u011fimizde konu\u015fmu\u015ftuk. Haliyle bizler asenkron ileti\u015fim i\u00e7in <a href=\"https:\/\/www.gencayyildiz.com\/blog\/rabbitmq-yazi-dizisi\/\" rel=\"noopener\" target=\"_blank\">RabbitMQ<\/a> sistemini tercih edece\u011fiz. Lakin uygulama i\u00e7erisinde salt bir \u015fekilde RabbitMQ&#8217;yu kodlamak bizler i\u00e7in bir nebze fazla maliyetli olaca\u011f\u0131ndan dolay\u0131 h\u0131zl\u0131 hareket edebilmek ve g\u00fcvenilir bir \u015fekilde kuyruklara mesajlar\u0131m\u0131z\u0131 iletebilmek i\u00e7in Enterprise Service Bus(ESB) olan <a href=\"https:\/\/www.gencayyildiz.com\/blog\/net-core-masstransit-kullanarak-rabbitmq-ile-messaging\/\" rel=\"noopener\" target=\"_blank\">MassTransit<\/a> k\u00fct\u00fcphanesini kullanaca\u011f\u0131z.<\/p>\n<p>Dolay\u0131s\u0131yla t\u00fcm servislerimize MassTransit&#8217;i 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>Ard\u0131ndan t\u00fcm servislerin &#8216;Startup.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki konfig\u00fcrasyonu sa\u011flayarak, ilgili k\u00fct\u00fcphaneyi ve RabbitMQ servislerini uygulamalara entegre edelim.<\/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>Dikkat ederseniz t\u00fcm servislerde ba\u011flant\u0131 kurulacak RabbitMQ sunucusunun bilgileri yap\u0131land\u0131rma noktas\u0131ndan talep edilmektedir. Bu bilgiyi ister <a href=\"https:\/\/www.gencayyildiz.com\/blog\/asp-net-core-environment-nedir-konfigurasyonu-nasil-yapilir\/\" rel=\"noopener\" target=\"_blank\">environment<\/a>&#8216;tan isterseniz de &#8216;appsettings.json&#8217; dosyas\u0131ndan verebilirsiniz. Misal bizler \u015fimdilik t\u00fcm servislerimizin &#8216;appsettings.json&#8217; dosyas\u0131nda sunucu bilgilerini a\u015fa\u011f\u0131daki gibi tutal\u0131m.<\/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:\/\/pg*******w6iBqJ92iNG_0@elk.r******p.com\/pgjnreme&quot;\r\n  }\r\n}\r\n<\/pre>\n<p>Bizler \u00f6rneklendirmemizde RabbitMQ&#8217;yu cloud servis olarak kullanaca\u011f\u0131m\u0131zdan dolay\u0131 gerekli ba\u011flant\u0131y\u0131 <a href=\"https:\/\/www.cloudamqp.com\/\" rel=\"noopener\" target=\"_blank\">cloudamqp.com<\/a> adresindeki instance&#8217;a uygun olarak yazm\u0131\u015f bulunmaktay\u0131z. E\u011fer ki, RabbitMQ instance&#8217;\u0131n\u0131n cloud ortamda nas\u0131l aya\u011fa kald\u0131r\u0131ld\u0131\u011f\u0131na dair herhangi bir fikriniz yoksa <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 makaleme g\u00f6z atman\u0131z\u0131 tavsiye ederim.<\/p>\n<p>Velhas\u0131l, t\u00fcm servislerde yap\u0131lan bu konfig\u00fcrasyon neticesinde art\u0131k microservice yap\u0131lanmam\u0131z\u0131n t\u00fcm par\u00e7alar\u0131 asenkron bir ileti\u015fim kurabilir niteli\u011fe kavu\u015fmu\u015f demektir. Art\u0131k servislerimizi tek tek amaca uygun bir \u015fekilde geli\u015ftirebilir ve Saga pattern&#8217;\u0131n Choreography implemantasyonunu kullanarak distributed transaction&#8217;\u0131 sa\u011flayabiliriz. Hadi buyrun ba\u015flayal\u0131m.<\/p>\n<h3>Servislerin Geli\u015ftirilmesi<\/h3>\n<p>\u015eimdi yukar\u0131larda bahsedilen senaryoya uygun bir \u015fekilde e-ticaret yap\u0131lanmam\u0131z\u0131 ad\u0131m ad\u0131m kodlamaya ge\u00e7ebiliriz. Burada makalenin hizas\u0131na g\u00f6re kodlanan herhangi bir servis s\u00fcre\u00e7te tekrardan d\u00fczeltmek yahut yeni bir event&#8217;e g\u00f6re tekrar kodlanmak gerektirebilece\u011finden dolay\u0131 say\u0131sal olarak ba\u015fl\u0131kland\u0131r\u0131lm\u0131\u015f ad\u0131mlarla par\u00e7a par\u00e7a ilerleyece\u011fiz. Dolay\u0131s\u0131yla iyi takip etmenizi, yap\u0131lan i\u015flemleri yakalayabilmek ve b\u00fct\u00fcnsel olarak yorumlayabilmek i\u00e7in gerekirse sizlerin de bu prati\u011fe e\u015flik etmenizi tavsiye ederim. Hadi 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\u0130lk olarak <span style=\"color:purple;\">Order.API<\/span> servisinin geli\u015ftirilmesiyle ba\u015flayal\u0131m.<\/p>\n<p>Merkezde bir sipari\u015f s\u00fcrecini ele alaca\u011f\u0131m\u0131zdan dolay\u0131 \u00f6ncelikle bu s\u00fcreci modelleyecek olan &#8216;Order&#8217; ve &#8216;OrderItem&#8217; entity&#8217;lerini olu\u015fturarak 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>Ard\u0131ndan bu entity&#8217;lerin code first yakla\u015f\u0131m\u0131 ile veritaban\u0131na migrate i\u015flemini ger\u00e7ekle\u015ftirebilmek i\u00e7in bir context nesnesi olu\u015ftural\u0131m ve i\u00e7eri\u011fini a\u015fa\u011f\u0131daki gibi doldural\u0131m.<\/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>Devam\u0131nda ise <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.SqlServer\/\" rel=\"noopener\" target=\"_blank\"><em>Microsoft.EntityFrameworkCore.SqlServer<\/em><\/a> k\u00fct\u00fcphanesini uygulamam\u0131za y\u00fckleyerek &#8216;Startup.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki konfig\u00fcrasyonu yapal\u0131m.<\/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; options.UseSqlServer(Configuration.GetConnectionString(&quot;SQLServer&quot;)));\r\n            .\r\n            .\r\n            .\r\n        }\r\n<\/pre>\n<p>Tabi yine burada veritaban\u0131n\u0131n connection string de\u011ferini yap\u0131land\u0131rma dosyas\u0131ndan almaktay\u0131z. Bunun i\u00e7in &#8216;appsettings.json&#8217; dosyas\u0131na ilgili ba\u011flant\u0131 c\u00fcmleci\u011fini a\u015fa\u011f\u0131daki gibi ekleyelim.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n  &quot;ConnectionStrings&quot;: {\r\n    &quot;SQLServer&quot;: &quot;Server=***;Database=SagaChoreographyDB;User Id=***;Password=***;&quot;,\r\n    &quot;RabbitMQ&quot;: &quot;amqps:\/\/pg*******w6iBqJ92iNG_0@elk.r******p.com\/pgjnreme&quot;\r\n  }\r\n<\/pre>\n<p>T\u00fcm bu i\u015flemlerden sonra art\u0131k s\u0131ra migration olu\u015fturma ve migrate etmeye gelmi\u015f bulunmaktad\u0131r. Bunun i\u00e7in <em><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Tools\" rel=\"noopener\" target=\"_blank\">Microsoft.EntityFrameworkCore.Tools<\/a><\/em> k\u00fct\u00fcphanesini y\u00fckleyerek &#8216;Package Manager Console&#8217; \u00fczerinden s\u0131ras\u0131yla <code>add-migration mig_1<\/code> ve <code>update-database<\/code> talimatlar\u0131n\u0131 verelim. Bu i\u015flemi isterseniz powershell \u00fczerinden dotnet cli komutlar\u0131 arac\u0131l\u0131\u011f\u0131yla da ger\u00e7ekle\u015ftirebilirsiniz. Bunun i\u00e7in <em><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.EntityFrameworkCore.Design\/\" rel=\"noopener\" target=\"_blank\">Microsoft.EntityFrameworkCore.Design<\/a><\/em> k\u00fct\u00fcphanesini y\u00fcklemeniz ve s\u0131ras\u0131yla <code>dotnet ef migrations add mig_1<\/code> ve <code>dotnet ef database update<\/code> talimatlar\u0131n\u0131 vermeniz yeterli olacakt\u0131r. Ee haliyle buradaki tercih art\u0131k sizindir.<\/p>\n<p>\u015eimdi konfig\u00fcre edilen t\u00fcm bu altyap\u0131 sonras\u0131 art\u0131k bir endpoint \u00fczerinden sipari\u015f talebinde bulunmaya ve ard\u0131ndan sipari\u015fi olu\u015fturtup sonraki microservisimiz olan <span style=\"color:purple;\">Stock.API<\/span>&#8216;a stok i\u015flemleri i\u00e7in bilgi vermeye s\u0131ra gelmi\u015f bulunmaktad\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 \/>\nSipari\u015f talebinde bulunmak ve bu talep neticesinde sipari\u015f olu\u015fturabilmek i\u00e7in <em>OrdersController.cs<\/em> isminde bir controller olu\u015ftural\u0131m ve i\u00e7eri\u011fini a\u015fa\u011f\u0131daki gibi doldural\u0131m.<\/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\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\u011funa g\u00f6z atarsan\u0131z e\u011fer &#8216;CreateOrder&#8217; isimli bir action metot arac\u0131l\u0131\u011f\u0131yla, i\u00e7eri\u011fi a\u015fa\u011f\u0131da verilmi\u015f olan &#8216;OrderVM&#8217; t\u00fcr\u00fcnde gelen sipari\u015f talebi gerekli manuel d\u00f6n\u00fc\u015f\u00fcmler neticesinde olu\u015fturulmakta ve veritaban\u0131na eklenmektedir. Burada dikkat etmenizi istedi\u011fim husus 23. sat\u0131rdaki sipari\u015fin durumunu ifade eden &#8216;OrderStatus&#8217; \u00f6zelli\u011finin <span style=\"color:orange;\">Suspend<\/span> olarak belirtiliyor olmas\u0131d\u0131r. Nihayetinde bu sipari\u015fin ba\u015far\u0131l\u0131 bir \u015fekilde sonlanabilmesi i\u00e7in \u00fcr\u00fcnlere dair stok bilgilerinin g\u00fcncellenmesi ve \u00f6demenin sa\u011fl\u0131kl\u0131 bir \u015fekilde ger\u00e7ekle\u015ftirilebiliyor olmas\u0131 gerekmektedir.<br \/>\n&#8216;OrderVM&#8217; i\u00e7eri\u011fi :<\/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<\/li>\n<li><strong><em>Ad\u0131m 3<\/em><\/strong> <em style=\"color:green;font-size:11px;\">Shared |<\/em> <em style=\"color:purple;font-size:11px;\">IEvent ve OrderCreatedEvent Event&#8217;ini Olu\u015fturma<\/em><br \/>\nSipari\u015f olu\u015fturulduktan sonra art\u0131k s\u0131rada <span style=\"font-size:10px;\"><em>&#8220;eyy <span style=\"color:purple;\">Stock.API<\/span>, sipari\u015f olu\u015fturuldu s\u0131ra sende!&#8221;<\/em><\/span> \u015feklinde di\u011fer servislere haber vermemiz gerekmektedir. Haliyle bunun i\u00e7in bir event f\u0131rlat\u0131lmas\u0131 gerekmektedir. Bu event <code style=\"color:red;font-size:13px;\">OrderCreatedEvent<\/code> olacakt\u0131r.<\/p>\n<p>Bunun i\u00e7in ortak message ve event t\u00fcrlerini bar\u0131nd\u0131raca\u011f\u0131m\u0131z <span style=\"color:purple;\">Shared<\/span> isimli class library&#8217;e &#8216;Events&#8217; isminde bir klas\u00f6r olu\u015ftural\u0131m ve i\u00e7erisine \u00f6ncelikle <em>IEvent<\/em> isimli bir interface olu\u015ftural\u0131m. Bu interface, sistemdeki event class&#8217;lar\u0131n\u0131 di\u011ferlerinden ay\u0131rt edebilmek i\u00e7in bir i\u015faretleyici g\u00f6revi g\u00f6recektir.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public interface IEvent\r\n    {\r\n    }\r\n<\/pre>\n<p>Haliyle ayn\u0131 klas\u00f6r i\u00e7erisine <code style=\"color:red;font-size:13px;\">OrderCreatedEvent<\/code> isminde bir class olu\u015ftural\u0131m ve i\u00e7eri\u011fini a\u015fa\u011f\u0131daki gibi doldural\u0131m.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class OrderCreatedEvent : IEvent\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\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<p>Burada <code style=\"color:red;font-size:13px;\">OrderCreatedEvent<\/code> i\u00e7eri\u011fine g\u00f6z atarsan\u0131z e\u011fer hangi al\u0131c\u0131n\u0131n, hangi sipari\u015fi, toplam ne kadar fiyata ve hangi \u00fcr\u00fcnlerle(OrderItems\/OrderItemMessage) ald\u0131\u011f\u0131na dair t\u00fcm bilgileri tutmaktay\u0131z. Haliyle sipari\u015f olu\u015fturulduktan sonra bu event&#8217;in kuyru\u011fa g\u00f6nderilmesi neticesinde <span style=\"color:purple;\">Stock.API<\/span> i\u00e7in gerekli t\u00fcm detaylar sa\u011flanm\u0131\u015f olacakt\u0131r.\n<\/li>\n<li><strong><em>Ad\u0131m 4<\/em><\/strong> <em style=\"color:green;font-size:11px;\">Shared |<\/em> <em style=\"color:purple;font-size:11px;\">OrderCreatedEvent Event&#8217;inin Kuyruk Ad\u0131n\u0131n Belirlenmesi<\/em><br \/>\nOlu\u015fturulan <code style=\"color:red;font-size:13px;\">OrderCreatedEvent<\/code> event&#8217;inin hangi kuyru\u011fa at\u0131laca\u011f\u0131n\u0131 belirleyebilmek i\u00e7in bir static s\u0131n\u0131f tasarlamam\u0131z olduk\u00e7a uygun olacakt\u0131r. \u00c7\u00fcnk\u00fc s\u00fcre\u00e7te birden \u00e7ok event i\u00e7in farkl\u0131 kuyruk isimleri belirlememiz ve bu kuyruk isimlerini birden \u00e7ok yerde kullan\u0131labilir k\u0131lmam\u0131z gerekecektir.<\/p>\n<p>Haliyle <span style=\"color:purple;\">Shared<\/span> class library&#8217;si i\u00e7erisine <em>&#8216;RabbitMQSettings&#8217;<\/em> isimli static bir class ekleyerek i\u00e7erisine kuyruk isimlerini a\u015fa\u011f\u0131daki gibi ekleyelim.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public static class RabbitMQSettings\r\n    {\r\n        public const string Stock_OrderCreatedEventQueue = &quot;stock-order-created-queue&quot;;\r\n    }\r\n<\/pre>\n<p>Evet, yukar\u0131daki kod blo\u011funu incelerseniz e\u011fer tan\u0131mlanan kuyruk isimlerini o kuyru\u011fa subscribe olacak olan servisin ad\u0131n\u0131 belirterek olu\u015fturdu\u011fuma dikkatinizi \u00e7ekerim. Ve bundan sonra eklenecek olan kuyruk isimlerinde de ayn\u0131 formata riayet edece\u011fim. Bu \u015fimdilik irticalen yapt\u0131\u011f\u0131m ve kabul etti\u011fim bir formatt\u0131r. Sizler kendinize g\u00f6re farkl\u0131 formatlar belirleyebilirsiniz.\n<\/li>\n<li><strong><em>Ad\u0131m 5<\/em><\/strong> <em style=\"color:green;font-size:11px;\">Order.API |<\/em> <em style=\"color:purple;font-size:11px;\">OrderCreatedEvent Event&#8217;inin Kuyru\u011fa At\u0131lmas\u0131<\/em><br \/>\nArt\u0131k sipari\u015f olu\u015fturuldu\u011funda, bu sipari\u015fin olu\u015fturuldu\u011funu <span style=\"color:purple;\">Stock.API<\/span>&#8216;a bildirecek olan <code style=\"color:red;font-size:13px;\">OrderCreatedEvent<\/code> ve kuyruk ad\u0131 haz\u0131rlanm\u0131\u015f oldu\u011funa g\u00f6re tek yapmam\u0131z gereken &#8216;OrdersController.cs&#8217;de a\u015fa\u011f\u0131daki ekstralarda bulunmakt\u0131r;<\/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 IPublishEndpoint _publishEndpoint;\r\n        public OrdersController(\r\n            ApplicationDbContext applicationDbContext,\r\n            IPublishEndpoint publishEndpoint)\r\n        {\r\n            _applicationDbContext = applicationDbContext;\r\n            _publishEndpoint = publishEndpoint;\r\n        }\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            OrderCreatedEvent orderCreatedEvent = new()\r\n            {\r\n                OrderId = order.Id,\r\n                BuyerId = order.BuyerId,\r\n                TotalPrice = order.TotalPrice,\r\n                OrderItems = order.OrderItems.Select(oi =&gt; new 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            await _publishEndpoint.Publish(orderCreatedEvent);\r\n            return Ok(true);\r\n        }\r\n    }\r\n<\/pre>\n<p>Yukar\u0131daki kod blo\u011funu incelerseniz e\u011fer 6. sat\u0131rda tan\u0131mlanm\u0131\u015f olan <em>&#8216;IPublishEndpoint&#8217;<\/em> referans\u0131na dependency injection ile gerekli publish i\u015flemini icra edecek nesne talep edilmekte ve 36. sat\u0131rda \u00fcretilen <code style=\"color:red;font-size:13px;\">OrderCreatedEvent<\/code> nesnesi 48. sat\u0131rda <em>&#8216;Publish&#8217;<\/em> metoduyla RabbitMQ kuyru\u011funa g\u00f6nderilmektedir.<\/p>\n<p><strong><em>Hoca neden kuyruk ad\u0131n\u0131 burada kullanmad\u0131n?<\/em><\/strong> diye sordu\u011funuzu duyar gibiyim. Burada dikkat ederseniz e\u011fer bir event publish edilmektedir. Haliyle publish y\u00f6nteminde yay\u0131nlanan event her ne ise o event&#8217;e subscribe olan t\u00fcm consumer&#8217;lara bu mesaj iletilmi\u015f olacakt\u0131r. O y\u00fczden mesaj yay\u0131nlama s\u00fcrecinde herhangi bir kuyruk ad\u0131 bildirilmemi\u015ftir lakin birazdan bu event&#8217;e subscribe olacak olan ilgili servis(lerde)te kuyruk ad\u0131n\u0131n belirtildi\u011fini g\u00f6r\u00fcyor olaca\u011f\u0131z. Nihayetinde <em>-bu event&#8217;e subscribe olanlar \u015fu kuyruk ad\u0131ndan ilgili mesaj\u0131 als\u0131nlar-<\/em> dememiz gerekmektedir.\n<\/li>\n<li><strong><em>Ad\u0131m 6<\/em><\/strong> <em style=\"color:green;font-size:11px;\">Stock.API |<\/em> <em style=\"color:purple;font-size:11px;\">OrderCreatedEvent Event&#8217;e Subscribe Olunmas\u0131 ve Mesaj\u0131n Okunmas\u0131<\/em><br \/>\n<span style=\"color:purple;\">Order.API<\/span>&#8216;da olu\u015fturulan mesaj\u0131n <span style=\"color:purple;\">Stock.API<\/span>&#8216;da yakalanabilmesi i\u00e7in \u00f6ncelikle <span style=\"color:purple;\">Stock.API<\/span>&#8216;da bir consumer olu\u015fturulmas\u0131 gerekmektedir. Bunun i\u00e7in ilgili serviste &#8216;Consumers&#8217; isimli bir klas\u00f6r ekleyerek i\u00e7erisine <em>&#8216;OrderCreatedEventConsumer&#8217;<\/em> isimli bir s\u0131n\u0131f olu\u015ftural\u0131m.<\/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            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\r\n            \/\/ ***\r\n            \/\/ StockReservedEvent\r\n            \/\/ ***\r\n            \/\/ OR\r\n            \/\/ ***\r\n            \/\/ StockNotReservedEvent\r\n            \/\/ ***\r\n        }\r\n    }\r\n<\/pre>\n<p>Yukar\u0131daki consumer s\u0131n\u0131f\u0131na g\u00f6z atarsan\u0131z e\u011fer MassTransit k\u00fct\u00fcphanesinden gelen &#8216;IConsumer&lt;OrderCreatedEvent&gt;&#8217; interface&#8217;inden t\u00fcremektedir. Haliyle b\u00f6ylece bu s\u0131n\u0131f\u0131n <code style=\"color:red;font-size:13px;\">OrderCreatedEvent<\/code> t\u00fcr\u00fcnden event&#8217;e subscribe olan bir consumer oldu\u011fu resmile\u015fmektedir. \u0130lgili s\u0131n\u0131f\u0131n i\u00e7erisine bakarsan\u0131z MongoDB ile ilgili \u00e7al\u0131\u015fmalar\u0131n oldu\u011funu g\u00f6receksiniz. Evet, zenginlik maksatl\u0131 <span style=\"color:purple;\">Stock.API<\/span>&#8216;da MongoDB&#8217;yi kullanm\u0131\u015f bulunmaktay\u0131z. Burada ekstra olarak birazdan i\u00e7eri\u011fini verece\u011fimiz <em>&#8216;MongoDbService&#8217;<\/em> t\u00fcm MongoDB i\u015flevselli\u011fini \u00fcstlenmekte ve &#8216;Consume&#8217; metodu i\u00e7erisinde gerekli operasyonu y\u00fcr\u00fctmektedir.<\/p>\n<p>\u0130lgili metoda g\u00f6z atarsan\u0131z e\u011fer 23. sat\u0131rda sipari\u015f edilen \u00fcr\u00fcnlerin stok miktar\u0131n\u0131n sipari\u015f adedinden fazla olup olmad\u0131\u011f\u0131 kontrol edilmekte ve 27 ile 35. sat\u0131r aral\u0131\u011f\u0131nda fazla ise sipari\u015f edilen \u00fcr\u00fcnlerin stok miktarlar\u0131 d\u00fc\u015f\u00fcr\u00fclmektedir. Yaz\u0131m\u0131z\u0131n devam\u0131nda ise bu consumer taraf\u0131ndan i\u015flemlerin nihai olarak ba\u015far\u0131l\u0131 ya da ba\u015far\u0131s\u0131z olmas\u0131 durumuna g\u00f6re ya <code style=\"color:red;font-size:13px;\">StockReservedEvent<\/code> ya da <code style=\"color:red;font-size:13px;\">StockNotReservedEvent<\/code> event&#8217;leri publish edilecektir. Bu detaylar\u0131 ilgili ad\u0131mlara geldi\u011fimizde konu\u015faca\u011f\u0131z. \u015eimdilik kodu bu \u015fekilde b\u0131rak\u0131p yola devam ediyoruz.<\/p>\n<p>&#8216;MongoDbService&#8217; i\u00e7eri\u011fi :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class MongoDbService\r\n    {\r\n        readonly IMongoDatabase _database;\r\n        public MongoDbService()\r\n        {\r\n            MongoClient client = new(&quot;mongodb:\/\/localhost:27017&quot;);\r\n            _database = client.GetDatabase(&quot;SagaChoreographyDB&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><span style=\"color:purple;\">Stock.API<\/span>&#8216;da geli\u015ftirilen bu consumer&#8217;\u0131n uygulama taraf\u0131ndan ger\u00e7ek bir consumer oldu\u011funu ifade edebilmek i\u00e7in ilgili uygulaman\u0131n &#8216;Startup.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki konfig\u00fcrasyonun yap\u0131lmas\u0131 gerekmektedir.<\/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.AddConsumer&lt;OrderCreatedEventConsumer&gt;();\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.Stock_OrderCreatedEventQueue, e =&gt; e.ConfigureConsumer&lt;OrderCreatedEventConsumer&gt;(context));\r\n                });\r\n            });\r\n\r\n            services.AddSingleton&lt;MongoDbService&gt;();\r\n            .\r\n            .\r\n            .\r\n        }\r\n        .\r\n        .\r\n        .\r\n    }\r\n<\/pre>\n<p>Dikkat ederseniz 13. sat\u0131rda &#8216;AddConsumer&#8217; metodu ile geli\u015ftirilen <em>&#8216;OrderCreatedEventConsumer&#8217;<\/em> s\u0131n\u0131f\u0131 bu uygulamaya bir consumer olarak eklenmektedir. Ayr\u0131ca 18. sat\u0131rda ise eklenen bu consumer&#8217;a kuyruk ad\u0131 da verilmektedir. Ha tabi ayr\u0131ca 22. sat\u0131ra g\u00f6z atarsan\u0131z geli\u015ftirilen &#8216;MongoDbService&#8217;in de dependency injection ile eri\u015filebilir olmas\u0131 i\u00e7in uygulama provider&#8217;\u0131na eklendi\u011finden de g\u00f6zle ka\u015f aras\u0131nda bahsetmi\u015f olal\u0131m..<\/p>\n<p>Bu ad\u0131ma istinaden <em>&#8216;OrderCreatedEventConsumer&#8217;<\/em> i\u00e7erisinde kullan\u0131lan &#8216;Stock&#8217; entity&#8217;sinin i\u00e7eri\u011fini merak edenleriniz olacakt\u0131r. Buyrun onu da payla\u015fm\u0131\u015f olal\u0131m;<\/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 7<\/em><\/strong> <em style=\"color:green;font-size:11px;\">Shared |<\/em> <em style=\"color:purple;font-size:11px;\">StockReservedEvent ve StockNotReservedEvent Event&#8217;lerinin Olu\u015fturulmas\u0131<\/em><br \/>\n\u015eimdi s\u0131rada <span style=\"color:purple;\">Stock.API<\/span>&#8216;da yap\u0131lan stok i\u015flemlerinin ba\u015far\u0131l\u0131 ya da ba\u015far\u0131s\u0131z olma durumuna istinaden kuyru\u011fa at\u0131lacak olan <code style=\"color:red;font-size:13px;\">StockReservedEvent<\/code> ve <code style=\"color:red;font-size:13px;\">StockNotReservedEvent<\/code> event&#8217;lerinin olu\u015fturulmas\u0131 vard\u0131r.<\/p>\n<p>E\u011fer ki stok miktar\u0131n\u0131n d\u00fc\u015f\u00fcr\u00fclmesi ba\u015far\u0131yla ger\u00e7ekle\u015ftirildiyse <code style=\"color:red;font-size:13px;\">StockReservedEvent<\/code> event&#8217;i yay\u0131nlanacakt\u0131r. Bu event&#8217;e <span style=\"color:purple;\">Payment.API<\/span> subscribe olacak ve ona g\u00f6re \u00f6deme i\u015flemleri devreye girecektir. Yok e\u011fer stok miktar\u0131nda bir hata ya da tutars\u0131zl\u0131k meydana geliyorsa <code style=\"color:red;font-size:13px;\">StockNotReservedEvent<\/code> event&#8217;i yay\u0131lacakt\u0131r. Bu event&#8217;e de <span style=\"color:purple;\">Order.API<\/span> subscribe olacakt\u0131r ve olu\u015fturulan sipari\u015fin durumunu <span style=\"color:orange;\">Fail<\/span>&#8216;e \u00e7ekip s\u00fcreci sonland\u0131racakt\u0131r. Dolay\u0131s\u0131yla bu event&#8217;leri a\u015fa\u011f\u0131daki gibi ama\u00e7lar\u0131 do\u011frultusunda geli\u015ftirmemiz gerekmektedir;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockReservedEvent : IEvent\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<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockNotReservedEvent : IEvent\r\n    {\r\n        public int OrderId { get; set; }\r\n        public int BuyerId { get; set; }\r\n        public string Message { get; set; }\r\n    }\r\n<\/pre>\n<p>Evet, g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere s\u00fcrecin sa\u011fl\u0131kl\u0131 devam etti\u011fini ifade eden <code style=\"color:red;font-size:13px;\">StockReservedEvent<\/code> sonraki tetiklenecek microservis olan <span style=\"color:purple;\">Payment.API<\/span>&#8216;a gerekli sipari\u015f bilgilerini iletirken, stok miktar\u0131nda problem oldu\u011funu ifade eden <code style=\"color:red;font-size:13px;\">StockNotReservedEvent<\/code> hangi al\u0131c\u0131n\u0131n yapt\u0131\u011f\u0131 hangi sipari\u015fin neden(Message) ba\u015far\u0131s\u0131z oldu\u011funa dair bilgileri iletmektedir.\n<\/li>\n<li><strong><em>Ad\u0131m 8<\/em><\/strong> <em style=\"color:green;font-size:11px;\">Stock.API |<\/em> <em style=\"color:purple;font-size:11px;\">StockReservedEvent ve StockNotReservedEvent Event&#8217;lerinin Yay\u0131nlanmas\u0131<\/em><br \/>\n\u015eimdi olu\u015fturulan <code style=\"color:red;font-size:13px;\">StockReservedEvent<\/code> ve <code style=\"color:red;font-size:13px;\">StockNotReservedEvent<\/code> event&#8217;lerinin yay\u0131nlanmas\u0131yla ilgilenelim. Bunun i\u00e7in <span style=\"color:purple;\">Stock.API<\/span>&#8216;da biraz \u00f6nce(6. ad\u0131m) olu\u015fturulan consumer&#8217;a gelelim ve a\u015fa\u011f\u0131daki eklentilerde bulunal\u0131m.<\/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            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                ISendEndpoint sendEndpoint = await _sendEndpointProvider.GetSendEndpoint(new Uri($&quot;queue:{RabbitMQSettings.Payment_StockReservedEventQueue}&quot;));\r\n                StockReservedEvent stockReservedEvent = new()\r\n                {\r\n                    BuyerId = context.Message.BuyerId,\r\n                    OrderId = context.Message.OrderId,\r\n                    OrderItems = context.Message.OrderItems,\r\n                    TotalPrice = context.Message.TotalPrice\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()\r\n                {\r\n                    BuyerId = context.Message.BuyerId,\r\n                    OrderId = context.Message.OrderId,\r\n                    Message = &quot;Stok yetersiz...&quot;\r\n                };\r\n                await _publishEndpoint.Publish(stockNotReservedEvent);\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<p>Yukar\u0131daki kod blo\u011funu incelerseniz e\u011fer 36 ile 44. sat\u0131rlar aras\u0131nda <code style=\"color:red;font-size:13px;\">StockReservedEvent<\/code> event&#8217;i 49 ile 55. sat\u0131rlar aras\u0131nda ise <code style=\"color:red;font-size:13px;\">StockNotReservedEvent<\/code>&#8216;i yay\u0131nlanmaktad\u0131r. <code style=\"color:red;font-size:13px;\">StockReservedEvent<\/code> event&#8217;ine odaklan\u0131rsak e\u011fer publish de\u011fil send i\u015flemi ile kuyru\u011fa g\u00f6nderildi\u011fini g\u00f6rmekteyiz. Bunun nedeni, bu event&#8217;a sade ve sadece tek bir servisin(<span style=\"color:purple;\">Payment.API<\/span>) subscribe olaca\u011f\u0131d\u0131r. Yani <span style=\"color:purple;\">Payment.API<\/span>&#8216;\u0131n d\u0131\u015f\u0131nda ba\u015fka bir servis bu event&#8217;i dinlemeyecektir. Haliyle bizler de send ile direkt hedef olarak belirlenen kuyru\u011fa bu event&#8217;i atmaktay\u0131z. Ha tabi sizler isterseniz publish olarak da yay\u0131nlayabilirsiniz. Yine maksad\u0131m\u0131z zenginlik olsun \ud83d\ude42<\/p>\n<p><code style=\"color:red;font-size:13px;\">StockReservedEvent<\/code> event&#8217;ine odakland\u0131\u011f\u0131m\u0131zda ise \u00f6nceki i\u015flemlerde tecr\u00fcbe edildi\u011fi gibi publish ile ilgili event yay\u0131nlanmaktad\u0131r.<\/p>\n<p>Bu ad\u0131mda <em>&#8216;RabbitMQSettings&#8217;<\/em> s\u0131n\u0131f\u0131n\u0131n en g\u00fcncel i\u00e7eri\u011fi a\u015fa\u011f\u0131daki gibi olacakt\u0131r;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public static class RabbitMQSettings\r\n    {\r\n        public const string Stock_OrderCreatedEventQueue = &quot;stock-order-created-queue&quot;;\r\n        public const string Payment_StockReservedEventQueue = &quot;payment-stock-reserved-queue&quot;;\r\n    }\r\n<\/pre>\n<\/li>\n<li><strong><em>Ad\u0131m 9<\/em><\/strong> <em style=\"color:green;font-size:11px;\">Payment.API |<\/em> <em style=\"color:purple;font-size:11px;\">StockReservedEvent Event&#8217;ine Subscribe Olunmas\u0131 ve Mesaj\u0131n Okunmas\u0131<\/em><br \/>\n<span style=\"color:purple;\">Order.API<\/span>&#8216;da sipari\u015f olu\u015fturulup, <span style=\"color:purple;\">Stock.API<\/span>&#8216;da stok i\u015flemleri de ba\u015far\u0131yla ger\u00e7ekle\u015ftirildikten sonra art\u0131k s\u0131rada \u00f6deme i\u015flemleri vard\u0131r. Ve bu i\u015flemi malumunuz <span style=\"color:purple;\">Payment.API<\/span>&#8216;da ger\u00e7ekle\u015ftiriyor olaca\u011f\u0131z. Bu uygulamada da art\u0131k ilgili <code style=\"color:red;font-size:13px;\">StockReservedEvent<\/code> event&#8217;ini dinleyecek olan bir consumer geli\u015ftirmeliyiz. Bunun i\u00e7in ilgili uygulamada &#8216;Consumers&#8217; isimli klas\u00f6r i\u00e7erisine <em>&#8216;StockReservedEventConsumer&#8217;<\/em> isimli bir s\u0131n\u0131f olu\u015ftural\u0131m.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockReservedEventConsumer : IConsumer&lt;StockReservedEvent&gt;\r\n    {\r\n        readonly IPublishEndpoint _publishEndpoint;\r\n\r\n        public StockReservedEventConsumer(IPublishEndpoint publishEndpoint)\r\n        {\r\n            _publishEndpoint = publishEndpoint;\r\n        }\r\n\r\n        public async Task Consume(ConsumeContext&lt;StockReservedEvent&gt; context)\r\n        {\r\n            \/\/ Process ...\r\n            \/\/ Process ...\r\n            \/\/ Process ...\r\n            bool paymentState = true;\r\n\r\n            \/\/\u00d6deme ba\u015far\u0131l\u0131ysa\r\n            if (paymentState)\r\n            {\r\n                Console.WriteLine(&quot;\u00d6deme ba\u015far\u0131l\u0131...&quot;);\r\n                \/\/ ***\r\n                \/\/ PaymentCompletedEvent\r\n                \/\/ ***\r\n            }\r\n            else\r\n            {\r\n                Console.WriteLine(&quot;\u00d6deme ba\u015far\u0131s\u0131z...&quot;);\r\n                \/\/ ***\r\n                \/\/ PaymentFailedEvent\r\n                \/\/ ***\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<p>Art\u0131k burada derman\u0131m\u0131z kalmad\u0131\u011f\u0131ndan dolay\u0131 \u00f6deme i\u015flemi farazi bir \u015fekilde es ge\u00e7ilmi\u015ftir. Lakin \u00f6nemli olan \u00f6deme neticesine g\u00f6re ba\u015far\u0131l\u0131 ya da ba\u015far\u0131s\u0131z bir event yay\u0131nlama i\u015flemi ger\u00e7ekle\u015ftirilecektir.<\/p>\n<p>Tabi olu\u015fturulan bu consumer&#8217;\u0131n <span style=\"color:purple;\">Payment.API<\/span>&#8216;\u0131n &#8216;Startup.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki gibi eklenmi\u015f olmas\u0131 gerekmektedir.<\/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;StockReservedEventConsumer&gt;();\r\n                configure.UsingRabbitMq((context, configurator) =&gt;\r\n                {\r\n                    configurator.Host(Configuration.GetConnectionString(&quot;RabbitMQ&quot;));\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Payment_StockReservedEventQueue, e =&gt; e.ConfigureConsumer&lt;StockReservedEventConsumer&gt;(context));\r\n                });\r\n            });\r\n            .\r\n            .\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;\">Shared |<\/em> <em style=\"color:purple;font-size:11px;\">PaymentCompletedEvent ve PaymentFailedEvent Event&#8217;lerinin Olu\u015fturulmas\u0131<\/em><br \/>\n<span style=\"color:purple;\">Payment.API<\/span>&#8216;da ki sonu\u00e7 ba\u015far\u0131l\u0131ysa <code style=\"color:red;font-size:13px;\">PaymentCompletedEvent<\/code> yok e\u011fer ba\u015far\u0131s\u0131zsa <code style=\"color:red;font-size:13px;\">PaymentFailedEvent<\/code> event&#8217;leri yay\u0131nlanacakt\u0131r. <code style=\"color:red;font-size:13px;\">PaymentCompletedEvent<\/code> ile sipari\u015f ba\u015far\u0131l\u0131 bir \u015fekilde tamamlanm\u0131\u015f olacak ve durumu <span style=\"color:orange;\">Completed<\/span>&#8216;a \u00e7ekilecektir. <code style=\"color:red;font-size:13px;\">PaymentFailedEvent<\/code> event&#8217;i ile sipari\u015f iptal edilerek <span style=\"color:orange;\">Fail<\/span> durumuna getirilecek hem de <span style=\"color:purple;\">Stock.API<\/span>&#8216;da ki yap\u0131lan de\u011fi\u015fiklikler compensable transaction ile telafi edilecektir\/geri al\u0131nacakt\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentCompletedEvent : IEvent\r\n    {\r\n        public int OrderId { get; set; }\r\n    }\r\n<\/pre>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentFailedEvent : IEvent\r\n    {\r\n        public int OrderId { get; set; }\r\n        public string Message { get; set; }\r\n        public List&lt;OrderItemMessage&gt; OrderItems { get; set; }\r\n    }\r\n<\/pre>\n<p>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere her iki event&#8217;te de gerekli bilgiler tutulmaktad\u0131r. <code style=\"color:red;font-size:13px;\">PaymentCompletedEvent<\/code> event&#8217;in de hangi sipari\u015fin ba\u015far\u0131yla sonland\u0131r\u0131laca\u011f\u0131 bilgisi gelirken <code style=\"color:red;font-size:13px;\">PaymentFailedEvent<\/code> event&#8217;in de ise hangi sipari\u015fin neden iptal edildi\u011fi bilgileri gelmektedir.\n<\/li>\n<li><strong><em>Ad\u0131m 11<\/em><\/strong> <em style=\"color:green;font-size:11px;\">Payment.API |<\/em> <em style=\"color:purple;font-size:11px;\">PaymentCompletedEvent ve PaymentFailedEvent Event&#8217;lerinin Yay\u0131nlanmas\u0131<\/em><br \/>\n<span style=\"color:purple;\">Payment.API<\/span> servisinde \u00f6deme sonucuna g\u00f6re bir ad\u0131m \u00f6nce olu\u015fturulan <code style=\"color:red;font-size:13px;\">PaymentCompletedEvent<\/code> ve <code style=\"color:red;font-size:13px;\">PaymentFailedEvent<\/code> event&#8217;lerinin yay\u0131nlanmas\u0131 i\u00e7in 9. ad\u0131mda geli\u015ftirilen <em>&#8216;StockReservedEventConsumer&#8217;<\/em> isimli consumer&#8217;a a\u015fa\u011f\u0131daki m\u00fcdahalenin yap\u0131lmas\u0131 gerekmektedir.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockReservedEventConsumer : IConsumer&lt;StockReservedEvent&gt;\r\n    {\r\n        readonly IPublishEndpoint _publishEndpoint;\r\n\r\n        public StockReservedEventConsumer(IPublishEndpoint publishEndpoint)\r\n        {\r\n            _publishEndpoint = publishEndpoint;\r\n        }\r\n\r\n        public async Task Consume(ConsumeContext&lt;StockReservedEvent&gt; context)\r\n        {\r\n            \/\/ Process ...\r\n            \/\/ Process ...\r\n            \/\/ Process ...\r\n            bool paymentState = true;\r\n\r\n            \/\/\u00d6deme ba\u015far\u0131l\u0131ysa\r\n            if (paymentState)\r\n            {\r\n                Console.WriteLine(&quot;\u00d6deme ba\u015far\u0131l\u0131...&quot;);\r\n                PaymentCompletedEvent paymentCompletedEvent = new()\r\n                {\r\n                    OrderId = context.Message.OrderId\r\n                };\r\n                await _publishEndpoint.Publish(paymentCompletedEvent);\r\n            }\r\n            else\r\n            {\r\n                Console.WriteLine(&quot;\u00d6deme ba\u015far\u0131s\u0131z...&quot;);\r\n                PaymentFailedEvent paymentFailedEvent = new()\r\n                {\r\n                    OrderId = context.Message.OrderId,\r\n                    OrderItems = context.Message.OrderItems,\r\n                    Message = &quot;Bakiye yetersiz!&quot;\r\n                };\r\n                await _publishEndpoint.Publish(paymentFailedEvent);\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<p>21 ile 25. sat\u0131r aral\u0131\u011f\u0131na bakarsan\u0131z <code style=\"color:red;font-size:13px;\">PaymentCompletedEvent<\/code>, 30 ile 35. sat\u0131r aral\u0131\u011f\u0131na bakarsan\u0131z e\u011fer <code style=\"color:red;font-size:13px;\">PaymentFailedEvent<\/code> event&#8217;leri yay\u0131nlanmaktad\u0131r. \u015eimdi gelin bu event&#8217;lerin consumer&#8217;lar\u0131n\u0131 olu\u015ftural\u0131m.\n<\/li>\n<li><strong><em>Ad\u0131m 12<\/em><\/strong> <em style=\"color:green;font-size:11px;\">Order.API |<\/em> <em style=\"color:purple;font-size:11px;\">PaymentCompletedEvent Event&#8217;ine Subscribe Olunmas\u0131 ve Mesaj\u0131n Okunmas\u0131<\/em><br \/>\n<code style=\"color:red;font-size:13px;\">PaymentCompletedEvent<\/code> event&#8217;i yay\u0131nland\u0131\u011f\u0131 zaman <span style=\"color:purple;\">Order.API<\/span> ilgili sipari\u015fi ba\u015far\u0131l\u0131 bir \u015fekilde sonu\u00e7land\u0131rmal\u0131d\u0131r. Bunun i\u00e7in <span style=\"color:purple;\">Order.API<\/span> servisinde ilgili event&#8217;i t\u00fcketecek bir consumer olu\u015fturulmal\u0131d\u0131r. Haliyle &#8216;Consumers&#8217; isimli klas\u00f6r alt\u0131nda <em>&#8216;PaymentCompletedEventConsumer&#8217;<\/em> isimli bir s\u0131n\u0131f olu\u015ftural\u0131m.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentCompletedEventConsumer : IConsumer&lt;PaymentCompletedEvent&gt;\r\n    {\r\n        readonly ApplicationDbContext _applicationDbContext;\r\n\r\n        public PaymentCompletedEventConsumer(ApplicationDbContext applicationDbContext)\r\n        {\r\n            _applicationDbContext = applicationDbContext;\r\n        }\r\n\r\n        public async Task Consume(ConsumeContext&lt;PaymentCompletedEvent&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>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere <code style=\"color:red;font-size:13px;\">PaymentCompletedEvent<\/code> yay\u0131nland\u0131\u011f\u0131 anda bu consumer devreye girecek ve ilgili sipari\u015fin durumunu <span style=\"color:orange;\">Completed<\/span>&#8216;a \u00e7ekecektir. Bu s\u0131n\u0131f\u0131 ilgili uygulaman\u0131n &#8216;Startup.cs&#8217; dosyas\u0131nda a\u015fa\u011f\u0131daki gibi consumer olarak ekleyelim;<\/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;PaymentCompletedEventConsumer&gt;();\r\n                configure.UsingRabbitMq((context, configurator) =&gt;\r\n                {\r\n                    configurator.Host(Configuration.GetConnectionString(&quot;RabbitMQ&quot;));\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Order_PaymentCompletedEventQueue, e =&gt; e.ConfigureConsumer&lt;PaymentCompletedEventConsumer&gt;(context));\r\n                });\r\n            });\r\n            .\r\n            .\r\n            .\r\n        }\r\n<\/pre>\n<p>Bu ad\u0131mda <em>&#8216;RabbitMQSettings&#8217;<\/em> s\u0131n\u0131f\u0131n\u0131n son hali a\u015fa\u011f\u0131daki gibidir :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public static class RabbitMQSettings\r\n    {\r\n        public const string Stock_OrderCreatedEventQueue = &quot;stock-order-created-queue&quot;;\r\n        public const string Payment_StockReservedEventQueue = &quot;payment-stock-reserved-queue&quot;;\r\n        public const string Order_PaymentCompletedEventQueue = &quot;order-payment-completed-queue&quot;;\r\n    }\r\n<\/pre>\n<\/li>\n<li><strong><em>Ad\u0131m 13<\/em><\/strong> <em style=\"color:green;font-size:11px;\">Order.API &#8211; Stock.API |<\/em> <em style=\"color:purple;font-size:11px;\">PaymentFailedEvent Event&#8217;ine Subscribe Olunmas\u0131 ve Mesaj\u0131n Okunmas\u0131<\/em><br \/>\n\u015eimdi s\u0131rada <span style=\"color:purple;\">Payment.API<\/span>&#8216;da olu\u015fan olumsuz bir durumdan dolay\u0131 yay\u0131nlanan <code style=\"color:red;font-size:13px;\">PaymentFailedEvent<\/code> event&#8217;ini <span style=\"color:purple;\">Order.API<\/span> ve <span style=\"color:purple;\">Stock.API<\/span> servislerinde dinleyecek consumer&#8217;lar\u0131 yazmaya geldi. Bunun i\u00e7in her iki servisin &#8216;Consumers&#8217; klas\u00f6r\u00fc alt\u0131nda <em>&#8216;PaymentFailedEventConsumer&#8217;<\/em> isimli consumer s\u0131n\u0131flar\u0131n\u0131n olu\u015fturulmas\u0131 gerekmektedir.<\/p>\n<p><span style=\"color:purple;\">Order.API<\/span> i\u00e7in;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentFailedEventConsumer : IConsumer&lt;PaymentFailedEvent&gt;\r\n    {\r\n        readonly ApplicationDbContext _context;\r\n        public PaymentFailedEventConsumer(ApplicationDbContext context)\r\n        {\r\n            _context = context;\r\n        }\r\n        public async Task Consume(ConsumeContext&lt;PaymentFailedEvent&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<p>Evet, g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere <span style=\"color:purple;\">Order.API<\/span> servisinde ilgili sipari\u015f durumu <span style=\"color:orange;\">Fail<\/span> yap\u0131larak iptal edilmektedir.<\/p>\n<p><span style=\"color:purple;\">Stock.API<\/span> i\u00e7in;<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class PaymentFailedEventConsumer : IConsumer&lt;PaymentFailedEvent&gt;\r\n    {\r\n        readonly MongoDbService _mongoDbService;\r\n        public PaymentFailedEventConsumer(MongoDbService mongoDbService)\r\n        {\r\n            _mongoDbService = mongoDbService;\r\n        }\r\n        public async Task Consume(ConsumeContext&lt;PaymentFailedEvent&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<p><span style=\"color:purple;\">Stock.API<\/span> servisinde ise bu sipari\u015ften dolay\u0131 stok miktar\u0131 d\u00fc\u015f\u00fcr\u00fclen t\u00fcm \u00fcr\u00fcnlerin de\u011ferleri compensable transaction ile telafi edilerek geri al\u0131nmaktad\u0131r.<\/p>\n<p>Haliyle her iki servisin &#8216;Startup.cs&#8217; dosyas\u0131nda kendilerine \u00f6zel olu\u015fturulan consumer&#8217;lar i\u00e7in gerekli konfig\u00fcrasyonlar a\u015fa\u011f\u0131daki gibi yap\u0131lmal\u0131d\u0131r;<br \/>\n<span style=\"color:purple;\">Order.API<\/span> i\u00e7in;<\/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;PaymentCompletedEventConsumer&gt;();\r\n                configure.AddConsumer&lt;PaymentFailedEventConsumer&gt;();\r\n                configure.UsingRabbitMq((context, configurator) =&gt;\r\n                {\r\n                    configurator.Host(Configuration.GetConnectionString(&quot;RabbitMQ&quot;));\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Order_PaymentCompletedEventQueue, e =&gt; e.ConfigureConsumer&lt;PaymentCompletedEventConsumer&gt;(context));\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Order_PaymentFailedEventQueue, e =&gt; e.ConfigureConsumer&lt;PaymentFailedEventConsumer&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> i\u00e7in;<\/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;PaymentFailedEventConsumer&gt;();\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.Stock_OrderCreatedEventQueue, e =&gt; e.ConfigureConsumer&lt;OrderCreatedEventConsumer&gt;(context));\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Stock_PaymentFailedEventQueue, e =&gt; e.ConfigureConsumer&lt;PaymentFailedEventConsumer&gt;(context));\r\n                });\r\n            });\r\n            .\r\n            .\r\n            .\r\n        }\r\n<\/pre>\n<p>Bu ad\u0131mda <em>&#8216;RabbitMQSettings&#8217;<\/em> s\u0131n\u0131f\u0131n\u0131n son hali a\u015fa\u011f\u0131daki gibidir :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public static class RabbitMQSettings\r\n    {\r\n        public const string Stock_OrderCreatedEventQueue = &quot;stock-order-created-queue&quot;;\r\n        public const string Payment_StockReservedEventQueue = &quot;payment-stock-reserved-queue&quot;;\r\n        public const string Order_PaymentCompletedEventQueue = &quot;order-payment-completed-queue&quot;;\r\n        public const string Order_PaymentFailedEventQueue = &quot;order-payment-failed-queue&quot;;\r\n        public const string Stock_PaymentFailedEventQueue = &quot;stock-payment-failed-queue&quot;;\r\n    }\r\n<\/pre>\n<\/li>\n<li><strong><em>Ad\u0131m 14<\/em><\/strong> <em style=\"color:green;font-size:11px;\">Order.API |<\/em> <em style=\"color:purple;font-size:11px;\">StockNotReservedEvent Event&#8217;ine Subscribe Olunmas\u0131 ve Mesaj\u0131n Okunmas\u0131<\/em><br \/>\nSon olarak <span style=\"color:purple;\">Stock.API<\/span>&#8216;da olabilecek herhangi bir hata yahut tutars\u0131zl\u0131k durumunda <span style=\"color:purple;\">Payment.API<\/span>&#8216;a gitmeksizin direkt olarak sipari\u015fin iptal edilmesi i\u00e7in <code style=\"color:red;font-size:13px;\">StockNotReservedEvent<\/code> event&#8217;i yay\u0131nlanmaktad\u0131r. Dolay\u0131s\u0131yla <span style=\"color:purple;\">Order.API<\/span>&#8216;da bu event&#8217;e subscribe olup gerekli iptal i\u015fleminin ger\u00e7ekle\u015ftirilmesi gerekmektedir. Bunun i\u00e7in &#8216;Consumers&#8217; klas\u00f6r\u00fc i\u00e7erisine <em>&#8216;StockNotReservedEventConsumer&#8217;<\/em> isimli bir consumer s\u0131n\u0131f\u0131 olu\u015ftural\u0131m.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class StockNotReservedEventConsumer : IConsumer&lt;StockNotReservedEvent&gt;\r\n    {\r\n        readonly ApplicationDbContext _context;\r\n        public StockNotReservedEventConsumer(ApplicationDbContext context)\r\n        {\r\n            _context = context;\r\n        }\r\n        public async Task Consume(ConsumeContext&lt;StockNotReservedEvent&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<p>Haliyle g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere ilgili sipari\u015f durumu <span style=\"color:orange;\">Fail<\/span> olarak belirtilmekte ve b\u00f6ylece iptal edilmektedir.<\/p>\n<p>Bu consumer&#8217;\u0131da &#8216;Startup.cs&#8217;e eklemeyi unutmuyoruz.<\/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;PaymentCompletedEventConsumer&gt;();\r\n                configure.AddConsumer&lt;PaymentFailedEventConsumer&gt;();\r\n                configure.AddConsumer&lt;StockNotReservedEventConsumer&gt;();\r\n                configure.UsingRabbitMq((context, configurator) =&gt;\r\n                {\r\n                    configurator.Host(Configuration.GetConnectionString(&quot;RabbitMQ&quot;));\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Order_PaymentCompletedEventQueue, e =&gt; e.ConfigureConsumer&lt;PaymentCompletedEventConsumer&gt;(context));\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Order_PaymentFailedEventQueue, e =&gt; e.ConfigureConsumer&lt;PaymentFailedEventConsumer&gt;(context));\r\n                    configurator.ReceiveEndpoint(RabbitMQSettings.Order_StockNotReservedEventQueue, e =&gt; e.ConfigureConsumer&lt;StockNotReservedEventConsumer&gt;(context));\r\n                });\r\n            });\r\n            .\r\n            .\r\n            .\r\n        }\r\n<\/pre>\n<p>Bu ad\u0131mda da <em>&#8216;RabbitMQSettings&#8217;<\/em> s\u0131n\u0131f\u0131n\u0131n son hali nihai olarak a\u015fa\u011f\u0131daki gibi olacakt\u0131r.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public static class RabbitMQSettings\r\n    {\r\n        public const string Stock_OrderCreatedEventQueue = &quot;stock-order-created-queue&quot;;\r\n        public const string Payment_StockReservedEventQueue = &quot;payment-stock-reserved-queue&quot;;\r\n        public const string Order_PaymentCompletedEventQueue = &quot;order-payment-completed-queue&quot;;\r\n        public const string Order_PaymentFailedEventQueue = &quot;order-payment-failed-queue&quot;;\r\n        public const string Stock_PaymentFailedEventQueue = &quot;stock-payment-failed-queue&quot;;\r\n        public const string Order_StockNotReservedEventQueue = &quot;order-stock-not-reserved-queue&quot;;\r\n    }\r\n<\/pre>\n<\/li>\n<\/ul>\n<h3>Test Edelim<\/h3>\n<p>Evet, art\u0131k yapt\u0131\u011f\u0131m\u0131z microservice \u00e7al\u0131\u015fmas\u0131n\u0131 ve Saga pattern ile distributed transaction uygulamas\u0131n\u0131 ba\u015ftan aya\u011fa test edebiliriz. Bunun i\u00e7in \u00f6ncelik olarak servislerin portlar\u0131n\u0131 ve MongoDB&#8217;de bir ka\u00e7 dummy data yap\u0131lanmas\u0131n\u0131 ayarlamam\u0131z gerekmektedir.<\/p>\n<table>\n<tbody>\n<tr>\n<td><strong>Order.API<\/strong><\/td>\n<td><code style=\"color:red;\">https:\/\/localhost:5001<\/code><\/td>\n<\/tr>\n<tr>\n<td><strong>Stock.API<\/strong><\/td>\n<td><code style=\"color:red;\">https:\/\/localhost:5003<\/code><\/td>\n<\/tr>\n<tr>\n<td><strong>Payment.API<\/strong><\/td>\n<td><code style=\"color:red;\">https:\/\/localhost:5005<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"color:purple;\">Stock.API<\/span>&#8216;\u0131n &#8216;Program.cs&#8217; dosyas\u0131na girelim ve a\u015fa\u011f\u0131daki dummy datalar\u0131 MongoDB&#8217;ye y\u00fckleyecek olan konfig\u00fcrasyonu ger\u00e7ekle\u015ftirelim.<\/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>Art\u0131k bu ayarlardan sonra uygulamam\u0131z teste haz\u0131r. \u015eimdi testimizi ger\u00e7ekle\u015ftirebiliriz.<br \/>\n<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi.png\" alt=\"Microservice - Saga - Events\/Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"489\" height=\"806\" class=\"aligncenter size-full wp-image-22538\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi.png 489w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-182x300.png 182w\" sizes=\"auto, (max-width: 489px) 100vw, 489px\" \/><\/a>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere MonboDB&#8217;ye girilen dummy datalara uygun verilerde bir sipari\u015f post edildi\u011fi taktirde<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-1.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-1.png\" alt=\"Microservice - Saga - Events Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"503\" height=\"47\" class=\"aligncenter size-full wp-image-22539\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-1.png 503w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-1-300x28.png 300w\" sizes=\"auto, (max-width: 503px) 100vw, 503px\" \/><\/a> ba\u015far\u0131l\u0131 bir sipari\u015f s\u00f6z konusu olabilmektedir.<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-2.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-2.png\" alt=\"Microservice - Saga - Events Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"418\" height=\"237\" class=\"aligncenter size-full wp-image-22540\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-2.png 418w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-2-300x170.png 300w\" sizes=\"auto, (max-width: 418px) 100vw, 418px\" \/><\/a>Veritaban\u0131na bak\u0131ld\u0131\u011f\u0131nda da verilerin stok miktarlar\u0131n\u0131n tutarl\u0131 bir \u015fekilde d\u00fc\u015ft\u00fc\u011f\u00fcn\u00fc g\u00f6zlemlemekteyiz.<\/p>\n<p><a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-3.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-3.png\" alt=\"Microservice - Saga - Events Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"399\" height=\"745\" class=\"aligncenter size-full wp-image-22542\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-3.png 399w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-3-161x300.png 161w\" sizes=\"auto, (max-width: 399px) 100vw, 399px\" \/><\/a>Benzer mant\u0131kla stok miktar\u0131 tutars\u0131z olan \u00fcr\u00fcnler sipari\u015f edildi\u011finde<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-4.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-4.png\" alt=\"Microservice - Saga - Events Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"500\" height=\"64\" class=\"aligncenter size-full wp-image-22543\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-4.png 500w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-4-300x38.png 300w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/a>sipari\u015f durumu <span style=\"color:orange;\">Fail<\/span> olarak d\u00fczenlenmektedir. Ve hatta <span style=\"color:purple;\">Order.API<\/span>&#8216;\u0131n console&#8217;una g\u00f6z atarsak bu durumun nedeni mesaj olarak kar\u015f\u0131m\u0131za \u00e7\u0131kmaktad\u0131r.<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-5.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-5.png\" alt=\"Microservice - Saga - Events Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"576\" height=\"60\" class=\"aligncenter size-full wp-image-22544\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-5.png 576w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-5-300x31.png 300w\" sizes=\"auto, (max-width: 576px) 100vw, 576px\" \/><\/a>MongoDB&#8217;ye g\u00f6z atarsak e\u011fer verilerin en sonuncu halinde oldu\u011funu g\u00f6rmekteyiz.<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-2.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-2.png\" alt=\"Microservice - Saga - Events Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"418\" height=\"237\" class=\"aligncenter size-full wp-image-22540\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-2.png 418w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-2-300x170.png 300w\" sizes=\"auto, (max-width: 418px) 100vw, 418px\" \/><\/a><\/p>\n<p>Ve son olarak <span style=\"color:purple;\">Payment.API<\/span>&#8216;da farazi olarak yapt\u0131\u011f\u0131m\u0131z \u00f6deme i\u015flemini &#8216;false&#8217; yap\u0131p ba\u015far\u0131s\u0131z duruma getirerek son durumu test edersek e\u011fer;<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-6.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-6.png\" alt=\"Microservice - Saga - Events Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"577\" height=\"43\" class=\"aligncenter size-full wp-image-22547\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-6.png 577w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-6-300x22.png 300w\" sizes=\"auto, (max-width: 577px) 100vw, 577px\" \/><\/a>ekran g\u00f6r\u00fcnt\u00fcs\u00fcnde de g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere &#8216;Bakiye yetersiz!&#8217; mesaj\u0131yla kar\u015f\u0131la\u015f\u0131lmakta ve stok bilgileri compensable transaction ile telafi edilerek geri al\u0131nmakta ve sipari\u015f iptal edilmektedir.<\/p>\n<p>Ayr\u0131ca bu servislerin RabbitMQ&#8217;da ki connection durumlar\u0131na da g\u00f6z atarsak e\u011fer<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-7.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-7.png\" alt=\"Microservice - Saga - Events Choreography Implemantasyonu \u0130le Transaction Y\u00f6netimi\" width=\"714\" height=\"431\" class=\"aligncenter size-full wp-image-22548\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-7.png 714w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/Microservice-Saga-Events-Choreography-Implemantasyonu-Ile-Transaction-Yonetimi-7-300x181.png 300w\" sizes=\"auto, (max-width: 714px) 100vw, 714px\" \/><\/a> \u015feklinde oldu\u011funu g\u00f6zlemlemekteyiz.<\/p>\n<p><strong>Nihai olarak;<\/strong><br \/>\nDistributed transaction senaryolar\u0131nda microservisler aras\u0131nda tutarl\u0131l\u0131\u011f\u0131 sa\u011flayabilmek i\u00e7in Saga pattern&#8217;\u0131n choreography implemantasyonunu ad\u0131m ad\u0131m hep beraber tecr\u00fcbe etmi\u015f olduk. Dikkat ederseniz e\u011fer her bir aksiyona kar\u015f\u0131l\u0131k bir event yay\u0131nlanmakta ve bu event&#8217;e subscribe olan consumer&#8217;lar arac\u0131l\u0131\u011f\u0131yla ilgili servisin alaca\u011f\u0131 aksiyon belirlenmektedir. Haliyle bu yakla\u015f\u0131mda servis say\u0131s\u0131 artt\u0131k\u00e7a ister istemez kuyruklar\u0131n y\u00f6netimi ve koordinasyonu zorla\u015facak ve bir yerden sonra geli\u015fimsel a\u00e7\u0131dan son raddeye var\u0131lm\u0131\u015f olunacakt\u0131r. Haliyle servis say\u0131s\u0131 2 ile 4 aras\u0131nda adil bir say\u0131ya tekab\u00fcl eden \u00e7al\u0131\u015fmalar da choreography implemantasyonunun tercih edilmesi makul g\u00f6r\u00fcl\u00fcrken daha fazla servisin s\u00f6z konusu oldu\u011fu durumlarda bir sonraki makalemizde pratiksel olarak yine benzer bir senaryo \u00fczerinden ele alaca\u011f\u0131m\u0131z Orchestrator implemantasyonunu uygulamam\u0131z daha ideal bir \u00e7\u00f6z\u00fcm getirmi\u015f olacakt\u0131r. O halde sizler bu makaleyi sindirirken, ben de biraz dinlendikten sonra di\u011fer makaleyi klavyeye almaya ba\u015fl\u0131yor olaca\u011f\u0131m \ud83d\ude42<\/p>\n<p>Bu noktaya kadar \u00fc\u015fenmeden okuyup, e\u015flik etti\u011finiz i\u00e7in te\u015fekk\u00fcr ederim&#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 indirmek i\u00e7in <a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2021\/08\/SagaChoreographyExample.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, Bir \u00f6nceki Microservice Mimarilerde Saga Pattern \u0130le Transaction Y\u00f6netimi ba\u015fl\u0131kl\u0131 makalemizde Saga pattern \u00fczerine detayl\u0131ca teorik incelemede bulunmu\u015ftuk. Bu i\u00e7eri\u011fimizde ise Saga pattern&#8217;\u0131n\u0131 Events\/Choreography implemantasyonu \u00e7er\u00e7evesinde ele alacak ve pratikte nas\u0131l bir in\u015fan\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":22316,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2739,153,3573],"tags":[4376,3575,4368,4377,4375,4367],"class_list":["post-22393","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net-core","category-design-pattern","category-microservices","tag-choreography","tag-microservice","tag-saga-choreography","tag-saga-choreography-example","tag-saga-events","tag-saga-events-choreography"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/22393","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=22393"}],"version-history":[{"count":147,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/22393\/revisions"}],"predecessor-version":[{"id":22550,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/22393\/revisions\/22550"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media\/22316"}],"wp:attachment":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media?parent=22393"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/categories?post=22393"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/tags?post=22393"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}