Kotlin Language Features Related to Null Handling

Any software engineer with a Java background would find the null handling features in the Kotlin language interesting. Let's summarize this topic with some examples. Nullable types: In Kotlin, types are non-nullable by default. If you want a variable to be able to hold a null value, you need to explicitly declare its type as nullable using the Type? syntax. For example, String? denotes a nullable string, while String represents a non-nullable string. Safe calls (?.): Kotlin introduces the safe call operator (?.) for handling nullable types. It allows you to safely invoke a method or access a property on a nullable object. If the object is null, the expression returns null instead of throwing a NullPointerException. Example: data class Person(val name: String, val age: Int, val address: String?) fun main() {     // Create a person with a nullable address     val person1 = Person("John Doe", 25, "123 Main Street")     val person2 = Person("Jane Doe", 30,...

NoSQL - Bölüm 2: Aggregate Kavramı

NoSQL veritabanlarının göze çarpan özelliği bir şema olmaksızın çalışıyor olmalarıdır. Örneğin bir kayda yeni bir özellik eklemek istediğimizde önce şema üzerinde değişiklik yapmamız gerekmez. Bir kayıt üzerinde bazı özel alanlar yaratmamız gerekiyorsa bu özellik işimize yarayabilir. Fakat neredeyse her zaman veritabanını kullanan yazılım içsel bir şemanın var olduğunu varsayar. Yani biz veritabanı mertebesinde bir şema tanımlamasak da, verinin içsel bir şeması vardır ve bu verileri kullanan program bir şekilde bu şema varmış gibi çalışabilmek zorundadır. Kısacası ilişkisel veritabanlarındaki şema gereksiz bir kavram değildir. Veritabanı seviyesinde verilerin ele alınış şekline bir düzen ve kontrol mekanizması getirir.

NoSQL veritabanı sistemleri 4 ayrı kategoride inceleniyor:

  1. Key-value : 
    • Örnekler: CouchDB, Redis, Riak
  2. Document 
    • Örnekler: Couchbase, MongoDB
  3. Column-family
    • Örnekler: Cassandra, Accumulo
  4. Graph
    • Örnekler: Neo4J, InfiniteGraph


NoSQL konusunu iyi anlayabilmek için "Aggregate" kavramını bilmek gerekiyor. Bir tanıma göre aggregate, bir bütün olarak ele almak istediğimiz ilişkili nesneler topluluğudur. Bu topluluk veri manipülasyonu ve tutarlılığı açısından bir birim oluşturmaktadır. Örneğin bir Siparişi ifade eden nesneler aggregate olarak modellenebilir. Burada SiparişKalemi ve Ürün de aggregate yapısı içerisinde kalmaktadır. Bu modeli ilişkisel veritabanında ifade ettiğimizde ilişkileri foreign key tanımlayarak belirleriz. Fakat Sipariş, SiparişKalemi ve Ürün tablolarının bir bütün olarak düşünülerek birlikte manipüle edildikleri açıkça belli değildir. Yani tipik bir SQL veritabanında aggregate tanımı açık bir biçimde yer almaz. Peki bu bir dezavantaj mıdır? Duruma göre değişir. Eğer aggregate yapılarımızı kolayca belirleyebiliyorsak ve farklı bakış açıları ile farklı ihtiyaçlara yönelik sınırlar çizmemiz gerekmiyorsa aggregate yapısını açıkça ifade etmek bizim için avantajlı olabilir. Fakat çoğu projede aggregate sınırlarını belirlemek zordur veya tercih edilmiyor olabilir. Bu noktada ilişkisel veritabanı bir avantaj sağlamaktadır çünkü sınırlar ile ilgili açık bir bilgi yoktur ve yapı esnektir. Graph veritabanlarında da aggregate yapıları açıkça belirlenmez. Fakat key-value store gibi NoSQL veritabanları aggregate yapıları üzerinden çalışmaktadır. Bunun en önemli avantajı veritabanı sisteminin hangi verileri bir arada değerlendirmesi gerektiğini bilmesidir ve böylece cluster üzerinde çalışma mümkün hale gelir. Burada Transaction yapıları genellikle aggregate üzerinde kurulur. 

Aşağıda JSON formatında bir Order görünüyor. (aggregate)


// in orders
{ "
id":99,
"customerId":1,
"orderItems":[
{
"productId":27,
"price": 32.45,
"productName": "NoSQL Distilled"
}
],
"shippingAddress":[{"city":"Chicago"}]
"orderPayment":[
{
"ccinfo":"1000-1000-1000-1000",
"txnId":"abelif879rft",
"billingAddress": {"city": "Chicago"}
}
],
}

Farklı bir modelleme ile Müşteri varlığı bir aggregate yapısının kökünde yer alabilirdi:

// in customers
{ "
customer": {
"id": 1,
"name": "Martin",
"billingAddress": [{"city": "Chicago"}],
"orders": [
{
"id":99,
"customerId":1,
"orderItems":[
{
"productId":27,
"price": 32.45,
"productName": "NoSQL Distilled"
}
],
"shippingAddress":[{"city":"Chicago"}]
"orderPayment":[
{
"ccinfo":"1000-1000-1000-1000",
"txnId":"abelif879rft",
"billingAddress": {"city": "Chicago"}
}],
}]
}}

Aggregate sınırlarının belirlenmesinde kesin bir kural yoktur. Sınırların belirlenmesi verinin nasıl manipüle edildiğine göre değişmektedir. Örneğin Sipariş nesnelerini kendi başlarına ele alarak manipüle ediyorsanız, bunları aggregate kökü olarak modellemek uygun olacaktır. Ama eğer siparişlere her zaman bir müşteri üzerinden erişiyorsanız, çoğu zaman bir müşteriyi ve onun tüm siparişlerini ele alıyorsanız, Müşteri varlığını bir aggregate root olarak kabul etmek uygun olacaktır.

Key-value store tarzındaki veritabanlarında bir ID ile aggregate köküne erişilir. Veritabanı bir değer tutmaktadır fakat bu değerin içsel yapısını bilmemektedir. Veritabanı açısından bu değer anlamsız bitler şeklindedir. Document veritabanları ise aggregate içerisindeki yapıyı görebilmektedir. Document veritabanlarına aggregate yapısının ayrıntıları ile ilgili sorgular gönderilebilmektedir. Kısacası key-value veritabanları için sorgulama yaparken bir anahtar gönderip değeri alırken, document veritabanlarında dökümanın içsel yapısı ile ilgili sorgular yapabiliyoruz.




Comments

Popular posts from this blog

Trie Data Structure and Finding Patterns in a Collection of Words

Virtual Memory

NOTES ON COMPUTER ARCHITECTURE: Some important concepts in computer architecture