Tasarımın Temelleri-2. Bağımlılık ve Uyumluluk Tasarım Prensipleri

Huseyin Kutluca
Yazılım Mimarileri
5 min readApr 13, 2020

Yazılım mimari tasarımı yapar iken bileşen içi uyumluluğu en üst seviyede (high cohhession) ve bileşenler arası bağımlılığı ise en az seviyede tutmayı (low coupling) hedefleriz. Bu iki temel tasarım prensibi 1960'lı yıllardan beridir ( Larry Constantine, Structured Design) bilinen ve kullanılan prensiplerdir. Mimari temelleri sağlam atmak için bu prensipleri iyi bilmeyi gerektirir.

Son zamanlarda öne çıkan SOLID prensipleri (Tek sorumluluk, Gelişmeye Açık değişmeye kapalılık, yerine geçme prensibi, arayüz ayrımı, bağımlılığın ters çevrilmesi)ve tasarım kalıpları (yaratımsal, davranışsal ve yapısal)bağımlılığı düşürme ve uyumluluğu arttırmaya çözüm üretmektedir. Benzer şekilde günümüzün favori yaklaşımı olan Alan Tabanlı Tasarım (Domain Driven Design DDD) ve Mikro servis mimarisi de temelde bu iki prensibin servisler seviyesinde uygulama şeklidir. Bu yaklaşımlar geliştireceğimiz sistemi servisler olarak tasarladığımız da servislerin sınırlarını nasıl belirleyeceğimizi ve servisler arası ilişkileri nasıl yöneteceğimizi düşük bağımlılık ve yüksek uyumluluk olacak şekilde belirler.

Bağımlılık

Düşük bağımlılığı sağlamak için önerilen yaklaşımları incelemeden önce bileşenler arası ne tür bağımlılıklar olabilir bunu irdelemek gerekir. Bağımlılık kod seviyesi (kod), kurulabilme (deployment) veya çalışma zamanı (execution time) olabilmektedir. Her bir seviye bağımlılığın azaltılması farklı açılardan projeye fayda sağlayacaktır. Kod seviyesi düşük bağımlılık projenin farklı kişilerce geliştirilebilmesi ve derlenebilmesini sağlar. Kurulabilme bağımlılığın düşük olması bileşenlerin kendi başlarına yeni versiyonunun sisteme yüklene bilmesine olanak tanır. Çalışma zamanı bağımlılıkları ise aşağıda verilmiştir:

Veri Yapısı Bağımlılığında bir bileşen diğer bir bileşen ile bir nesne ya da veri yapısının tamamını paylaşır. Burada esas sorun veriyi alan bileşenin bütün veri yapısına değil sadece belirli bilgilere bağımlı olmasıdır.

Kontrol bağımlılığında bir başka bir bileşenin nasıl işleyeceği ile ilgili kontrol algoritmasını belirler. Örneğin sıralama algoritmasına gönderilecek karşılaştırma metodu böyle bir bağımlılığı ifade eder.

Dış bağımlılıkta yazılım bileşeni dış bir donanıma ya da başka bir yazılıma bağımlıdır. Örneğin iletişim kurulan cihazın arayüz tanımına bağımlılık bulunmaktadır.

Ortak bağımlılıkta ise iki bileşen ortak bir veri yapısına bağımlıdır. Bu ortak veri yapısındaki değişiklik ilgili bütün bileşenlerde değişikliğe sebep olur.

İçerik Bağımlılığında bir bileşen diğer bileşenin verisini ya da kontrol akışını değiştirir. Bu en kötü bağımlılıktır.

Bileşenler Arası Bağımlılığı Azaltma

Bağımlılığı azaltma, bileşenleri daha bağımsız yapma konusundaki sistematik yaklaşımı içerir. İçerik bağımlılığını azaltmak için sarmalama (encapsulation) yaklaşımını kullanmamız gerekir.

Ortak bağımlılığı ortadan kaldırmak için soyutlama prensibini uygulamalıyız. Tasarım kalıpları bu konuda imdadımıza yetişecektir.

Dış bağımlılık veri biçiminden bağımsız şekilde iş alanı nesnelerini tanımlayarak yok edilebilir.

Kontrol bağımlılığı durum makinesi (state machine) ve strateji patterni ile önlenebilir.

Veri bağımlılığı da mesaj tabanlı ara katman kullanılarak yok edilir.

Demeter Kanunu olarak ta bilinen En az bilme prensibi ( principle of least knowledge) bağımlılığı azaltmak için temel alınabilir.

  • Bir bileşen diğer bileşenler ile ilgile en az şey bilmeli
  • Bir bileşen sadece yakın bileşenler ile haberleşmeli yani komşusu değilse doğrudan haberleşmemeli
Demeter Kanunu

Uyumluluk:

Bileşen içi uyumluluk ile ilgili sınıflandırma aşağıdaki gibidir:

Tesadüfi Uyumluluk alakasız elementlerin aynı bileşen içinde yer almasıdır. Örneğin dosya okuma işlemi ile veri yapıları ile ilgili kodların aynı bileşen içinde yer almasıdır. Utils, Common gibi isimler ile tanımlanan bileşenler bu sınıfa girer.

