Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

REPR(Request-Endpoint-Response) Pattern Nedir? Nasıl Uygulanır? Teferruatlıca İnceleyelim…

Merhaba,

Bu içeriğimizde, Asp.NET Core ile geliştirilen projelerin potansiyellerini daha farklı bir şekilde değerlendirmemizi sağlayacak olan bir yaklaşımı inceliyor olacağız. REPR desenini… Bu desen, yazılım geliştirme süreçlerinde isteklerin(request) ve bu isteklere karşın verilen yanıtların(response) yapılandırılmasını sağlamakta ve genellikle API tasarımlarıyla birlikte servis odaklı mimarilerde(SOA) tercih edilerek projenin tasarımına farklı bir efektiflik kazandırmaktadır. Şimdi gelin, bu desenin geliştirme süreçlerine dair olan kazanımlarına hem teorik hem de pratik olarak bakış sergilemeye başlayalım…

REPR deseni(telafuz ‘Reaper’) ile Web API’ler de, controller sınıfları yerine endpoint adı verilen özel sınıflar ön plana çıkmakta ve request/response süreçleri bu sınıflar üzerinden işlenerek, yapılacak işe göre daha öznel ve yalıtılmış hale getirilmektedir.

Neden REPR Pattern’ı Kullanmalıyız? Nedir Bu?

Şimdi bu soruyu cevaplandırabilmenin en doğru yolu bildiğimiz bir pattern üzerinden mukayesede bulunmaktır, yani MVC pattern’ından. Malumunuz MVC pattern’ı, geleneksel olarak API endpoint’leri oluşturmak için kullandığımız bir davranışsal modele sahiptir. Bu modelin çeşitli faydaları olsa da ister istemez dezavantajları da mevcuttur. Ki bana sorarsanız en büyük dezavantajı olarak değerlendirebileceğimiz, nihai olarak projeyi şişirilmiş controller(swollen controller) sınıflarına boğmasıdır. Şişirilmiş controller’dan kastedilen, controller’ların çok fazla sorumluluk almaları ve haddinden fazla işlev/metot/aksiyon barındırmalarıdır. Tabi zamanla, controller’larda biriken ve birbirleriyle ilişkili olmayan pek çok metot, bu sınıfların karmaşık hale gelmelerine neden olacaktır ve bu karmaşa, uygulamanın RESTful ilkelerine aykırı durumları barındırmasına ve bu ilkelerden istemsiz uzaklaşmasına ortam sağlayacaktır. Bu durumların dışında ayrıca, controller’lar da haddinden fazla aksiyonun olması bakım zorluğuna ve hangi metodun ne işe yaradığını anlamanın zorlaşmasına neden olacaktır.

İşte bu durumlara karşın çözüm arayışına girildiği taktirde akla aşağıdaki yöntemler gelmektedir;

  • Servis Katmanı Kullanımı
    Business logic, controller’lardan ayrılarak daha küçük ve sadece işe odaklanmış servis sınıflarında oluşturulmalıdır. Ama bu yöntem bir düzen getiriyor olsa da yukarıdaki satırlarda ifade edilen swollen controller durumu için pekte çözüm sağlamamaktadır. Düşünürsek eğer bu halde bile yüzlerce aksiyon barındıran bir controller sınıfının anlaşılabilir olması yine ciddi emek gerektirecektir.
  • Özelleştirilmiş Controller Tasarlamak
    İşte, işe odaklı servis sınıflarının tasarlanıp aynı controller’da farklı aksiyonlar için kullanmak yerine her bir aksiyona/amaca karşılık tek bir controller’ın hizmet etmesini sağlamak en doğru çözüm olacaktır diyebiliriz. Böylece kodun daha düzenli, okunabilir ve sürdürülebilir olması mümkün olacaktır. Ancak bu sefer de ciddi manada kod tekrarı durumunun söz konusu olacağını itiraf etmemiz gerekecektir.

    REPR deseni ise tıpkı buradaki mantıkta olduğu gibi uygulamadaki her bir endpoint’i ayrı bir sınıf olarak tanımlamamızı ve her sınıfın gelen istekleri işlemek için tek bir metodu olması gerektiğini önermektedir.

