SOLID nedir ?

SOLID Candır !

SOLID'in geçmişine baktığımızda çokta uzağa gitmemize gerek yok. İlk olarak 2000'li yılların başında Michael Feathers tarafından ortaya atıldı ve sonrasında Robert C. Martin tarfından "first five principles of object-oriented programming and design" olarak "SOLID" adını aldı.

 

Neden SOLID'e ihtiyaç duyuldu ?

"Bir kere yazılan kod asla ilelebet aynı kalamaz..!" aslında SOLID'in ortaya çıkış sebebi bu cümlede gizlidir. Aylar-yıllar geçtikten sonra yazmış olduğumuz kodlara bir değişiklik yapmak istediğimizde bu değişikliği ne kadar efor sarf ederek ve ne kadar sürede yapacağız..? SOLID projeyi yazdıktan sonraki süreçte bu soruya en iyi şekilde cevap verebilmemizi sağlamak için var.

Şu gerçektir ki bazen 7 ay gibi bir efor harcanıp production'a alınmış bir proje için çok değil 6 ay sonra ki süreçte müşteri bir değişiklik veya extra modül istediğinde o modülün projeye eklenmesi neredeyse projeyi production'a almak için harcanan süre kadar sürebiliyor.

7 ay gibi bir sürede projeyi bitir canlıya al, canlıya alındıktan 6 ay sonra müşteri gelsin yeni bir feature istesin sonra müşteriye "benim bunun geliştirmesini yapmak için 6 aya daha ihtiyacım var.." dediğinizde müşteri heralde ufaktan şöyle olur "WTF...man ?"

SOLID prensiplerinin amacı aslında bizlerin daha iyi programlar yazmamızdan çok ileride istenecek değişimlere açık olup bu değişimlere en az eforla ayak uydurabilecek kodlar yazmamızı sağlamaktır. Nesne yönelimli programla yapıyorsak dünyada standart kabul edilen bu 5 prensibe uygun kodlar yazıyor olmamız gerekir.

 

 Peki nedir bu ilk 5 prensip ?

  • – Single-Responsibility Principle
  • – Open-Closed Principle
  • L  – Liskov Substitution Principle
  • I  – Interface Segregation Principle
  • D  – Dependency Inversion Principle

Her bir prensip için sonrasında ayrı ayrı blog'lar yazacağım ancak kısaca bir kaç kelimeyle şu şekilde özetleyebiliriz;

Single-Responsibility Principle

Bir class'ın sadece tek bir işi olmalı ve sadece o işten sorumlu olmalıdır. Örneğin Log tutmak için yaptığınız bir Logger class'ı sadece Log işlemlerinden sorumlu olmalıdır.

 

Open-Closed Principle

Uygulamada yazdığımız objeler yada entity'ler gelişime açık değişime kapalı olmalıdır. Örnek olarak çok if-else yazmaktan kaçının demek desek yanlış olmaz. Bir şekilde daha öcnesinde yazmış olduğunuz metodunuzu genişletmek istediğinizde interface yada abstract class'lardan faydalanarak daha yönetilebilir ve ihtiyaç duyulduğunda genişletilebilir kodlar yazmak gerekmektedir.

 

Liskov Substitution Principle

Liskov prensibi için kısaca yerine geçme prensibi diyebiliriz. Bir base class'tan türetilen class'ların yeri geldiğinde ihtiyaç halinde üst class'ların yerine de kullanabileceğini söylemekte.

 

Interface Segregation Principle

Client'lar ihtiyaç duymadıkları bir interface'i kullanmaya zorlanmamalı veya ihtiyaç duyduğu interface'e ait tek bir metod için bütün interface'in metodları implemente etmemelidir. Interface Segregation prensibi bu gibi durumlar için interface'lerinizi ayırın ve bir interface'e gerektiğinden fazla görev yüklemeyin der. Böylelikle client geliştirmesini yaparken ihtiyaç duymadığı hiç bir metodu implemente etmek zorunda kalmicaktır.

 

