Caner Tosuner

Leave your code better than you found it

Kısaca Agile Nedir Neler Gerektirir

Agile aşağı Agile yukarı... Herkes bir agile'dır gidiyor ama malesef çoğu kişi için agile bir kaç cümle veya kelimeden ibaret.

Bir çok Yönetici & Patron için çabucak at koşturur gibi işi yapıp bitirme,

Bir çok çalışan için ise agile jira'da bulunan bug'ları çözmekten ve scrum yapmaktan ibaret..

Malesef Türkiye'de biraz gerilerde ancak güzel haberlerde gelmiyor değil, öyle ki bazı kurumsal bankalar dahi yazılım departmanı olmadığı halde kendi iç birimlerinde agile'ı test etmeye başladığı söyleniyor ve Türkiye'de bulunan çok büyük bir bankanın da bütün birimleriyle agile'a %100 geçtiğiyle ilgili geçenlerde bir haber okumuştum. Tabi bunlar agile doğru anlaşıldığı zaman güzel haberler oluyor.

Sektörde bulunan yazılım firmalarında ki bir çok development takımı kendi içlerinde agile yaptıklarını söylüyorlar ancak işin aslını konuşmaya çalıştığınızda anlıyorsunuz ki sadece agile'a ait bir kaç işi yaptıkları ortaya çıkıyor.

Scrum için ise aslında yapılan şey iş yoğunluğa göre bazı sabahlar proje müdürünün ekibi toplayıp sadece kendisinin konuşması...

 

Peki Agile olmak neleri gerektirir ?

Daha önce okuduğum ve bir çok kişininde referans alarak uygulamaya çalıştığı "What Do We Know About Scientific Software Development’s Agile Practices ?" makalesinde belirtilen 35 Agile Best Practice'i baz alarak takım olarak ne yapıp ne yapmıyorsunuz, ne kadar agile'sınız yorumlayabilirsiniz. 

  1. Önceliklendirmeler projede en yetkili kişi olan product owner tarafından (ekibin fikrini alarak) yapılmalı,
  2. Development sürecindeki sorunlar scrum master tarafından scrum'larda çözüm aranmalı,
  3. Spring backlog oluşturmak için Spring planlama toplantıları yapılmalı,
  4. Efor verirken Planning Poker oyunu gibi eğlenceli bir seçiminiz olmalı,
  5. Kısa ve Orta vadede koşan sprint'leriniz olmalı,
  6. Product owner ve ekip arasında sürekli bir communication olmalı,
  7. Mevcut konuları konuşmak için kısa daily meeting'ler düzenlenmeli,
  8. Self-organization yani kendiliğinden organize olabilen bir ekip uluşturulabilmeli,
  9. Grafikler ile sprint süreci gözlemlenmeli,
  10. Sprint sonlarında review toplantıları düzenlenmeli,
  11. Retrospective yaparak geçmiş sprintle ilgili değerlendirmelerde bulunulmalı,
  12. Short & Fast product release'ler çıkılmalı,
  13. User storie'ler yazılmalı,
  14. Takıma dedike bir şekilde çalışabileceği açık çalışma alanları yaratılmalı,
  15. Sürdürülebilir bir çalışma ortamı sunulmalı,
  16. Proje ivmesi ölçülebilmeli,
  17. Ekipte bulunanlar her konuda fikrini söyleyebilmeli,
  18. Müşteriye ihtiyaç duyulduğunda hemen ulaşılabiliyor olmalı,
  19. Ekip olarak kod standartları belirlenmeli,
  20. İlk önce unit-test'ler yazılmalı,
  21. Prod'a alınan kod'un bir pair-programming ürünü olduğu unutulmamalı,
  22. Geliştirmeler branch'lere ayrılarak yapılmalı ve test sonuçlarına göre merge işlemi yapılmalı,
  23. Sürekli olarak yeni şeyler entegre edilebilecek altyapıya sahip bir altyapı,
  24. Repository yönetimini iyi yapıp release günü geldiğinde sorunsuz bir şekilde release çıkılabilmeli,
  25. Collective bir biçimde developer'lar atayın bir sorumluluğu tek bir kişiye yüklemekten kaçının,
  26. Basit bir tasarıma desenine sahip olmalı,
  27. Bir sistem metforu seçilmeli,
  28. Design&Development için Class-responsibility-collaboration kartlarını kullanmaya çalışın,
  29. Riski azaltmak için çözümler üretilmeli,
  30. İhtiyaç duyulmayan hiç bir fonksiyonalite önceden entegre edilmemeli,
  31. Ne zaman-Nerde olduğuna bakılmaksızın refactor edilebilmeli,
  32. Bütün kod'un unit-test'i yazılmış olmalı,
  33. Proje prod'a alınmadan önce bütün unit-test'lerden geçmeli,
  34. Yeni bir bug bulunduğunda testler anında oluşturulmalı,
  35. Acceptence-test'ler sıklıkla uygulanmalı ve skor yayınlanmalı.

 