Böylece REPR deseni ile bir controller’daki birden fazla aksiyonu barındırma ihtiyacı ortadan kaldırılarak swollen controller durumu çözülebilmekte ve bir yandan da tek sorumluluk ilkesine de(bknz: Single Responsibility Principle – SRP) bağlı kalınarak aksiyon başına controller desteklenmektedir. Evet, bu tarz bir tasarım neticesinde proje bünyesinde dosya karmaşıklığının artmasıyla birlikte önceki satırlarda ifade ettiğimiz gibi istemsizce kod tekrarları da mevzu bahis olmaktadır.

REPR Pattern’ının Bileşenleri Nelerdir?

REPR pattern’ı, üç temel aşamadan oluşmaktadır;

  • Request
    • Kullanıcı veya client’lardna gelen istekleri ifade etmektedir.
    • Genellikle bir API çağrısı ya da HTTP isteği şeklindedir.
    • JSON, XML vs. gibi belirli bir formattadır.
  • Endpoint
    • Gelen request’in yölendirildiği servisi ifade etmektedir.
    • Request bu servis tarafından alını ve gerekli iş mantığı işlenerek, çalıştırılır.
    • Ve çalışma neticesinde bir result(sonuç) elde edilir.
  • Response
    • Request neticesinde endpoint’te oluşturulan result’ı tekrar kullanıcı ya da client’a döndüren yapıyı ifade etmektedir.
    • Çoğunlukla HTTP durum kodları(200, 404, 500 vb.) ile birlikte dönen result içermektedir.

REPR Pattern Nasıl Uygulanır?

REPR pattern, manuel uygulanabileceği gibi FastEndpoints ya da ApiEndpoints gibi kütüphaneler aracılığıyla da uygulanabilmektedir. Şimdi gelin her üç yöntemle de ilgili pattern’ı pratiksel olarak uygulamayı ele alalım;

Manuel Uygulama (Controller-Based)

Misal olarak, bir kullanıcının iş bilgilerinin güncellenmesini üstlenecek olan bir endpoint’in REPR pattern ile manuel olarak tasarımı aşağıdaki gibi olabilir;

  1. Kullanıcının yeni iş bilgilerini barındıracak olan bir request sınıfı oluşturulmalıdır;
        public class UpdateUserJobRequest
        {
            public int UserId { get; set; }
            public string NewJob { get; set; }
        }
    
  2. Aynı şekilde yapılacak güncelleme işlemi neticesinde geriye durum bilgisini döndürecek olan response sınıfı da oluşturulmalıdır;
        public class UpdateUserJobResponse
        {
            public int UserId { get; set; }
            public string Message { get; set; }
        }
    
  3. Ve nihai olarak iş mantığını uygulayabilmek için de bu işleme özel bir controller sınıfı oluşturulmalı ve içeriği de aşağıdaki gibi operatif tasarlanmalıdır;
        [Route("api/users/update-job")]
        [ApiController]
        public class UpdateUserJobController : ControllerBase
        {
            [HttpPut]
            public IActionResult UpdateUserJobAsync([FromBody] UpdateUserJobRequest updateUserJobRequest)
            {
                //Process...
    
                var response = new UpdateUserJobResponse()
                {
                    UserId = updateUserJobRequest.UserId,
                    Message = "Job information has been successfully updated..."
                };
                return Ok(response);
            }
        }
    

Evet, görüldüğü üzere REPR pattern eşliğinde bir aksiyona karşılık sadece o aksiyona özel geliştirilmiş olan bir controller sınıfı tasarlamış ve gerekli çözüm getirilmiştir. Benzer mantıkla, bir kullanıcının eklenmesini de yine aynı şekilde gerçekleştirebiliriz;

  1. Eklenecek kullanıcı bilgilerini barındıran request oluşturulmalı;
        public class CreateUserRequest
        {
            public string Name { get; set; }
            public string Surname { get; set; }
            public int Age { get; set; }
        }
    
  2. Kullanıcı oluşturma neticesinde durum bilgisini döndürecek response oluşturulmalı;
        public class CreateUserResponse
        {
            public int UserId { get; set; }
            public string Message { get; set; }
        }
    
  3. Ve kullanıcı ekleme işlemi bu aksiyona özel controller’da gerçekleştirilmeli;
        [Route("api/users/create")]
        [ApiController]
        public class CreateUserController : ControllerBase
        {
            [HttpPost]
            public IActionResult CreateUserAsync([FromBody] CreateUserRequest createUserRequest)
            {
                //Process...
    
                var response = new CreateUserResponse()
                {
                    UserId = ...,
                    Message = "The user has been successfully created..."
                };
                return Ok(response);
            }
        }
    