Dependency Inversion Principle

Bağımlılığı tersine çevirme prensibine göre base class'lar veya metodlar vs. alt seviyeli sınıflara veya metodlara bağımlı olmamalıdır ve alt class'larda yapılan bir değişiklik base class'ları etkilememelidir. Örnek olarak kısa bir süre önce gözlüklerimin camını değiştirmiştim ve nedendir bilinmez tam o sırada aklıma bu prensip gelmişti :)

Alt modül ; gözlük camı,

Üst modül ; gözlük

olsun. Gözlükçüye gittiniz ve camları değiştireceksiniz adam dedi ki "Camlar gözlüğe bağlı olduğundan camları değiştirmek için komple gözlüğü değiştirmeniz gerekmekte.."  . En iyi ihtimal adama "manyak mısın sen arkadaş.. " dersiniz. İşte bu prensip bu gibi durumlar için var. Alt modül üst modüle bağımlı olsun ama bağımlılık başımızın belası da olmasın tabi ki :)

 

Yazımız burada bitiyor ancak her bir prensip için ayrı ayrı blog yazıları yazıyor olacağım..just follow :)

Web Api Exception Handling and Logging

Herhangi bir yazılım projesinde Exception alındığında bu exception'nın handle edilip belli başlı bazı süreçlerden geçirilip log atılmasına kadar geçen süreçler oldukça önemlidir ve enterprise bir projede development yapıyor isek ExceptionLog olmazsa olmazlardan biridir. Projeye yeni başlarken mimarisi ilk düşünülüp tasarlanması gerekir çünkü proje ilerledikçe log, exception handling veya cache gibi yapıları entegre etmek biraz daha zorlaşacaktır.

Web Api tarafında Exception handling ve log işlemleri yapmak biraz daha keyifli ve basit diyebiliriz çünkü Microsoft sağolsun bazı şeyleri bizim yerimize düşünüp kolaylaştırmış.

İlk olarak şöyle bir controller metodumuz olsun ve bu metoda requestte bulunduğumuzda ne gibi bir response ile karşılaşıyoruz onu görelim.

[HttpGet]
public IHttpActionResult CheckValue(int value)  
{
    if(value > 20)
    {
        throw new ArgumentOutOfRangeException();
    }
    return Ok(value);
}

Herhangi bir 3th party tool (DHC, Postman etc.) kullanarak ilgili metoda parametresi "10" olacak şekilde request attığımızda respose olarak göndermiş olduğumuz "10" değerini status code 200 - OK olacak şekilde döner.

Aynı requesti parametre "25" olacak şekilde attığımızda ise yukarıda fırlattığımız "ArgumentOutOfRangeException" hata açıklamasını status code 500 - Internal Server Error olacak şekilde döner.

Uygulamada controller seviyesinde bir exception aldığımızda olması gereken exception'nın handle edilip kullanıcıya doğrudan Exception class'ından fırlatılan hata mesajı değilde(ihtiyaca göre) ilgili daha anlamlandırılmış bir şekli response'a eklenip client'a dönüyor olması ve bu exception'nın log olarak bir yerde tutuluyor olması gerekir.

Yukarıda bahsettiğimiz gibi WebApi için Exception alındığında Log işleminin yapılmasını için kullanıma sunulan abstract class ve onun metodlarını inceleyeceğiz. 

1.Logging ExceptionLogger class Log metodunu kullanarak uygulamadaki herhangi bir exception'nın loglanmasını sağlayabiliriz. Bunun için aşağıda UnhandledExceptionLogger isimli class'ımızı kullanarak ilerleyeceğiz.

public class UnhandledExceptionLogger : ExceptionLogger  
{
    public override void Log(ExceptionLoggerContext context)
    {
        var log = context.Exception.ToString();
        //Loglama için ilgili ilemlerin yapıldığı yer (db log, file log vs gibi)
    }
}

