Uygulama Oluşturma İlkeleri
Günümüzde bir uygulama oluşturulurken bu uygulamanın veriyle alakasının olması genellikle kaçınılmaz. Uygulamayı yaptıranın aklına uygulamanın veri kaynağı, uygulamanın çalıştırılacağı ortam ve gereksinimler ile alakalı birçok şey gelirken uygulamayı yazan kişi de verinin depolanacağı yer, çalıştırılacağı ortamın kapasitesi, verinin hacmi, artış hızı ve karmaşıklığı gibi birçok şeyi göz önünde bulundurarak kod yapısını şekillendirmeye çalışır.
Biraz daha açacak olursak, yazılımcının bu parametrelere göre şekillendirmesi gereken şu birkaç farklı parametrenin varlığından söz edebiliriz: Veritabanı ve index yapısı, caching sistemi, streaming ve batch processing.
Yeni bir uygulama yazmaya başlandığında yazılımcıların aklına oturup en baştan veritabanı uygulaması yazmak gelmez çünkü halihazırda birçok veritabanı uygulaması ve benzeri tool uygulamaları bulunmaktadır. Bu veritabanlarının gelen verileri nasıl işledikleri, performansları ve veriyi kaydettikleri gibi birçok karakteristiği birbirinden farklıdır. Sadece veritabanı değil, caching, stream yapıları için de aynı şeyi söyleyebiliriz.
Verilerimizi saklayabildiğimiz veritabanları, queue yapıları, caching sistemleri gibi yapıların aslında amaçları aynı olsa da bunlar çok farklı kulvardaki araçlardır. Örneğin veritabanı ve message queue yapılarında veriye erişim birbirinden oldukça farklıdır.
Ancak farklı amaçlarda kullanılmak üzere oluşturulan veri uygulamaları da bulunmaktadır. Örneğin Redis’i hem message queue hem cache için kullanabiliriz veya Apache Kafka’yı message queue olarak kullanabilirken bir taraftan da veritabanı düzeyinde veri saklama garantisi yakalayabiliriz.
Zaman geçtikçe uygulamaların ihtiyaçları çok farklı alanlara yayılmakta ve oldukça genişlemektedir. Bu sebeple bu ihtiyaçların hepsini tek bir tool karşılayamayacağı için birden fazla tool, uygulamanın kodu içerisinde birleştirerek bir ürün ortaya çıkarılmaktadır. Örneğin alışveriş sitelerinde veritabanı uygulamasından farklı olarak caching ve full-text search gibi gereksinimler için farklı sistemler kullanılmaktadır. Bu sistemlerin birbirleriyle uyum içerisinde çalışarak ürünü çalışır halde tutması ise uygulama kodu içerisinde yapılmaktadır. Bu birliktelik içerisinde ne gibi bir sorun oluşacağı, bu sorunlara nasıl çözümler getirilebileceği gibi birçok soru işareti ortaya çıkmaktadır.
Uygulamanın tasarımını etkileyecek birçok faktör olacaktır, insanların yetenek ve deneyim düzeyleri, kısıtlamalar, hata tolerans seviyesi gibi. Bu faktörleri de göz önünde bulundurarak uzun süreler çalışan bir sistem yapmak için yazılacak olan uygulama şu üç şey üzerine inşa edilmelidir:
Reliable: Yazılım, donanım ve insan kaynaklı hatalar oluştuğunda bile belirlenen performans çizgisinde beklenen çıktıyı vermeye devam etmesidir.
Scalable: Veri hacmi, artış hızı ve karmaşıklığı arttıkça sistemi büyütebilmenin mantıklı yollarının var olması gerektiğidir.
Maintainable: Farklı takımların, tasarlanan sistemin hem o anki haline hem de yeni use-case’lere kolayca adapte olabilmesi ve üzerinde üretken bir şekilde çalışabilmesidir.
Burada bahsedilen hatalar, tüm dünyanın havaya uçması gibi hatalar değil, sadece algoritmayla tolere edilebilecek hatalardır. Bu hataları da sıfıra indirmek mümkün olmayacaktır ancak olasılıklarını olabildiğince minimize etmek gereklidir. Örneğin donanım kaynaklı hataların önüne geçmek için RAID konfigürasyonları veya cloud kullanılabilir.
Sistematik yazılım hatalarını minimize etmek için önceden tahmin yürütülerek varsayımlar oluşturulabilir ve birçok yazılımsal önlem alınabilir. Örneğin monitoring ve measuring araçları kullanarak sorun oluştuğunda aniden müdahale edilebilir ve aynı hatanın bir daha çıkmaması sağlanabilir.
Uygulamayı yazanlar da birer insan olduğu için ihtimal dahiinde insan hatasıyla karşılaşılabilir. Bu durumların önüne geçmek içinse dev-test ortamları oluşturulabilir, uygulama yazılırken abstract yapılar, admin panelleri, API’ler gibi sistemler kullanarak tasarım inşa edilebilir.
Uygulamayı oluşturan sistemler yük ve performans olarak kaldırabileceği azami kapasiteye ulaşınca artık scale edilmesi gerekmektedir. Bu aşamaya varmadan önce sorulacak soru ise bu büyümeyle hangi yollarla baş edilebileceği ve ek seçeneklerimizin neler olduğudur diyebiliriz. Bahsettiğimiz yüke örnek verelim. Örneğin Twitter’ın saniyede kaç kez yeni post yayınlayabilmesi (write) ve bir postun sahibinin saniyede kaç takipçisinin bu postu görebileceği (read) örneğini verebiliriz. Yük fazlalaştığında sistemin belirlenmiş yöntemler doğrultusunda scale edilmesi gerekir. Performans ise bir kullanıcının bir sayfayı görme isteği (request) göndermesiyle yanıtın (response) gelme süresi arasındaki farkı örnek verebiliriz. Bu gibi performans metriklerinin sağlanması için SLO ve SLA gibi belirlenen performans düzeyinin yüzdelik olarak belirtildiği kontraklar oluşturulur.
Sisteme harcanan vakit ve para, sistem ilk kurulurken değil daha çok sistemin devamlılığını sağlarken olacaktır. Bazı insanlar eski sistemlerin bakımını yapmaktan ve başkalarının hatalarını telafi etmekten hoşlanmazlar. Bu gibi durumları minimize etmek için maintainability kavramı altında şu üç ilke ile sistemi yazmak gerekir:
Operability: Takımların sistemi akıcı bir şekilde yazılımı ayakta tutabilmelerini kolaylaştırmak.
Simplicity: Yeni çalışanların sisteme hızlıca adapte olabilmelerini kolaylaştırmak ve sistemdeki karmaşık yapıyı olabildiğince dışarı atmak.
Evolvability: Mühendislerin ilerde sistemde değişiklik yapabilmelerini kolaylaştırmak.
Kaynak: Kleppmann, M., 2019. Designing data-intensive applications. Beijing: O’Reilly, pp.3-27.