REPR(Request-Endpoint-Response) Pattern Nedir? Nasıl Uygulanır? Teferruatlıca İnceleyelim...

Controller tabanlı REPR tasarımı

Nasıl… Çok saçma değil mi 😀 Projeyi şişirilmiş controller sınıflarından arındıracağız derken her bir aksiyona karşılık böyle tek tek controller sınıfı oluşturmanın ilk bakışta pek cazip gelmemesi kadar doğal bir şey olamaz sanırım. Ben bile burada örneklendireceğim diye oluşturduğum controller sınıflarının ya ‘Route’ değerlerini güncellemeyi unutuyorum ya action isimlendirmesinde nasıl bir standart uyguluyordum diye diğerlerine bakmadan bi satır yazamıyorum ya da action türünü her seferinde yanlış giriyorum. Evet, bu şekilde manuel bir kullanım oldukça zorluyor.

Ki zaten REPR pattern’ı uygularken böyle controller sınıflarıyla cebelleşmeyeceğinizi söylemekte fayda var. Evet, bu pattern’ın uygulandığı projeler yüksek ihtimalle controller sınıflarından bağımsız cereyan etmektedirler. Misal olarak, minimal api’ler aklınıza gelebilir. Evet, artık Asp.NET Core deyince akla gelen minimal api’ler ile kısmen swollen controller durumuna çözüm getiriliyor olsa dahi, bir de REPR pattern ile bu yapılanmaya temas edildiğinde ap ayrı bir dinamizm söz konusu olmaktadır diyebiliriz. Şimdi gelin manuel uygulama sürecini bir de minimal api’ler üzerinden tecrübe edelim…

Manuel Uygulama (Minimal API-Based)

Tabi, minimal api’ler üzerinden REPR pattern’ı uygulayabilmenin en gözde yolu MediatR kütüphanesinden istifade etmektir. CQRS mantığında gelen istekleri hem command-query mahiyetinde ayırt edebileceğiz hem de REPR pattern davranışını da rahatlıkla uygulayabildiğimizi göreceğiz. Şimdi bir önceki controller-based örnek senaryolarını gelin minimal api’lerle de gerçekleştirelim;

Öncelikle Mediator.SourceGenerator ve Mediator.Abstractions kütüphanelerinin projeye yüklenmesi gerekmektedir. Bu kütüphaneler Mediator framework’ünün birer parçasıdırlar ve özellikle davranışsal olarak Mediator pattern’ının performansını artırmak ve runtime yerine compile-time’da işlemleri gerçekleştirmek için kullanılmaktadırlar. Mediator.SourceGenerator, MediatR kütüphanesine nazaran gelen istekleri runtime’da dependency injection ve reflection ile çözmeye yeltenmeksizin direkt compile-time’da yapılan kodsal inşaya uygun olarak otomatik üretilen kodlarla karşılamayı ve böylece istek süreçlerindeki performans ve maliyet açısından verimlilikleri ciddi artırmayı sağlamaktadır. İşte bu yüzden CQRS ya da REPR pattern davranışlarını uygularken MediatR yerine Mediator.SourceGenerator kütüphanesini tercih edebiliriz, edeceğiz de…