Mantıksal uyumluluk mantıksal olarak benzer fakat yöntemsel olarak farklı işlemlerin bir arada bulunmasıdır. Örneğin dosya okuma, ağdan veri okuma işlemenin aynı bileşen içinde yer almasıdır.

Zamansal uyumlulukta aynı zaman içinde/ sırasında yapılacak işler aynı bileşen içinde yer alır. Örneğin sistemin açılış aşamasında yapılacak işlemler aynı bileşen içinde yer alabilir. Benzer şekilde verinin geçerliliğinin kontrolü ve veri tabanına yazılması zamansal uyumluluk gözetilerek aynı bileşene konuşmuştur.

Yordam Uyumluluğu işleme sırası peş peşe olan işlemler aynı bileşen içinde yer alır. Örneğin sınıf listesi oluşturma ve sınıf listesi bastırma aynı bileşen içinde yer aldığında ikisi belki aynı anda kullanılabilir fakat birlikte yeniden kullanılabileceği anlamına gelmez.

Haberleşme uyumluluğunda aynı girdi üzerine işlem yapan elemanlar aynı bileşende yer alır.

Sıralı uyumlulukta bir bileşenin çıktısı diğer bileşenin girdisi şeklinde ve bu iki bileşen aynı yerde yer almaktadır. Örneğin verinin formatlanması ve ardından geçerliliğinin sağlanması sıralı uyumluluktur. Fonksiyonel programlamada tercih edilen bir uyumluluktur.

Fonksiyonel uyumlulukta tek bir fonksiyonu yerine getiren işlemler aynı bileşen içinde yer almaktadır. Bu ideal durumdur.

Bağımlılık ve Uyumluluk

3 Farklı cihazla haberleşeceğimiz bir yazılım tasarlamak istediğimizi varsayalım. Her bir cihaz farklı protokollerde ve farklı verileri sağlasın. Böyle bir sistemi en ideal şekilde aşağıdaki gibi tasarlayabiliriz.

Fakat varsayalım ki şöyle bir ihtiyacımız var: device1 üzerinden bir sinyal ürettik (ses, basın. vb.) ve aynı zamanda device2 ve device3 ten ölçmek istiyoruz. Ama bu ölçümü milisaniye altı gecikme ile senkron yapmamız gerekiyor. Sinyali device1 den verir vermez diğer cihazlardan ölçmek gerekiyor. Bu durumda yukarıdaki tasarım ile çözüm üretemeyiz çünkü module1, module2 ve module3 zamansal olarak bağımlı(temporal coupling). Bu durumda aşağıdaki tasarıma geçiş yapmamız en doğru olur. Amacımız beklenen işi yapmak yoksa doğru çalışmayan en iyi tasarımı yapmak değil.

Bileşenler Arası Uyumluluğu Arttırma

Tasarım yapar iken bileşen içi uyumluluğu en üst seviyede ve bileşenler arası bağımlılığı ise en az seviyede tutmayı hedefleriz. Bu şekilde ölçeklenebilir, değişiklik yapılabilir, test edilebilir sistemler oluşturabiliriz. Fakat tasarım yapılır iken dikkate alacağımız diğer kalite öznitelikleri (performans, güvenlik vb.) sebeplerden dolayı tavizler verebiliriz. Önemli olan bunun farkında olmak ve bu ödünleşimleri (tradeoff) dokümante etmektir.

Eğer bir kütüphane geliştiriyor isek büyük bir ihtimalle fonksiyonel uyumluluğa büyük ölçüde uyarız. Fakat sensörden veya kullanıcıdan gelen bir veriyi işleyen bir mikro servisi geliştiriyor isek haberleşme veya zamansal uyumluluğu ön planda tutabiliriz.

Uyumluluk prensibi çoğu zaman SOLID prensiplerinin ilki olan Tekil Sorumluluk (Single Responsibility Principle) ile eş görülür. Tekil Sorumluluk prensibi Uyumluluğu prensibine benzese daha dar ve göreceli bir bağlama sahiptir.

Düşük bağımlılık yüksek uyumluluğu sağlar. Bu iki prensip değiştirebilirliği ve bakım yapılabilirliği yüksek sistemler oluşturmamıza olanak tanır. Tamamen bağımsız bileşenler kullanışsız olacağı gibi çok düşük bağımlılığı olan sistemler de bakım açısından zorluklar yaratacaktır.

Mikro servis yaklaşımında birbirinden bağımsız servisler geliştirileceği ve bunların bağımsız biçimde çalıştırılacağı şeklinde bir ifade edilir. Bu teorik varsayım bir yanılgıdan ibarettir. Servisler ayrı ayrı çalışabilir birimler olarak tasarlansa da çalıştığı ortamdaki işlemci, ağ ve disk kaynaklarını paylaşacaklardır. Ayrıca bu servisler en iyi ihtimalle tanımlanmış arayüzler ile veri alışverişi yapacaktır. Bu durumda arayüz mesajına veri alanı eklediğimizde, ilgili bütün bileşenlerin bu değişikliği dikkate alacak şekilde değişmesini gerektirecektir.

--

--

Huseyin Kutluca
Yazılım Mimarileri

Highly motivated Software Architect with hands-on experience in design and development of mission critical distributed systems.