Yazılım mühendisliği dünyasında "Spagetti Kod" salgını gerçek bir tehdittir. Uygulamalar büyüdükçe, sıkı bağlı bileşenler bakımı acı verici ve test etmeyi zor hale getirir. Geleneksel Katmanlı Mimari bazı ayrımlar sunsa da, iş mantığını altyapı endişelerinden gerçekten izole etmekte çoğu zaman başarısız oldu. İşte Altıgen Mimari (Portlar ve Adaptörler olarak da bilinir) burada parlar. Alistair Cockburn tarafından tasarlanan bu desen, katı bir bağımlılık tersine çevirmeyi dayatır ve çekirdek iş mantığınızın saf, test edilebilir ve dış dünyadan bağımsız kalmasını sağlar.
Temel Kavram: Altıgen
Altıgen Mimarinin merkezinde, Çekirdek Uygulama bulunur. Bu çekirdek, iş kurallarını ve kullanım durumlarını içerir. Veritabanları, HTTP sunucuları veya mesaj kuyrukları hakkında bilgi sahibi değildir. Bunun yerine, ne yapması gerektiğini (ör. "siparişi kalıcı hale getir") ve ne yapabileceğini (ör. "bildirim gönder") açıklayan arayüzleri tanımlar; bunlara Portlar denir.
Çekirdeğin etrafında Adaptörler bulunur. Bunlar, o portların spesifik uygulamalarıdır. Bir veritabanı adaptörü, bir OrderRepository portunu uygulayabilir. Bir HTTP adaptörü, bir OrderController portunu uygulayabilir. Önemli olan, bağımlılık okunun içeriye doğru işaret etmesidir. Adaptörler Çekirdeğe bağımlıdır, asla tersi değildir.
Bağımlılık Tersine Çevirmenin Neden Önemli Olduğu
Bağımlılık Tersine Çevirme İlkesi (DIP), yüksek seviyeli modüllerin düşük seviyeli modüllere bağımlı olmaması gerektiğini belirtir. Her ikisi de soyutlamalara bağımlı olmalıdır. Altıgen Mimari'de "Yüksek Seviye" iş mantığınız, "Düşük Seviye" ise altyapınızdır (DB, API, UI). Portları Çekirdekte tanımlayarak bağımlılığı tersine çevirirsiniz. Altyapı, iş mantığına uyar, tersi değil. Bu sayede, iş mantığınızın tek bir satırına dokunmadan PostgreSQL veritabanını MongoDB ile değiştirebilirsiniz.
Java'da Pratik Uygulama
Sipariş Servisi içeren basitleştirilmiş bir örneğe bakalım. Kalıcılık için bir Port ve Kullanım Durumu için bir arayüz tanımlıyoruz.
// Çekirdek Alan - Dış bağımlılıklar yok
package com.myapp.core;
public interface OrderRepository {
void save(Order order);
Order findById(String id);
}
public interface ProcessOrderUseCase {
void process(String orderId);
}
Sonraki adım olarak, yalnızca soyut Porta bağımlı olan Kullanım Durumu mantığını uyguluyoruz.
// Çekirdek Uygulama - Yalnızca Portlara bağımlı
package com.myapp.core;
public class ProcessOrderUseCaseImpl implements ProcessOrderUseCase {
private final OrderRepository repository;
public ProcessOrderUseCaseImpl(OrderRepository repository) {
this.repository = repository;
}
@Override
public void process(String orderId) {
Order order = repository.findById(orderId);
if (order == null) throw new RuntimeException("Order not found");
// İş Mantığı Burada
order.setStatus(Processed);
repository.save(order);
}
}
Son olarak, spesifik teknolojiler için Adaptörler oluşturuyoruz. Bunlar ayrı modüllerde veya paketlerde yaşar.
// Altyapı Adaptörü - Çekirdeğe bağımlı
package com.myapp.infrastructure;
import com.myapp.core.OrderRepository;
import com.myapp.core.Order;
public class JpaOrderRepository implements OrderRepository {
// Spring Data JPA veya JPA spesifik kodu burada
@Override
public void save(Order order) {
// DB'ye kalıcı hale getir
}
@Override
public Order findById(String id) {
// DB'den getir
}
}
Test Edilebilirlik ve Bakım
Bu yapının en büyük faydası test edilebilirliktir. Çekirdek yalnızca arayüzlere bağımlı olduğu için, iş mantığınız için basit mock'lar veya stub'lar kullanarak birim testleri yazabilir, veritabanını veya ağı tamamen atlayabilirsiniz. ProcessOrderUseCaseImpl'in davranışını, entegrasyon testinin yükü olmadan milisaniyeler içinde test edebilirsiniz.
Ayrıca, gereksinimler değiştiğinde etki izole edilir. İşlemden sonra e-posta gönderme gibi yeni bir çıktı kanalı eklemeniz gerekiyorsa, çekirdekte yeni bir NotificationPort tanımlar ve bir EmailAdapter oluşturursunuz. Mevcut mantık dokunulmadan kalır.
Sonuç
Altıgen Mimari sadece teorik bir kavram değildir; dayanıklı yazılım geliştirmek için pratik bir stratejidir. "Ne"yi (iş mantığı) "Nasıl"dan (altyapı) ayırarak, test etmesi, bakımı ve evrimi daha kolay sistemler oluşturursunuz. Başlangıçta bazı kurulum maliyeti getirse de, kod netliği ve ölçeklenebilirlikteki uzun vadeli faydalar, onu ciddi mühendislik ekipleri için değerli bir yatırıma dönüştürür. Küçük başlayın, portlarınızı dikkatlice tanımlayın ve mimarinin kodunuzu yönlendirmesine izin verin.