Agile hakkında söylenecek oldukca fazla şey bulunuyor ancak kısa bir özetle yukarıda ki rule'ları sıralamış olduk. İyi bir agile-team oluştururken bu kurallardan faydalanmak çöğunlukla iyi olacaktır ama belirttiğim gibi liste genişletilebilir de.

Caching With Postsharp

Cache bir proje için olmazsa olmazlardan biridir diyebiliriz ve ihtiyaç duyulduğunda çok can kurtarır. Öyleki çok fazla değişmesini düşünmediğiniz verileri cache de saklayarak reqeust response time konusunda projenize oldukça fazla performans kazandırabilirsiniz. Daha önceki web api için cache ve aspect oriented - postsharp konularından bahsetmiştik. WCF Service projenize Postsharp ile basit bir server-side cache yapısı kurabilirsin.

İlk adım olarak cache de tutacağımız CacheModel adlı model'i tanımlayalım.

public class CacheModel
{
    public DateTime CacheTimeOutInfo { get; set; }//cache'in ne zaman timeout olacağını belirten time bilgisi
    public object CacheData { get; set; }//cache'de tutulacak object
}

Şimdi ise CacheManager'a implement edeceğimiz ICache interface'i tanımlayalım ve içerisine cache metotlarımızı yazalım.

public interface ICache
{
   bool Contains(string key);//key varmı yokmu diye control ettiğimiz metot
   void Add(string key, CacheModel cacheData);//cache key'i ile birlikte cache model'i alıp cache'e ekleyen metot
   object Get(string key);//key parametresi alarak cache'de ki data yı return eden metot
   void Remove(string key);//key parametresine göre mevcut cache'i silen metot
   void RemoveAll();//bütün cache'i silen metot
}

Aşağıda yazacağımız class'ta ise üstte yazdığımız ICache'i implement eden cache yönetimini yapacağımız CacheManager class'ını oluşturacağızz.

    public class CacheManager : ICache
    {
        public bool Contains(string key)
        {
            var isExist = HttpRuntime.Cache[key];
            if (isExist != null)
            {
                var obj = GetCacheModel(key);
                if (obj.CacheTimeOutInfo > DateTime.Now)
                    return true;
                Remove(key);
            }
            return false;
        }

        public void Add(string key, CacheModel cacheData)
        {
            if (Contains(key))
                Remove(key);
            HttpRuntime.Cache[key] = cacheData;
        }

        public object Get(string key)
        {
            var obj = GetCacheModel(key);
            return obj.CacheData;
        }

        private CacheModel GetCacheModel(string key)
        {
            return (CacheModel)HttpRuntime.Cache[key];
        }

        public void Remove(string key)
        {
            HttpRuntime.Cache.Remove(key);
        }

        public void RemoveAll()
        {
            var enumerator = HttpRuntime.Cache.GetEnumerator();
            while (enumerator.MoveNext())
            {
                HttpContext.Current.Cache.Remove((string)enumerator.Key);
            }
        }
    }