İşte bu zemin üzerine kullanıcının iş bilgilerinin güncellenmesini üstlenecek olan endpoint’i aşağıdaki gibi tasarlayabiliriz;

  1. Kullanıcının yeni iş bilgilerini barındıracak request sınıfı tanımlanmalı;
        public class UpdateUserJobCommandRequest : IRequest<UpdateUserJobCommandResponse>
        {
            public int UserId { get; set; }
            public string NewJob { get; set; }
        }
    
  2. Güncelleme işlemi neticesinde geriye durum bilgisini döndürecek response sınıfı tanımlanmalı;
        public class UpdateUserJobCommandResponse
        {
            public int UserId { get; set; }
            public string Message { get; set; }
        }
    
  3. Ve nihai olarak güncelleme işlemini yapacak operatif handler sınıfı tasarlanmalıdır;
        public class UpdateUserJobCommandHandler : IRequestHandler<UpdateUserJobCommandRequest, UpdateUserJobCommandResponse>
        {
            public ValueTask<UpdateUserJobCommandResponse> Handle(UpdateUserJobCommandRequest request, CancellationToken cancellationToken)
            {
                //Process...
    
                var response = new UpdateUserJobCommandResponse()
                {
                    UserId = request.UserId,
                    Message = "Job information has been successfully updated..."
                };
    
                return ValueTask.FromResult(response);
            }
        }
    
  4. Bu noktaya kadar her şey yolunda… Şimdi bu işlemin isteğini karşılayacak olan endpoint tasarlanmalıdır. Bunun için aşağıdaki çalışmaya göz atabilirsiniz;
        public static class UpdateUserJobCommandEndpoint
        {
            public static RouteGroupBuilder UpdateUserJobEndpoint(this RouteGroupBuilder routeGroupBuilder)
            {
                routeGroupBuilder.MapPut(pattern: "/update-job", async (UpdateUserJobCommandRequest updateUserJobCommandRequest, IMediator mediator) =>
                {
                    var result = await mediator.Send(updateUserJobCommandRequest);
    
                    return new ObjectResult(result)
                    {
                        StatusCode = (int)HttpStatusCode.Created
                    };
                });
    
                return routeGroupBuilder;
            }
        }
    

    Görüldüğü üzere sadece kullanıcının iş bilgilerini güncelleme isteğini karşılayacak bir endpoint’i barındıran static sınıf oluşturmuş bulunuyoruz. Aynı minvalde diğer aksiyonlar için de benzer yaklaşımı sergileyecek ve her birine özel endpoint tasarımında bulunacağız. Burada yapılan işleme dikkat edilirse eğer ismi tarafımızca opsiyonel bir şekilde belirlenmiş(UpdateUserJobEndpoint) ve RouteGroupBuilder referansı üzerinden erişilebilir olan bir extension metot tanımlanmıştır ve metot içerisinde GET, POST, PUT vs. gibi endpoint’ler aksiyona özel tasarlanmaktadır. Şimdi bu ve buna benzer şekilde tasarlanmış olan endpoint(ler)’i bir sonraki adımda merkezi bir noktada toplayacak ve ardından uygulamaya dahil ediyor olacağız.

  5. Artık endpoint’i tasarlanmış olan aksiyonu sisteme tam dahil edebilmek için bunun ve bunun gibi diğerlerinin merkezi bir noktada aşağıdaki gibi toplanması gerekmektedir;

        public static class UserEndpoints
        {
            public static void RegisterUsersEndpoints(this WebApplication application)
            {
                application.MapGroup(prefix: "api2/users")
                    .UpdateUserJobEndpoint();
            }
        }
    

    Evet, görüldüğü üzere kullanıcılarla ilgili yapılan tüm işlemler yukarıda tasarlanmış olan UserEndpoints static sınıfı üzerinde api2/users yolunda maplenmektedirler. Ayrıca MapGroup metodu geriye RouteGroupBuilder döndürdüğünden dolayı bir önceki adımda oluşturduğumuz UpdateUserJobEndpoint extension metodumuza da direkt erişim sağlanmıştır ve böylece bu merkeze ilgili aksiyona dair endpoint dahil edilmiştir. Tabi bu işlem neticesinde api2/users/update-job yoluyla ilgili endpoint’e erişim/istek sağlanabilecektir.

  6. Son olarak artık UserEndpoints‘te ki maplenmiş tüm endpoint’lerin uygulamaya dahil edilmesi gerekmektedir. Bunun için de UserEndpoints gibi tüm aksiyonlara karşın oluşturulan merkezi endpoint gruplarını tek elden uygulamaya dahil etmeyi sağlayacak aşağıdaki gibi bir mapping’in oluşturulması gerekmektedir;

        public static class RegisterRequestMapping
        {
            public static void RegisterMappings(this WebApplication application)
            {
                UserEndpoints.RegisterUsersEndpoints(application);
                .
                . Diğerleri...
                .
            }
        }
    

    Evet, böylece artık RegisterRequestMapping static sınıfı içerisindeki RegisterMappings extension metodunu aşağıdaki gibi Program.cs‘de çağırıp, uygulamadaki tüm endpoint yapılandırmalarını sisteme entegre edebilir ve minimal api’ler de REPR pattern eşliğinde bir tasarıma sahip olunabilir.

    using REPR.Pattern.Example.Manuel_Implementation.Minimal_API_Based.Endpoints;
    
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddMediator(options =>
    {
        options.ServiceLifetime = ServiceLifetime.Scoped;
    });
    
    var app = builder.Build();
    
    app.RegisterMappings();
    
    app.Run();
    

İşte bu kadar… Evet, minimal api’ler de olay biraz zor(muş) gibi gözüküyor olabilir ancak esasında öyle değil. Şimdi gelin ikinci örnek olarak bir kullanıcı eklemeyi de simüle edelim. İnanıyorum ki daha iyi anlaşılacaktır;

  1. Eklenecek kullanıcı bilgilerini barındıran request sınıfı tanımlanmalı;
        public class CreateUserCommandRequest : IRequest<CreateUserCommandResponse>
        {
            public string Name { get; set; }
            public string Surname { get; set; }
            public int Age { get; set; }
        }
    
  2. Response tanımlanmalı;
        public class CreateUserCommandResponse
        {
            public int UserId { get; set; }
            public string Message { get; set; }
        }
    
  3. Ve istek handle edilmelidir;
        public class CreateUserCommandHandler : IRequestHandler<CreateUserCommandRequest, CreateUserCommandResponse>
        {
            public ValueTask<CreateUserCommandResponse> Handle(CreateUserCommandRequest request, CancellationToken cancellationToken)
            {
                //Process...
    
                var response = new CreateUserCommandResponse()
                {
                    UserId = 0,
                    Message = $"The user has been successfully created... | {request.Name}, {request.Surname}, {request.Age}"
                };
    
                return ValueTask.FromResult(response);
            }
        }
    
  4. Devamında ise bu aksiyon için bir endpoint oluşturulmalı;
        public static class CreateUserCommandEndpoint
        {
            public static RouteGroupBuilder CreateUserEndpoint(this RouteGroupBuilder routeGroupBuilder)
            {
                routeGroupBuilder.MapPost(pattern: "/create", async (CreateUserCommandRequest createUserCommandRequest, IMediator mediator) =>
                {
                    var result = await mediator.Send(createUserCommandRequest);
    
                    return new ObjectResult(result)
                    {
                        StatusCode = (int)HttpStatusCode.Created
                    };
                });
    
                return routeGroupBuilder;
            }
        }
    
  5. Ve bu endpoint’te UserEndpoints static sınıfına eklenerek sisteme dahil edilmelidir;
        public static class UserEndpoints
        {
            public static void RegisterUsersEndpoints(this WebApplication application)
            {
                application.MapGroup(prefix: "api2/users")
                    .UpdateUserJobEndpoint()
                    .CreateUserEndpoint();
            }
        }
    

REPR(Request-Endpoint-Response) Pattern Nedir? Nasıl Uygulanır? Teferruatlıca İnceleyelim...

Minimal API tabanlı REPR tasarımı

İşte bu kadar 🙂 Aslında, özellikle minimal api’ler aracılığıyla yaptığımız bu davranış ile dikey bir mimari zeminini olabildiğince en verimli şekilde inşa ettiğimizi söyleyebiliriz.

Evet, artık olayı manuel deneyimlediğimize göre davranışsal mantığın yeterince anlaşıldığını düşünüyorum. Şimdi sıra, tercihen hazır kütüphanelerle bu davranışı şekillendirmeye çalıştığımızda nasıl bir yapılandırmanın olabileceğini incelemeye gelmiştir. Bunun için öncelikle FastEndpoints kütüphanesi üzerinden incelemede bulunacağız. Ardından ApiEndpoints kütüphanesine göz atacağız.

FastEndpoints İle REPR Pattern’i Uygulama

FastEndpoints, geleneksel Asp.NET Core controller ya da minimal api yapılanmalarına karşın daha akıcı bir şekilde REPR pattern’ını uygulamamızı sağlayan bir kütüphanedir. Şimdi bu kütüphane ile ilgili davranışı sergileyebilmek için her şeyden önce FastEndpoints kütüphanesinin uygulamaya yüklenmesi gerekmektedir. Ardından uygulamanın Program.cs dosyasında aşağıdaki yapılandırma gerçekleştirilip, ilk elden sistemsel entegrasyon sağlanmalıdır.

using FastEndpoints;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFastEndpoints();

var app = builder.Build();

app.UseFastEndpoints();

app.Run();

Bu aşamadan sonrası klasik request, response ve endpoint handler çalışması olacaktır. Şimdi gelin, yukarıdaki satırlarda yaptığımız örnek senaryoları FastEndpoints üzerinden gerçekleştirmeye başlayalım;

İlk olarak kullanıcının iş bilgilerini güncellemeye bakalım;

  1. Kullanıcının yeni iş bilgilerini barındıracak olan request sınıfı;
        public record UpdateUserJobRequest(int UserId, string NewJob)
        {
        }
    
  2. Response sınıfı;

        public record UpdateUserJobResponse(int UserId, string Message)
        {
        }
    
  3. Ve isteğe karşın iş mantığının yürütüleceği operatif endpoint sınıfı;
        public class UpdateUserJobEndpoint : Endpoint<UpdateUserJobRequest, UpdateUserJobResponse>
        {
            public override void Configure()
            {
                Put("/api3/users/update-job");
                AllowAnonymous();
            }
    
            public override async Task HandleAsync(UpdateUserJobRequest req, CancellationToken ct)
            {
                //Process...
    
                var response = new UpdateUserJobResponse(req.UserId, "Job information has been successfully updated...");
    
                await SendAsync(response);
            }
        }
    

    Burada dikkat edilirse eğer endpoint sınıfı içerisinde route ve erişim gibi temel yapılandırmalar override edilmiş olan Configure metodu içerisinde gerçekleştirilmektedir. Geri kalan istekle ilgili tüm operasyonlar yine override edilmiş olan HandleAsync metodu içerisinde yürütülmekte ve varsa istek neticesinde bir response bu da SendAsync ile geriye döndürülmektedir.

Benzer mantıkla kullanıcı ekleme senaryosunu da FastEndpoints ile örneklendirirsek eğer;

  1. Eklenecek kullanıcı bilgilerini barındıran request sınıfı;
        public record CreateUserRequest(string Name, string Surname, int Age)
        {
        }
    
  2. Response sınıfı;

        public record CreateUserResponse(int UserId, string Message)
        {
        }
    
  3. Ve iş mantığının yürütüldüğü endpoint sınıfı;

        public class CreateUserEndpoint : Endpoint<CreateUserRequest, CreateUserResponse>
        {
            public override void Configure()
            {
                Post("/api3/users/create");
                AllowAnonymous();
            }
    
            public override async Task HandleAsync(CreateUserRequest req, CancellationToken ct)
            {
                //Process...
    
                var response = new CreateUserResponse(0, $"The user has been successfully created... | {req.Name}, {req.Surname}, {req.Age}");
    
                await SendAsync(response);
            }
        }
    

REPR(Request-Endpoint-Response) Pattern Nedir Nasıl Uygulanır Teferruatlıca İnceleyelim...

FastEndpoins REPR tasarımı

Evet, görüldüğü üzere FastEndpoints kütüphanesiyle REPR pattern’ı oldukça kolay ve sade bir şekilde uygulayabilmekte ve özellikle projenin yapısını da karmaşıklaştırmaksızın daha az dosya barındıracak şekilde bir çalışma yürütülebilmektedir.

Tabi bunun dışında FastEndpoints kütüphanesi ile diğer istek türlerinin yanında, farklı şekillerde model binding yaklaşımları sergilenebilmekte ve bunların haricinde de validation, dependency injection, authorization, authentication, token generation vs. gibi üst düzey birçok yetenekte beraberinde gelmektedir. Bunları incelemek ve tecrübe edebilmek için şuradaki dokümantasyondan istifade edebilirsiniz.

ApiEndpoints İle REPR Pattern’i Uygulama

Şu ana kadar yapmış olduğumuz davranışı birebir aynı şekilde ApiEndpoints kütüphanesiyle de sergileyebiliriz. Bunun için tabi ki de öncelikle ilgili projeye Ardalis.ApiEndpoints kütüphanesinin yüklenmesi gerekmektedir.

Ardından Program.cs dosyasında aşağıdaki gibi controller yapılandırmasının sağlanması gerekmektedir;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();

app.MapControllers();

app.Run();