Üstte tanımladığımız UnhandledExceptionLogger class'ını Web Api projesinde kullanabilmek için register etmemiz gerekmekte. Register işlemi ile birlikte WebApi nin otomatik olarak default set ettiği ExceptionLogger'ı kendi yazdığımız UnhandledExceptionLogger class'ı ile değiştiriyoruz. Bu işlem için aşağıdaki kodu Global.asax.cs içerisindeki Application_Start metoduna yazıyoruz.

config.Services.Replace(typeof(IExceptionLogger), new UnhandledExceptionLogger());

 

Bu adımdan sonra uygulamada bir unhandled exception fırlatıldığı anda logger'ımız onu yakalayıp yazmak istediğimiz bir yere (db, file etc.) yazmamızı sağlayacaktır.

 

2.Excepiton Hander Bu kısımda uygulamamızda fırlatılmış olan exception'nı yakalayıp response da değiştirmek istediğimiz yerleri değiştirip client'a dönme işlemlerini yapacağız. Bunun için ExceptionHandler class'ından faydalanacağız. Bu class ExceptionLogger ve ExceptionFilter dan sonra eğer ilgili exception handle edilmediyse çağrılır. GlobalExceptionHandler ismindeki class'ımız aşağıdaki gibi olacaktır.

 public class GlobalExceptionHandler : ExceptionHandler
    {
        /// <summary>
        /// This function is used for to change the response when an exception occurs.
        /// </summary>
        /// <param name="context"></param>
        public override void Handle(ExceptionHandlerContext context)
        {
            var result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
            {
                Content = new StringContent(context.Exception.Message),
            };
            context.Result = new ExceptionActionResult(result);
        }
    }

 

Yukarıda exception fırlatıldığında yakalaıp client'a dönen response'u modify ettik. Bunun için kendi custom yazdığımız ExceptionActionResult class'ını kullanacağız. O da aşağıdaki gibi olacaktır.

    /// <summary>
    /// This class is a kind of Custom Action Result. When we get any exception and want to handle the web api method that returns HttpActionResult, we can use this class and it's functions.
    /// </summary>
    public class ExceptionActionResult : IHttpActionResult
    {
        private HttpResponseMessage _httpResponseMessage;

        public ExceptionActionResult(HttpResponseMessage httpResponseMessage)
        {
            _httpResponseMessage = httpResponseMessage;
        }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            return Task.FromResult(_httpResponseMessage);
        }
    }

 

Şimdi sırada 1.Adımda da yaptığımız gibi yazış olduğumuz Handler'ı WebApi'nin default set ettiğiyle değiştirme işlemi var bunu da Global.asax.cs içerisindeki Application_Start metodunda aşağıdaki gibi yapıyoruz.

config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());  

 

Uygulamamız hazır. Bu entegrasyondan sonra uygulamamıza 2 şey kazandırmış olduk.

  1. Exception alındığında Log atma işlemi
  2. Fırlatılan unhandled exception'ı yakalayıp kendi yazdığımız custom HttpActionResult ile değiştirip client'a giden respons'u daha anlamlı ve yönetilebilir bir hale getirmiş olduk.

Yazımızın en başında yazmış olduğumuz CheckValue metoduna veya uygulamada herhangi bir metoda request'te bulunup exception fırlatıldığında artık bütün response'lar artık şu şekilde olacaktır.

StatusCode : 500 Internal Server Error

Body : Specified argument was out of the range of valid values.

Gördüğünüz üzre projede exception alındığında log'umuzu atıp client'a dönen response'u da daha anlamlı bir hale getirmiş olduk. 

Ben örnek uygulamada response dönerken statusCode HttpStatusCode.InternalServerError yani 500 set ettim ancak siz projenizde kullanırken daha farklı statusCode'lar da ihtiyaca göre client'a dönebilirsiniz.

 

 

=> http://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/