Sırada Postsharp kullanarak oluşturacağmız CacheAspect adında interceptor'ı yazama var. Bunun için nuget'ten postsharp'ı indiriyoruz ve aşağıdaki gibi CacheAspect adındaki interceptor'ımızı oluşturuyoruz.

    [Serializable]
    public class CacheAspect : OnMethodBoundaryAspect
    {
        //Metottan alacağımız cache süresi
        public int MethodCacheDurationInMinute = 0;

        public override void OnEntry(MethodExecutionArgs args)
        {
                 //cache key oluşturma
                var cacheKey = CreateMD5(GenerateCacheKey(args));

                var cacheManager = new CacheManager();

                var isCacheKeyExist = cacheManager.Contains(cacheKey);

                if (isCacheKeyExist)
                { 
                    //eğer request geldiğinde cache key cache de varsa client'a cahe de bulunan veriyi dön
                    args.ReturnValue = cacheManager.Get(cacheKey);
                    args.FlowBehavior = FlowBehavior.Return;
                }
                else
                {
                    //cache de yoksa normal akışta ilerlemesine devam eder
                    args.MethodExecutionTag = cacheKey;
                }
        }

        public override void OnSuccess(MethodExecutionArgs args)
        {
                //başarılı bir şekilde metottan çıktıysa cache'e at

                    var cacheKey = (string)args.MethodExecutionTag;

                    var cacheManager = new CacheManager();
                    var cacheModel = new CacheModel
                    {
                        CacheData = args.ReturnValue,
                        CacheTimeOutInfo = DateTime.Now.AddMinutes(MethodCacheDurationInMinute)
                    };
                    cacheManager.Add(cacheKey, cacheModel);
        }

        private static string GenerateCacheKey(MethodExecutionArgs args)
        {
            var generatedKey = string.Empty;

            if (args.Method.DeclaringType != null)
                generatedKey = args.Method.DeclaringType.Name + "-" + args.Method.Name + (args.Arguments.Any() ? "-" + args.Arguments.Aggregate((first, second) => first + "-" + second) : "");
            generatedKey += SerializeObjectToJson(args.Arguments);

            return generatedKey;
        }

        public static string CreateMD5(string input)
        {
            using (var md5 = System.Security.Cryptography.MD5.Create())
            {
                var inputBytes = Encoding.ASCII.GetBytes(input);
                var hashBytes = md5.ComputeHash(inputBytes);

                var sb = new StringBuilder();
                foreach (var t in hashBytes)
                {
                    sb.Append(t.ToString("X2"));
                }
                return sb.ToString();
            }
        }
    }

Artık sıra cache'i projemizde kullanmaya geldi. yazdığımız interceptor'ı projede hangi metodu cache'e atmak istiyorsak metodun başına attribute olarak ekleyip kaç dakikalık cache'de tutacağını belirteceğiz.

        [CacheAspect(MethodCacheDurationInMinute = 120)] //120 dakikalığına cachelemesini söylüyoruz
        public List<FooModel> Foo(FooRequest reqModel)
        {
            var fooManager=new FooManager();
            return fooManager.GetFooList();
        }

 

Kullanım olarak oldukç basit ve projede hangi metotta istersek aynı yukarıda olduğu gibi attribute olarak projemize ekleyebiliriz. Yazının başında da belirttiğim gibi cache bir çok zaman hayat kurtarır ve yabana atılmaması gereken bir konudur.

 

Builder Design Pattern

Design pattern'leri spagetti kodlara veya tekrar eden kod bloklarına çözümler olarak ortaya çıkmışlardı. Builder Design Pattern creational patterns kategorisi altında bulunan patternlerden birisidir ve kısaca basit objeler kullanarak complex objeler inşa etmede kullanılır diyebiliriz. 

 

Builder'ı DTO yani data transfer object class'larını örnek olarak düşünürsek, bu objeler birden fazla objenin birleşmesinden oluşurlar ve genelde complex objelerdir. Builder pattern'nini de base'de bir obje var ve bu objeye ait alt property'leri oluştururken her bir özellik için ayrı build metotları yazmamız gerekir. Builder design pattern'i bu gibi durumlarda kullanabilecegimiz bir pattern olarak dusunebiliriz. Başka bir deyişle; runtime da yaratılması gereken complex objelerinizin olduğu bir projeniz varsa bu pattern'i kullanmak güzel bir seçim olabilir veya adım adım obje yaratma işlemlerini yönetmek istediğiniz bir durum var ise yine bu pattern'i kullanabiliriz.

Builder pattern'i 4 ana yapıdan oluşur;

  • Product  : Build edilen obje,
  • Builder   : Product'ı build eden abstract yapı,
  • Concrete Builder : Builder nesnesinin abstract yapıyı kullanarak aynı yapıdaki farklı ürünlerin build edilmesini sağlayan yapı,
  • Director  : Kendisine verilen Builder'ı property'lerini kullanarak ürünü inşa eden yapı.

Örnek bir proje üzerinden Builder Design Pattern'i implement etmeye çalışalım. Apple fabrikasında Iphone modelleri üretimi yapan bir senaryo yazalım ve bunu pattern'imizi uygulayarak yapmaya çalışalım.

Öncelikle ürün class'ımızı oluşturalım. Iphone'un farklı modellerini (6, 6S, SE, vs.) ürettiğimizi düşünelim ve constractor seviyesinde hangi modeli ürettiğimiz bilgisini verelim.

    //Product'ımız   
    public class Iphone
    {
        string phoneType;
        public Iphone(string phoneType)
        {
            this.phoneType = phoneType;
        }
    }

Şimdi ise Builder gibi davranan abstract class'ımızı oluşturalım. Bu class üretimde kullanacağımız abstract üyeler içerecektir. Icerisinde BuildOS ve BuildDevice adında iki abstract metot ve hangi iphone modelini üreteceği bilgisinin olacağı Iphone tipinde olan obje olacaktır.

    //abstract Builder nesnemiz   
    public abstract class IphoneBuilder
    {
        public abstract void BuildOS();

        public abstract void BuildDevice();

        Iphone IphoneType { get; }
    }

Sırada Builder class'larını geliştirme var. 6sBuilder ve SeBuilder adında iki tane iphone modeli için geliştirme yapalım.

SeBuilder

    // SE modeli için kullanacağımız Concrete Builder class'ımız
    public class SeBuilder : IphoneBuilder
    {
        Iphone device;

        public SeBuilder()
        {
            device = new Iphone("Se");
        }

        public override void BuildOS()
        {
            //TODO
        }

        public override void BuildDevice()
        {
            //TODO
        }

        public Iphone IphoneType
        {
            get { return device; }
        }
    }

_6sBuilder

   // 6S modeli için kullanacağımız Concrete Builder class'ımız
   public class _6SBuilder : IphoneBuilder
    {
        Iphone device;

        public _6SBuilder()
        {
            device = new Iphone("6S");
        }

        public override void BuildOS()
        {
            //TODO
        }

        public override void BuildDevice()
        {
            //TODO
        }

        public Iphone IphoneType
        {
            get { return device; }
        }
    }

Şimdi sırada üretim var. Manufacturer adında üretici class'ımızı oluşturalım.

   //Director class'ımız
   public class Manufacturer
    {
        public void Build(IphoneBuilder iphoneBuilder)
        {
            iphoneBuilder.BuildDevice();

            iphoneBuilder.BuildOS();
        }
    }

Sıra yaptığımız implementasyonu kullanmaya geldi. 

	public static void Main()
	{
		 Manufacturer manufacturer = new Manufacturer();

         _6SBuilder _6s = new _6SBuilder();

         manufacturer.Build(_6s);
	}

Main fonksiyon içerisinde Manufacturer'ı kullanarak _6s modelini build ettik. Builder pattern'nini elimizden geldiğince projemize entegre ettik ve diğer iphone ürünleri için de aynı akışı kullanarak ürünlerimizi Build edebiliriz.

Builder pattern'i factory ve abstract factory pattern'leri ile birçok yönden benzerlikler göstermektedir. Hangisini kullanmalıyım diye tereddütte kaldığınız durumlarda, complex objeler yaratıyorsanız Builder'ı, tek bir obje yaratmak istiyorsanız da factory veya abstract factory design pattern'i tercih edebilirsiniz.

Quartz .Net Nedir Nasıl Kullanılır ?

Quartz.Net, geliştirdiğiniz web tabanlı projelerde scheduled task yani zamana dayalı planlı görevler çalıştırmak istediğinizde çok kolay bir şekilde implement edip kullanabileceğiniz open source bir kütüphanedir. Aslında bir çok kişi bu tarz işlemleri yapmak için bir console uygulaması geliştirir ve windows server'da task schedular'a bu uygulamayı ne zaman çalıştıracağını söyler ve iş biter. Ancak gerek sunucuya erişimin kısıtlı olduğu durumlarda, gerekse ikinci bir uygulama geliştirmenin yol açtığı efor düşündüğünde quartz.net daha avantajlı gibi görünüyor.

