Software Architecture

Temiz Mimarili Ustalaşmak: Bakımı Kolay Yazılım Sistemleri İnşa Etmek

Yazılım mühendisliği alanında, Temiz Mimarilik kadar dönüştürücü birkaç kavram vardır. Robert C. Martin (Uncle Bob) tarafından popüler hale getirilen bu mimari yaklaşım, yalnızca bir kurallar kümesi değil, yazılım sistemlerini değiştirmeyi, test etmeyi ve anlamayı kolaylaştırmayı amaçlayan bir felsefedir. Orta düzeyden ileri düzey geliştiriciler için bu ilkeleri ustalaşmak, uzun vadeli proje başarısı açısından hayati önem taşır. Bu yazıda, Temiz Mimariliğin temel ilkelerini; kaygıların ayrılması, bağımlılık kuralları ve iş mantığının bağımsızlığı üzerine odaklanarak inceleyeceğiz.

Temel Felsefe: Kaygıların Ayrılması

Özü itibarıyla Temiz Mimarilik, kaygıların ayrılması (separation of concerns) ile ilgilidir. Bir sistemi, her biri belirli bir sorumluluğa sahip katmanlara böler. En kritik ayrım, bir yanda varlıklar (entities) ve kullanım durumları (use cases) veya etkileşenler (interactors) ile diğer yanda arayüz dönüştürücüler (interface adapters) ve çerçeveler/sürücüler (frameworks/drivers) arasındadır. Bu yapı, iş mantığının harici çerçevelerden, veritabanlarından veya kullanıcı arayüzü teknolojilerinden bağımsız kalmasını sağlar.

İş mantığı bir veritabanına veya bir web çerçevesine sıkı sıkıya bağlı olduğunda, birini değiştirmek diğerini sıklıkla bozar. Temiz Mimarilik bu bağımlılığı tersine çevirir. İç katmanlar, yalnızca o katmanlar içinde tanımlanan soyutlamalara bağlıdır; dış katmanlardaki somut uygulamalara değil.

Bağımlılık Kuralı ve Dairesel Yapı

Temiz Mimariliğin en belirgin özelliği Bağımlılık Kuralı'dır: kaynak kodu bağımlılıkları yalnızca içe doğru işaret edebilir. Bu, iç halkalardaki dosyaların (varlıklar, kullanım durumları) dış halkalar hakkında (denetleyiciler, arayüz dönüştürücüler, çerçeveler) hiçbir şey bilmemesi gerektiği anlamına gelir.

Buna ulaşmak için Bağımlılık Tersine Çevirme İlkesi'ne (Dependency Inversion Principle) güveniriz. Yüksek düzeyli modüllerin düşük düzeyli modüllere bağımlı olması yerine, her ikisinin de soyutlamalara bağımlı olması gerekir. Soyutlamalar detaylara bağımlı olmamalıdır; detaylar soyutlamalara bağımlı olmalıdır.

Varlıkları ve Kullanım Durumlarını Tanımlama

Pratik bir örneğe bakalım. Kullanıcı hesaplarını yöneten bir sistemi ele alalım. Temiz Mimarilikte, önce Varlık (Entity) tanımınızı yaparsınız. Bu varlık, kurumsal çapta iş kurallarını içerir ve uygulamanızın en kararlı parçasıdır.

// entities/User.js
class User {
  constructor(id, name, email) {
    this.id = id;
    this.name = name;
    this.email = email;
  }

  // İş kuralı: Doğrulanmamışsa e-posta yalnızca değiştirilebilir
  setEmail(newEmail) {
    if (this.isVerified) {
      throw new Error('Doğrulanmış kullanıcılar yeniden doğrulama olmadan e-posta değiştiremez');
    }
    this.email = newEmail;
  }

  verify() {
    this.isVerified = true;
  }
}

Ardından, Kullanım Durumu (Use Case) veya Etkileşeni (Interactor) tanımlarız. Bu katman, belirli bir görevi tamamlamak için verilerin varlıklara ve varlıklardan akışını koordine eder. Kritik olarak, Kullanım Durumu, somut veritabanı uygulamasına değil, havuzun (repository) bir soyutlamasına bağlıdır.

// useCases/VerifyUser.js
class VerifyUserUseCase {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  execute(userId) {
    const user = this.userRepository.findById(userId);
    if (!user) throw new Error('Kullanıcı bulunamadı');
    
    user.verify(); // Varlık metodunu çağırır
    this.userRepository.save(user); // Soyutlamayı çağırır
  }
}

VerifyUserUseCase'un userRepository'un SQL, NoSQL veya bir dosya sistemi kullanıp kullanmadığını bilmediğine dikkat edin. Yalnızca findById ve save metodlarına sahip bir nesne olduğunu bilir. Bu bağımsızlık, test etmeyi ve bakımı önemli ölçüde kolaylaştırır.

Arayüz Dönüştürücülerin Uygulanması

Dış katman olan Arayüz Dönüştürücüler (Interface Adapters) katmanı, veriyi kullanım durumları ve varlıklar için en uygun formattan, harici ajans (bir web sunucusu veya veritabanı gibi) için en uygun formata dönüştürür. Tam olarak burada, kullanım durumu katmanında tanımlanan havuz arayüzünü uygularsınız.

// adapters/InMemoryUserRepository.js
class InMemoryUserRepository {
  constructor() {
    this.users = [];
  }

  findById(id) {
    return this.users.find(u => u.id === id);
  }

  save(user) {
    const index = this.users.findIndex(u => u.id === user.id);
    if (index === -1) this.users.push(user);
    else this.users[index] = user;
  }
}

Sonuç

Temiz Mimarilik sihirli bir değnek değildir, ancak değişime dirençli sistemler oluşturmaya yönelik sağlam bir çerçeve sağlar. Bağımlılık Kuralını zorlayarak ve varlıkları kullanım durumlarından açıkça ayırarak, iş mantığınızın saf, test edilebilir ve teknolojik eğilimlerden bağımsız kalmasını sağlarsınız. İlk kurulum, basit bir betikten daha fazla kod ve yapı gerektirse de, bakılabilirlik ve ekip ölçeklenebilirliği açısından sağladığı fayda büyüktür. Bu ilkeleri bir sonraki projenizde uygulamaya başlayın ve yazılımınızın yönetimini ve evrimini ne kadar kolaylaştırdığını izleyin.

Share: