﻿
{"id":26937,"date":"2024-04-06T22:08:46","date_gmt":"2024-04-06T22:08:46","guid":{"rendered":"https:\/\/www.gencayyildiz.com\/blog\/?p=26937"},"modified":"2024-04-06T22:08:46","modified_gmt":"2024-04-06T22:08:46","slug":"mediatr-kutuphanesi-esliginde-cross-cutting-concernsccc-manevralari","status":"publish","type":"post","link":"https:\/\/www.gencayyildiz.com\/blog\/mediatr-kutuphanesi-esliginde-cross-cutting-concernsccc-manevralari\/","title":{"rendered":"MediatR K\u00fct\u00fcphanesi E\u015fli\u011finde Cross-Cutting Concerns(CCC) Manevralar\u0131"},"content":{"rendered":"<div id=\"fb-root\"><\/div>\n<p>Merhaba,<\/p>\n<p>Bu i\u00e7eri\u011fimizde bir yaz\u0131l\u0131m sisteminin farkl\u0131 par\u00e7alar\u0131 aras\u0131nda yayg\u0131n olarak bulunan ve bir\u00e7ok farkl\u0131 i\u015flevselli\u011fi etkileyen davran\u0131\u015flara kar\u015f\u0131n tarif olarak kullan\u0131lan <em>Cross-Cutting Concerns<\/em> kavram\u0131n\u0131 de\u011ferlendiriyor olacak ve logging, validation, caching vs. gibi belli ba\u015fl\u0131 \u00e7al\u0131\u015fmalar\u0131 MediatR k\u00fct\u00fcphanesiyle nas\u0131l uygulayabilece\u011fimizi de\u011ferlendiriyor olaca\u011f\u0131z. Haliyle vakit kaybetmeksizin hemen ba\u015flayal\u0131m&#8230;<\/p>\n<h4>Cross-Cutting Concerns Nedir?<\/h4>\n<p>Cross-Cutting Concerns (CCC), bir yaz\u0131l\u0131m\u0131n birden fazla bile\u015feni veya mod\u00fcl\u00fc \u00fczerinde yay\u0131lan, tekrarlayan ve b\u00f6ylece birden fazla noktay\u0131 etkileyen davran\u0131\u015flar\u0131 ifade eden bir kavramd\u0131r. Ee do\u011fal olarak birden fazla noktay\u0131 etkileyen davran\u0131\u015flar\u0131n s\u00f6z konusu olmas\u0131, ister istemez kimi sorunlar\u0131 beraberinde getirmekte ve neticede bir endi\u015fe yaratmaktad\u0131rlar. \u0130\u015fte bu nedenden dolay\u0131 bu davran\u0131\u015flar <em>concerns<\/em> olarak nitelendirilmektedirler. <em>Cross-Cutting<\/em> denmesinin sebebi ise bu endi\u015felerin, uygulamalar\u0131n farkl\u0131 par\u00e7alar\u0131 aras\u0131nda cereyan etmesidir. Yani farkl\u0131 noktalarda kesi\u015fen, ortak olan kayg\u0131lar\/endi\u015feler mevzu bahistir.<\/p>\n<p>Buna misal olarak logging&#8217;i \u00f6rnek verebiliriz. Yaz\u0131l\u0131mlarda logging; uygulaman\u0131n temel i\u015f mant\u0131\u011f\u0131ndan ba\u011f\u0131ms\u0131z olarak bir\u00e7ok noktas\u0131nda tekrarl\u0131 bir \u015fekilde uygulanan ve b\u00f6ylece uygulamaya yay\u0131lm\u0131\u015f olan bir davran\u0131\u015ft\u0131r. Haliyle logging; uygulaman\u0131n temel i\u015flevselli\u011finin d\u0131\u015f\u0131nda olan bir i\u015flev olmas\u0131ndan ve uygulaman\u0131n bir\u00e7ok noktas\u0131nda kullan\u0131lmas\u0131ndan dolay\u0131 bir Cross-Cutting Concern&#8217;dir diyebiliriz.<\/p>\n<h4>Ee Ne Yapmal\u0131 O Halde?<\/h4>\n<p>Yaz\u0131l\u0131mlardaki bu t\u00fcr endi\u015felerin, uygulaman\u0131n geni\u015fli\u011fi ve karma\u015f\u0131kl\u0131\u011f\u0131 artt\u0131k\u00e7a y\u00f6netilmesi zorla\u015fabilir. Bu nedenle, yaz\u0131l\u0131m geli\u015ftirme s\u00fcrecinde Cross-Cutting Concerns&#8217;lerin iyi bir \u015fekilde ele al\u0131nmas\u0131 ve y\u00f6netilmesi \u00f6nem arz etmektedir. Bu ama\u00e7la, genellikle <em>Aspect-Oriented Programming(AOP)<\/em> gibi teklikler veya farkl\u0131 tasar\u0131mlar kullan\u0131larak bu endi\u015felerin y\u00f6netilmesi gerekmektedir.<\/p>\n<p>\u2754<strong><em>Peki nas\u0131l y\u00f6netmeliyiz hoca<\/em><\/strong>\u2754<\/p>\n<p>Bu tarz farkl\u0131 yerlerde tekrar eden davran\u0131\u015flar\u0131, temel i\u015flevselli\u011fi etkilemeden uygulamaya entegre etmeyi sa\u011flayacak bir yakla\u015f\u0131mla. Yani endi\u015feli kodlar\u0131 tek bir yerde merkezile\u015ftirerek. B\u00f6ylece hem kod tekrar\u0131 azalt\u0131lm\u0131\u015f olacakt\u0131r hem de kodun bak\u0131m\u0131 kolayla\u015facakt\u0131r.<\/p>\n<p>\u00d6zellikle farkl\u0131 bile\u015fenlerden olu\u015fan uygulamalarda, bu bile\u015fenler aras\u0131ndaki ileti\u015fimi sa\u011flamak i\u00e7in MediatR k\u00fct\u00fcphanesi kullan\u0131l\u0131yorsa buradaki her bir iste\u011fe kar\u015f\u0131n pipeline&#8217;da merkezi bir \u00e7\u00f6z\u00fcm noktas\u0131 olu\u015fturulmal\u0131 ve \u00f6nceki sat\u0131rlarda bahsedildi\u011fi \u00fczere caching, validation ya da logging tarz\u0131 i\u015flemlerin bu noktadan \u00e7\u00f6z\u00fcmlenmesi gerekmektedir. Yani bir nevi bunun i\u00e7in MediatR pipeline&#8217;\u0131n\u0131n geni\u015fletilmesi gerekmektedir. Bunun i\u00e7in MediatR&#8217;da <code style=\"color:red;\">IPipelineBehavior<\/code> interface&#8217;inden istifade ediyor olaca\u011f\u0131z. Bu interface ile MediatR \u00fczerinden yap\u0131lan isteklerin \u00f6ncesi ve sonras\u0131 s\u00fcre\u00e7lerinde belirli davran\u0131\u015flar\u0131 ger\u00e7ekle\u015ftirebilir ve bu sayede temel i\u015f mant\u0131\u011f\u0131ndan ayr\u0131 olarak bu \u015fekilde Cross-Cutting Concern&#8217;leri ekleyip, y\u00f6netebiliriz.<\/p>\n<p>\u015eimdi gelin MediatR k\u00fct\u00fcphanesini kulland\u0131\u011f\u0131m\u0131z \u00e7al\u0131\u015fmalarda birka\u00e7 senaryo \u00fczerinden Cross-Cutting Concerns davran\u0131\u015flar\u0131n\u0131n y\u00f6netimini tecr\u00fcbe etmeye \u00e7al\u0131\u015fal\u0131m.<\/p>\n<h4>Cross-Cutting Concerns #1 &#8211; Logging<\/h4>\n<p>Logging, uygulamalar\u0131m\u0131z\u0131n canl\u0131 ortamlardaki reflekslerini takip edebilmemizi, uygulaman\u0131n anl\u0131k ya da geriye d\u00f6n\u00fck davran\u0131\u015flar\u0131na dair veriler sa\u011flayan hayati \u00f6neme sahip bir davran\u0131\u015ft\u0131r diyebiliriz.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\npublic record MyRequest(string Text) : IRequest&lt;MyResponse&gt;;\r\n\r\npublic record MyResponse(string Message);\r\n\r\npublic sealed class MyHandler : IRequestHandler&lt;MyRequest, MyResponse&gt;\r\n{\r\n    public Task&lt;MyResponse&gt; Handle(MyRequest request, CancellationToken cancellationToken)\r\n        =&gt; Task.FromResult&lt;MyResponse&gt;(new(&quot;...&quot;));\r\n}\r\n<\/pre>\n<\/div>\n<p>\u015eimdi, yukar\u0131daki gibi MediatR konsepti e\u015fli\u011finde bir iste\u011fi y\u00fcr\u00fctt\u00fc\u011f\u00fcm\u00fcz\u00fc varsayarsak e\u011fer bu s\u00fcre\u00e7teki logging i\u015fleminin ger\u00e7ekle\u015ftirilebilmesi i\u00e7in <code style=\"color:red;\">IPipelineBehavior<\/code> interface&#8217;ini \u015f\u00f6yle kurgulayabilir;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class LoggingPipelineBehavior&lt;TRequest, TResponse&gt;(ILogger&lt;LoggingPipelineBehavior&lt;TRequest, TResponse&gt;&gt; logger) : IPipelineBehavior&lt;TRequest, TResponse&gt;\r\n        where TRequest : class\r\n        where TResponse : class\r\n    {\r\n        public async Task&lt;TResponse&gt; Handle(TRequest request, RequestHandlerDelegate&lt;TResponse&gt; next, CancellationToken cancellationToken)\r\n        {\r\n            logger.LogInformation($&quot;Handling {typeof(TRequest).Name}&quot;);\r\n            TResponse response = await next();\r\n            logger.LogInformation($&quot;Handled {typeof(TRequest).Name}&quot;);\r\n\r\n            return response;\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>ve a\u015fa\u011f\u0131daki gibi uygulamaya dahil edebiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar builder = WebApplication.CreateBuilder(args);\r\n\r\nLog.Logger = new LoggerConfiguration()\r\n    .WriteTo.File(&quot;logs.txt&quot;)\r\n    .CreateLogger();\r\n\r\nbuilder.Host.UseSerilog();\r\nbuilder.Services.AddTransient(typeof(IPipelineBehavior&lt;,&gt;), typeof(LoggingPipelineBehavior&lt;,&gt;));\r\nbuilder.Services.AddMediatR(configuration =&gt; configuration.RegisterServicesFromAssemblyContaining&lt;Program&gt;());\r\n\r\nvar app = builder.Build();\r\n\r\napp.MapGet(&quot;\/&quot;, async (IMediator mediator) =&gt; await mediator.Send(new MyRequest(&quot;MyRequest Sending...&quot;)));\r\n\r\napp.Run();\r\n<\/pre>\n<\/div>\n<p>B\u00f6ylece t\u00fcm MediatR isteklerindeki loglama s\u00fcreci, olu\u015fturdu\u011fumuz <code>LoggingPipelineBehavior<\/code> s\u0131n\u0131f\u0131 \u00fczerinden y\u00fcr\u00fct\u00fclecek ve haliyle yaz\u0131l\u0131ma yay\u0131lmaktan ziyade merkezi olarak bu sorumluluk ger\u00e7ekle\u015ftirilecektir.<\/p>\n<h4>Cross-Cutting Concerns #2 &#8211; Validation<\/h4>\n<p>Validation, yaz\u0131l\u0131m uygulamalar\u0131nda sisteme girecek olan verilerin beklenen formatta olup olmad\u0131\u011f\u0131n\u0131 denetleyen ve b\u00f6ylece sistemin tutarl\u0131l\u0131\u011f\u0131n\u0131 korumak i\u00e7in ilk savunma hatt\u0131 olarak kullan\u0131lan davran\u0131\u015flard\u0131r. Validation sayesinde yaz\u0131l\u0131mlar, hem potansiyel g\u00fcvenlik a\u00e7\u0131klar\u0131na kar\u015f\u0131 korunak sa\u011flamakta hem de olas\u0131 tutars\u0131zl\u0131klara kar\u015f\u0131 \u00f6nlem almaktad\u0131rlar.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\npublic record CreateUserCommandRequest(string Username, string Email, string Password) : IRequest&lt;CreateUserCommandResponse&gt;;\r\n\r\npublic record CreateUserCommandResponse(string Message);\r\n\r\npublic sealed class CreateUserCommandHandler : IRequestHandler&lt;CreateUserCommandRequest, CreateUserCommandResponse&gt;\r\n{\r\n    public Task&lt;CreateUserCommandResponse&gt; Handle(CreateUserCommandRequest request, CancellationToken cancellationToken)\r\n        =&gt; Task.FromResult&lt;CreateUserCommandResponse&gt;(new(&quot;...&quot;));\r\n}\r\n<\/pre>\n<\/div>\n<p>E\u011fer ki, yukar\u0131daki gibi bir kullan\u0131c\u0131 olu\u015fturma ak\u0131\u015f\u0131ndaki istek yap\u0131lanmas\u0131n\u0131 ele al\u0131rsak e\u011fer burada <code>CreateUserCommandRequest<\/code> nesnesine validation uygulamak gerekecektir. Bunun i\u00e7in <em>FluentValidation<\/em> k\u00fct\u00fcphanesinden destek alarak a\u015fa\u011f\u0131daki gibi validasyon kurallar\u0131n\u0131 olu\u015fturabiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\npublic class CreateUserCommandValidator : AbstractValidator&lt;CreateUserCommandRequest&gt;\r\n{\r\n    public CreateUserCommandValidator()\r\n    {\r\n        RuleFor(x =&gt; x.Username)\r\n            .NotEmpty()\r\n                .WithMessage(&quot;Username bo\u015f ge\u00e7ilemez!&quot;);\r\n        RuleFor(x =&gt; x.Email)\r\n            .NotEmpty()\r\n                .WithMessage(&quot;Email bo\u015f ge\u00e7ilemez!&quot;)\r\n            .EmailAddress()\r\n                .WithMessage(&quot;Yanl\u0131\u015f e-mail format\u0131!&quot;);\r\n        RuleFor(x =&gt; x.Password)\r\n            .NotEmpty()\r\n                .WithMessage(&quot;Password bo\u015f ge\u00e7ilemez!&quot;)\r\n            .MinimumLength(6)\r\n               .WithMessage(&quot;Password minimum 6 karakter olmal\u0131d\u0131r!&quot;);\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>Ard\u0131ndan gelen verileri bu kurallar do\u011frultusunda kontrol edecek olan <code style=\"color:red;\">IPipelineBehavior<\/code> interface&#8217;ini implemente etmi\u015f olan <code>ValidationPipelineBehavior<\/code> s\u0131n\u0131f\u0131n\u0131 olu\u015fturabiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class ValidationPipelineBehavior&lt;TRequest, TResponse&gt;(IEnumerable&lt;IValidator&lt;TRequest&gt;&gt; validators) : IPipelineBehavior&lt;TRequest, TResponse&gt;\r\n        where TRequest : class\r\n        where TResponse : class\r\n    {\r\n        public async Task&lt;TResponse&gt; Handle(TRequest request, RequestHandlerDelegate&lt;TResponse&gt; next, CancellationToken cancellationToken)\r\n        {\r\n            var context = new ValidationContext&lt;TRequest&gt;(request);\r\n\r\n            var validationResults = await Task.WhenAll(validators.Select(v =&gt; v.ValidateAsync(context, cancellationToken)));\r\n\r\n            var failures = validationResults\r\n                .SelectMany(result =&gt; result.Errors)\r\n                .Where(f =&gt; f != null)\r\n                .ToList();\r\n\r\n            if (failures.Any())\r\n                throw new ValidationException(failures);\r\n\r\n            return await next();\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere bu s\u0131n\u0131f sayesinde do\u011frulama kurallar\u0131n\u0131 kontrol edebilmekteyiz. Bu ad\u0131mdan sonra yap\u0131lmas\u0131 gereken a\u015fa\u011f\u0131daki gibi gerekli tan\u0131mlar\u0131n ve yap\u0131land\u0131rman\u0131n sa\u011flanmas\u0131d\u0131r.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar builder = WebApplication.CreateBuilder(args);\r\n\r\nbuilder.Services.AddMediatR(configuration =&gt; configuration.RegisterServicesFromAssemblyContaining&lt;Program&gt;());\r\nbuilder.Services.AddValidatorsFromAssemblyContaining&lt;Program&gt;();\r\nbuilder.Services.AddTransient(typeof(IPipelineBehavior&lt;,&gt;), typeof(ValidationPipelineBehavior&lt;,&gt;));\r\n\r\nvar app = builder.Build();\r\n\r\napp.MapPost(&quot;\/create-user&quot;, async (IMediator mediator, CreateUserCommandRequest createUserCommandRequest) =&gt; await mediator.Send(createUserCommandRequest));\r\n\r\napp.Run();\r\n<\/pre>\n<\/div>\n<p>B\u00f6ylece y\u00fcksek veri kalitesi standard\u0131n\u0131 koruyarak, daha iyi bir kullan\u0131c\u0131 deneyimi sa\u011flayabilmekteyiz.<\/p>\n<h4>Cross-Cutting Concerns #3 &#8211; Caching<\/h4>\n<p>Caching; yaz\u0131l\u0131mlar\u0131n kaynak t\u00fcketim maliyetlerini azaltarak, performans art\u0131\u015f\u0131 amac\u0131yla daha verimli kullan\u0131m sa\u011flamak i\u00e7in s\u0131k kullan\u0131lan verilerin ge\u00e7ici olarak daha h\u0131zl\u0131 eri\u015filebilir bir konuma kopyalanmas\u0131 davran\u0131\u015f\u0131d\u0131r. Haliyle bu verilere eri\u015fim, normal duruma nazaran kat be kat h\u0131zl\u0131 olabilmekte ve b\u00f6ylece kullan\u0131c\u0131 deneyimi a\u00e7\u0131s\u0131ndan da avantaj sa\u011flanabilmektedir. \u0130\u015fte caching bu a\u00e7\u0131dan olduk\u00e7a \u00f6nemlidir.<\/p>\n<p>MediatR k\u00fct\u00fcphanesiyle yap\u0131lan CQRS \u00e7al\u0131\u015fmalar\u0131nda caching davran\u0131\u015f\u0131 i\u00e7in esas\u0131nda <em>Cache Aside Pattern<\/em>&#8216;\u0131 uygulanmaktad\u0131r. Bu model ile istek i\u015flemeden \u00f6nce cache kontrol edilerek gerekiyorsa yeni verilerle cache&#8217;in g\u00fcncellemesi ger\u00e7ekle\u015ftirilmektedir ve b\u00f6ylece caching kullan\u0131m\u0131 basit ve etkili bir strateji ile optimize edilmektedir. \u015eimdi bunun i\u00e7in a\u015fa\u011f\u0131daki istek s\u00fcrecini ele alarak bir \u00f6rneklendirmede bulunabiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\npublic record GetProductsQueryRequest() : IRequest&lt;List&lt;GetProductsQueryResponse&gt;&gt;;\r\n\r\npublic record GetProductsQueryResponse(string ProductName, int Quantity);\r\n\r\npublic sealed class GetProductsQueryHandler : IRequestHandler&lt;GetProductsQueryRequest, List&lt;GetProductsQueryResponse&gt;&gt;\r\n{\r\n    public Task&lt;List&lt;GetProductsQueryResponse&gt;&gt; Handle(GetProductsQueryRequest request, CancellationToken cancellationToken)\r\n        =&gt; Task.FromResult&lt;List&lt;GetProductsQueryResponse&gt;&gt;(new() {\r\n            new(&quot;Product1&quot;, 10),\r\n            new(&quot;Product2&quot;, 20),\r\n            new(&quot;Product3&quot;, 30),\r\n            new(&quot;Product4&quot;, 40),\r\n            new(&quot;Product5&quot;, 50),\r\n        });\r\n}\r\n<\/pre>\n<\/div>\n<p>Yukar\u0131daki \u00f6rnekte oldu\u011fu gibi <code>GetProductsQueryRequest<\/code> iste\u011fi neticesinde geriye veritaban\u0131ndan sorgulanarak elde edilen <span style=\"font-size:12px;\">( m\u0131\u015f gibi \ud83d\ude42 )<\/span> <code>GetProductsQueryResponse<\/code> koleksiyonunun d\u00f6nd\u00fcr\u00fcld\u00fc\u011f\u00fcn\u00fc varsayarsak e\u011fer bu iste\u011fin her yap\u0131l\u0131\u015f\u0131na kar\u015f\u0131n ayn\u0131 neticeyi return etmektense bunu cache&#8217;lemek ve cache&#8217;de tutulan veri \u00fczerinden iste\u011fe sonu\u00e7 d\u00f6nmek en performansl\u0131 ve az maliyetli tercih olacakt\u0131r diyebiliriz. Bunun i\u00e7in Redis&#8217;ten a\u015fa\u011f\u0131daki servis arac\u0131l\u0131\u011f\u0131yla istifade edebiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\npublic class RedisService\r\n{\r\n    ConnectionMultiplexer? connectionMultiplexer;\r\n    public void Connect()\r\n        =&gt; connectionMultiplexer = ConnectionMultiplexer.Connect(&quot;localhost:6379&quot;);\r\n    public IDatabase Database\r\n    {\r\n        get\r\n        {\r\n            if (!connectionMultiplexer?.IsConnected == null)\r\n                Connect();\r\n\r\n            return connectionMultiplexer.GetDatabase(0);\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>Devam\u0131nda ise a\u015fa\u011f\u0131daki gibi <code style=\"color:red;\">IPipelineBehavior<\/code> interface&#8217;ini implemente ederek olu\u015fturdu\u011fumuz <code>CachingBehavior<\/code> s\u0131n\u0131f\u0131 \u00fczerinden caching operasyonunu y\u00fcr\u00fctebiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public class CachingBehavior&lt;TRequest, TResponse&gt;(RedisService redisService) : IPipelineBehavior&lt;TRequest, TResponse&gt;\r\n        where TRequest : class\r\n        where TResponse : class\r\n    {\r\n        public async Task&lt;TResponse&gt; Handle(TRequest request, RequestHandlerDelegate&lt;TResponse&gt; next, CancellationToken cancellationToken)\r\n        {\r\n            var redisDatabase = redisService.Database;\r\n\r\n            var cacheKey = $&quot;{request.GetType().FullName}&quot;;\r\n\r\n            TResponse cachedResponse = null;\r\n\r\n            var cachedData = await redisDatabase.StringGetAsync(cacheKey);\r\n            if (cachedData.HasValue)\r\n            {\r\n                var cachedResponseJson = Encoding.UTF8.GetString(cachedData);\r\n                cachedResponse = JsonSerializer.Deserialize&lt;TResponse&gt;(cachedResponseJson);\r\n            }\r\n\r\n            \/\/Cache'de varsa o veriyi d\u00f6nd\u00fcr\u00fcyoruz.\r\n            if (cachedResponse != null)\r\n                return cachedResponse;\r\n\r\n            \/\/Cache'de yoksa ger\u00e7ek sorguyu y\u00fcr\u00fct\u00fcyoruz.\r\n            var response = await next();\r\n\r\n            \/\/Yeni veriyi cache'e al\u0131yoruz.\r\n            var responseJson = JsonSerializer.Serialize(response);\r\n            var encodedData = Encoding.UTF8.GetBytes(responseJson);\r\n\r\n            var result = await redisDatabase.StringSetAsync(cacheKey, encodedData, expiry: TimeSpan.FromSeconds(60));\r\n\r\n            return response;\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Dikkat edilirse e\u011fer her iste\u011fe kar\u015f\u0131n \u00f6nce cache&#8217;deki veri kontrol edilerek sonu\u00e7 \u00fcretilmektedir. E\u011fer veri varsa geriye d\u00f6nd\u00fcr\u00fclmekte, yoksa ya da s\u00fcresi dolmu\u015fsa(ki bu da yok anlam\u0131na gelir) o taktirde de yeni veri veritaban\u0131ndan elde edilerek hem cache&#8217;lenmekte hem de geriye d\u00f6nd\u00fcr\u00fclmektedir. Mant\u0131k budur. Gerisi a\u015fa\u011f\u0131daki gibi salt yap\u0131land\u0131rmadan ibarettir.<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar builder = WebApplication.CreateBuilder(args);\r\n\r\nbuilder.Services.AddMediatR(configuration =&gt; configuration.RegisterServicesFromAssemblyContaining&lt;Program&gt;());\r\nbuilder.Services.AddSingleton&lt;RedisService&gt;();\r\nbuilder.Services.AddTransient(typeof(IPipelineBehavior&lt;,&gt;), typeof(CachingBehavior&lt;,&gt;));\r\n\r\nvar app = builder.Build();\r\n\r\napp.MapGet(&quot;\/&quot;, async (IMediator mediator) =&gt; await mediator.Send(new GetProductsQueryRequest()));\r\n\r\napp.Run();\r\n<\/pre>\n<\/div>\n<p>Nihai olarak;<\/p>\n<p>Geli\u015ftirdi\u011fimiz yaz\u0131l\u0131mlar\u0131n sa\u011flam ve s\u00fcrd\u00fcr\u00fclebilir olmas\u0131 i\u00e7in <em>Cross-Cutting Concerns<\/em>&#8216;lerin i\u00e7eri\u011fimizde \u00f6rneklendirildikleri \u00fczere hassas bir \u015fekilde ele al\u0131nmas\u0131 gerekmektedir. Haliyle bizler bu ele al\u0131\u015f \u015fekillerinin ekalliyeti de olsa en \u00e7ok kullan\u0131lan ve en kritik olanlar\u0131n\u0131 MediatR k\u00fct\u00fcphanesi e\u015fli\u011finde \u00f6rneklendirmi\u015f bulunuyoruz.<\/p>\n<p>\u0130lgilenenlerin faydalanmas\u0131 dile\u011fiyle&#8230;<br \/>\nSonraki yaz\u0131lar\u0131mda g\u00f6r\u00fc\u015fmek \u00fczere&#8230;<br \/>\n\u0130yi \u00e7al\u0131\u015fmalar&#8230;<\/p>\n<p>Not : \u00d6rnek \u00e7al\u0131\u015fmaya a\u015fa\u011f\u0131daki github adresinden eri\u015febilirsiniz.<br \/>\n<a href=\"https:\/\/github.com\/gncyyldz\/Cross.Cutting.Concerns.With.MediatR.Example\" rel=\"noopener\" target=\"_blank\">https:\/\/github.com\/gncyyldz\/Cross.Cutting.Concerns.With.MediatR.Example<\/a><\/p>\n<!-- AddThis Advanced Settings generic via filter on the_content --><!-- AddThis Share Buttons generic via filter on the_content -->","protected":false},"excerpt":{"rendered":"<p>Merhaba, Bu i\u00e7eri\u011fimizde bir yaz\u0131l\u0131m sisteminin farkl\u0131 par\u00e7alar\u0131 aras\u0131nda yayg\u0131n olarak bulunan ve bir\u00e7ok farkl\u0131 i\u015flevselli\u011fi etkileyen davran\u0131\u015flara kar\u015f\u0131n tarif olarak kullan\u0131lan Cross-Cutting Concerns kavram\u0131n\u0131 de\u011ferlendiriyor olacak ve logging, validation, caching vs. gibi belli&#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":26948,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2739,12,3573],"tags":[5042,5041,5043,5039,5040,4153],"class_list":["post-26937","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net-core","category-c-sharp-c","category-microservices","tag-aop","tag-aspect-oriented-programming","tag-cache-aside-pattern","tag-ccc","tag-cross-cutting-concerns","tag-mediatr"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/26937","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=26937"}],"version-history":[{"count":10,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/26937\/revisions"}],"predecessor-version":[{"id":26947,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/26937\/revisions\/26947"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media\/26948"}],"wp:attachment":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media?parent=26937"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/categories?post=26937"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/tags?post=26937"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}