Kullanım olarak iki aşamadan oluşuyor diyebiliriz,

  1. Çalışacak olan Job'ı belirleme,
  2. Hangi zaman aralıklarında çalışacağı bilgisi set edilir.

Quartz 3 ana bileşenden oluşur, job, trigger, schedular. Job yapılacak olan iştir. Trigger job'ın ne zaman, ne şekilde tetikleneceği emrini veren & tetikleyen yapıdır. Job ve Trigger ikisi birlikte schedular'a register olurlar ve kısaca çalışma şekli olarak; job schedular üzerinden trigger tarafından çalıştırılır diyebiliriz.

Örnek bir case üzerinden ilerleyelim; bir banka web service projemiz olsun ve müşterilere her gün saat 13:30 da push notification yollayan bir kampanyalar job'ı geliştirelim.

1. Quartz.Net Kurulumu

Kurulum için projenize sağ tıklayıp Manage Nuget Packages dedikten sonra aşağıdaki gibi nuget'ten projeyi install edebiliriz.

 

2. Job'ı Oluşturma

Bunun için ilk olarak IJob interface'inden implement olan CampaignPushJob adında bir class oluşturalım. IJob ile birlikte class'ımız Execute adında bir metoda sahip olur ve job çalışırken aslında bu metot içerisindeki akış çalışır diyebiliriz.

    public class CampaignPushJob : IJob
    {
        public void Execute(IJobExecutionContext context)
        {
            var userRepository = new UserRepository();
            var userList = userRepository.GetPNUserList();

            var pushNotText = "Size özel konut kredisi teklifleri.";

            var pnService = new PnService();
            pnService.SendPush(pushNotText, userList);
        }
    }

Örnekte PnService adında bir service'imiz olduğunu varsayalım ve SendPush metodu ile kullanıcılara notification gönderiliyor olsun.

3. Schedular Oluşturma

Bu adımda CampaignPushJobScheduler adında içerisinde ne zaman ve neyi çalıştıracağı bilgisini verdiğimiz schedular'ımızı yazıyoruz. JobBuilder.Create<CampaignPushJob>().Build() satırında da görüldüğü gibi bizden bir adet IJob interface'ini implement etmiş class istemekte. Bizde bir üstte yazdığımız CampaignPushJob'ını buraya set ediyoruz. Ne zaman çalıştıracağı bilgisini ise TriggerBuilder'ı initialize ederken set edeceğiz. Örneğimizde her gün saat 13:30 da çalışmasını istediğimiz bir job çalıştırmak istediğimiz için TriggerBuilder içerisinde aşağıdaki gibi bir tanımlama yaptık.

    public class CampaignPushJobScheduler
    {
        public static void Start()
        {
            IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
            scheduler.Start();

            IJobDetail job = JobBuilder.Create<CampaignPushJob>().Build();

            ITrigger trigger = TriggerBuilder.Create()
                .WithDailyTimeIntervalSchedule
                  (s =>
                     s
                    .OnEveryDay() //hergün çalışacağı bilgisi
                    .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(13, 30)) //hergün hangi saatte çalışacağı bilgisi
                  )
                .Build();

            scheduler.ScheduleJob(job, trigger);
        }
    }

4. Schedular Start Etme

Bu adıma kadar job, schedular ve trigger bilgilerimizi oluşturduk ve artık uygulama ayağa kalktıktan sonra schedular'ı start etmemiz gerekiyor. Geliştirmekte olduğumuz proje bir Asp.Net projesi olduğundan bu işlemi kolayca Global.asax dosyası içerisinde bulunan Application_Start metodunda yapabiliriz. Tek yapmamız gereken CampaignPushJobScheduler'ın içerisinde bulunan Start metodunu çağırmak.

    public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            CampaignPushJobScheduler.Start();
        }
    }

Quartz.Net implement etme işlemimiz bu kadar. Artık web projemiz bir kere ayağa kalktıktan sonra set ettiğimiz değerler doğrultusunda çalışacaktır.

Quartz schedular işlemler için .Net tarafında kullanılabilecek en yaygın kütüphanelerden bir tanesidir. Örnekte her ne kadar az kullanmış olsak da trigger time özelliğiyle ilgili kullanıcılara bir çok seçenek sunmakta. Bunlarla ilgili ayrıntılı bilgiyi bu linkte bulabilirsiniz.