Nesneye yönelik programlama(OOP-Object Oriented Programming) yönteminin üç temel özelliği bulunmaktadır. Bunlar;
- Encapsulation (Kapsülleme)
- Polymorphism (Çok biçimlilik)
- Inheritance (Kalıtım)
Bu yazının konusu, inheritance(kalıtım) özelliğinin veri tabanında temsil edilmesidir. Kalıtım, bir sınıfın bazı özelliklerini üst sınıftan almasına denir.
Örneğin bir uygulamadaki kullanıcıları, bireysel ve kurumsal olarak ayırarak temsil etmek istediğimizde nesneye yönelik bir tasarım kullanarak aşağıdaki gibi bir yapı oluşturabiliriz.
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Email { get; set; }
}
public class IndividualUser: User {
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class CorporateUser: User {
public string Title { get; set; }
public string NationalTaxNumber { get; set; }
}
Görüldüğü üzere, tek bir class tipi kullanarak tüm özellikleri(property) içerisine doldurmadık. Bunun yerine, User adında bir ana class ve IndividualUser, CorporateUser isimli iki alt class oluşturduk. Çünkü User class içerisindeki özellikler diğer class’lar için ortaktır.
Bu yapının, sadece programlama tarafında temsil edilmesi bizim için yeterli olmayacaktır. İlgili kayıtların aynı zamanda veritabanına kaydedilebilmesi gerekmektedir. Bu nedenle, veritabanında bu yapıya uygun bir çalışma yapmak gerekmektedir. Veritabanı tasarımı yapılırken Normal Form(NF) kuralları dikkate alınmalıdır.
Normal form kurallarının ihmal edilmesinin iki sebebi olabilir. Birincisi kurallardan haberdar olmamak. İkincisi, kurallarla uğraşmadan olayın kod tarafında çözülebileceğinin düşünmektir.
Normal Form kuralları bazı durumlarda ihlal edilebilmektedir.
Tek tablo kullanarak kural ihlali
Kurumsal ve Bireysel kullanıcıları tek bir tablo içerisinde tutmak istediğinizde, tüm alanlar tek bir tablo içerisinde bulundurulacaktır. Bu durumda bireysel kullanıcılar için Title ve NatianalTaxNumber alanları boş kalacak, kurumsal kullanıcılar için FirstName ve LastName alanları boş kalacaktır. Tabloda bir kolon ya nullable ya da non-nullable özellikte olmalıdır. Nullable kolonlar, feature için henüz elde edilememiş ancak elde edildiğinde girilecek olan alanlar için kullanılır. Oysa burada FirsName, LastName, Title, NationalTaxNumber gibi alanlar kayıt türüne göre olmazsa olmaz alanlardır.
Id | Username | Password | FirstName | LastName | Title | NatiaonaTaxNumber | |
1 | bob | 1232 | b@mail.com | Bob | Smith | ||
2 | corp | 1235 | c@crp.com | Corp | 4656512 |
Tek tablo çözümlerinde bazen 1NF kuralları hiçe sayılabilmektedir. Örneğin tasarım yapılırken, Name diye bir alan açarak bireysel kullanıcılar için ad ve soyad birleştirilerek bu alana yazılıp, kurumsal kullanıcılar için şirket adı aynı kolona yazılabilmektedir. Bunlar 1NF kuralına aykırıdır. 1NF kuralları;
- Tekrarlanan sütun yapıları olmamalıdır.
- Birden fazla bilgi tek bir sütunda olamaz.
- Bir alan içerisindeki bilgi özel karakterlerle ayrılarak tutulmamalıdır.
İki tablo kullanarak kural ihlali
Eğer bireysel ve kurumsal kullanıcılar için iki ayrı tablo tutulursa;
Id | Username | Password | FirstName | LastName | |
1 | bob | 1232 | b@mail.com | Bob | Smith |
Id | Username | Password | Title | NatiaonaTaxNumber | |
1 | corp | 1235 | c@crp.com | Corp | 4656512 |
Bu durumda, müşteri ile ilişkilendirilecek olan bir tabloda hangi Id bilgisi tutulacak? 1 numaralı id bilgisi hem kurumsal hem de bireysel kullanıcı tablosunda bulunmaktadır. Örneğin Sipariş tablosuna ilişki kurulmak istendiğinde, iki tablo ile birden ilişki kurulmak durumunda kalınacaktır.
Üç tablo kullanarak kalıtım özellikli tasarım
Kalıtım özelliğini veritabanına yansıtarak bir tasarım oluşturmak, hem SOLID prensiplerine hem de OOP mantığına uygun olacaktır.
Id | Username | Password | |
1 | bob | 1232 | b@mail.com |
2 | corp | 1235 | c@crp.com |
UserId | FirstName | LastName |
1 | Bob | Smith |
UserId | Title | NatiaonaTaxNumber |
2 | Corp | 4656512 |
Bu tasarıma sayesinde, bir önceki tasarımdaki ilişki sorunu çözülmüş olacaktır. Siparis tablosu ile ilişki kurulacak tablo Users tablosu olacaktır.
Artık kullanıcılar tek bir merkez tablodan(users) Id bilgisi elde edecektir. Yeni bir kullanıcı türü geldiğinde sistemin mevcut özellikleri bozulmadan yeni özellik ekleme(extensibility) sağlanmış olacaktır. Örneğin iot_users gibi cihaz temelli bir kullanıcıyı sisteme eklemek kolay olacaktır. Bu sayede sistem open/closed prensibine uyugun olacaktır.
Ekstra Bilgi
Bu tasarıma göre users tablosunda user_type gibi tür bilgileri tutulmamaktadır. Sorgulamalarda kolaylık olsun diye bu tür bir alan açıldığında, eğer kurumsal bir kullanıcı eklenirken yanlışlıkla kullanıcı türü bireysel olarak işaretlenirse veri tutarlılığı bozulacaktır. Bunun yerine kullanıcıların türlerine göre sayılarını raporlamak için örnek bir sorgu aşağıdaki gibi oluşturulabilir;
select type, count(*)
from (
(select 'Individual' as type from individual_users)
union all
(select 'Corporate' from corporate_users)
union all
(select 'IoT' from iot_users)
) tables
group by (type);
Özet
Programlama dünyasında kullanılan teknik ve prensipler, veritabanı için bir engel değildir. Birinde OOP kuralları uygulanırken, diğerinde Normal Form kuralları uygulanarak doğru tasarımlar yapılabilmektedir. Programlama yaparken kolaylık olsun diye veya SQL sorgulamalarında kolaylık olsun diye hatalı tasarıma yönelmek veri bütünlüğünü ve tutarlılığını bozabilir.