Bu aşamadan sonra yapılacak işleme uygun request, response ve endpoint sınıflarının tasarlanması kafidir. Şöyle ki, bizler yukarıdaki satırlarda yaptığımız senaryoları, artık içeriği haddinden fazla şişirmemek ve yersiz tekrara uğratmamak için request ve response sınıflarına değinmeksizin direkt endpoint sınıflarına odaklanarak örneklendirelim;

    public class UpdateUserJobEndpoint : EndpointBaseAsync.WithRequest<UpdateUserJobRequest>.WithActionResult<UpdateUserJobResponse>
    {
        [HttpPut("api4/users/update-job")]
        public override async Task<ActionResult<UpdateUserJobResponse>> HandleAsync(UpdateUserJobRequest request, CancellationToken cancellationToken = default)
        {
            //Process...

            var response = new UpdateUserJobResponse(request.UserId, "Job information has been successfully updated...");

            return response;
        }
    }
    public class CreateUserEndpoint : EndpointBaseAsync.WithRequest<CreateUserRequest>.WithActionResult<CreateUserResponse>
    {
        [HttpPost("api4/users/create")]
        public override async Task<ActionResult<CreateUserResponse>> HandleAsync(CreateUserRequest request, CancellationToken cancellationToken = default)
        {
            //Process...

            var response = new CreateUserResponse(0, $"The user has been successfully created... | {request.Name}, {request.Surname}, {request.Age}");

            return response;
        }
    }

Sanırım kod maliyeti açısından en uygunu ApiEndpoints kütüphanesiyle yapılan çalışma olduğu aşikar olsa gerek.

Dikkat ederseniz belirli bir mimari stili oluşturmak için REPR pattern’ı kullanmak mecburiyetinde değiliz. Yani bu pattern’ı kullanarak uygulamanın gereksinimlerine göre RESTful kaynakları ve hatta RPC tarzı endpoint’leri tanımlayabiliriz. En nihayetinde REPR pattern ile yaptığımız çalışmalarda görüldüğü üzere hangi tarzda bir endpoint yapısı kullanıyorsak kullanalım uygulamadaki katmanları birbirlerinden izole ederek kod seviyesinde okunabilirlik, sürdürülebilirlik ve ölçeklenebilirlik sağlayabildiğimiz aşikardır.

Tüm bunların dışında unutulmaması gereken bir husus da vardır ki, o da;

REPR deseni, bir REST deseni değildir!

Haliyle kaynakları tanımlamak için değil, bir kaç satır önce bahsettiğim gibi RESTful ya da RPC gibi API endpoint’lerini daha efektif tanımlayabilmek için kullanılan bir desendir.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Not : Örnek çalışmaya aşağıdaki github adresinden erişebilirsiniz.
https://github.com/gncyyldz/REPR.Pattern.Example

Bunlar da hoşunuza gidebilir...

6 Cevaplar

  1. Emre Can TERKAN dedi ki:

    Harikasın

  2. kerem dedi ki:

    çok büyük proejelerde görev dağıtımı konusunu çok rahatlatıyor

  3. Burak dedi ki:

    Bu bilgileri Türkçe bulmak büyük nimet…

  4. ahmet dedi ki:

    Merhaba Nebim Winner ön muhasebe paket programı kullanıyorum progrmada müşterilerime sms atma modülü var önceden sms atıyordum sıkıntı olmuyordu fakat firması programın yenisini çıkartınca bizim kullandığımıza desteğini kaldırdığını fakat programda değişiklik yapmadığını söylüyorlar mobildev sms firmasından sms alıyordum onlarda bizde sıkıntı yok diyorlar. sizin işlediğiniz ( request-response, endpoint ve örnek gsm) şeklinde yazınızı okudum belki benim bu sms müşterileme tekrar atabileceğim şekilde konuyla alakalı yardımcı olabilirmisiniz şimdiden çok teşekkür ederim adım Ahmet Bastem tel: 0555 *******

    • Gençay dedi ki:

      Merhaba,

      Kaynak kodlara erişim olmadığı sürece çözüm getirebileceğimizi sanmıyorum. Usulen firmanın ilgilenmesi gerektiğini düşünüyorum.
      İyi çalışmalar.

  5. Yunus dedi ki:

    Selam gençay hocam,
    Kafama takılan bir soru oldu bu repr patterni kullandığımız zaman mediatr a ihtiyaç kalmıyor yada cqrs gibi command ve query gibi işlemleri ayırmıyoruz galiba doğru bir çıkarımmıdır?

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir