Sürdürülebilir, ölçeklenebilir ve test edilebilir frontend uygulamaları oluşturmak için Hexagonal ve Clean Architecture'ı keşfedin. İlkelerini, faydalarını ve pratik uygulama stratejilerini öğrenin.
Frontend Mimarisi: Ölçeklenebilir Uygulamalar için Hexagonal ve Clean Architecture
Frontend uygulamalarının karmaşıklığı arttıkça, iyi tanımlanmış bir mimari sürdürülebilirlik, test edilebilirlik ve ölçeklenebilirlik için hayati önem taşır. Bu endişeleri ele alan iki popüler mimari desen, Hexagonal Mimari (Portlar ve Adaptörler olarak da bilinir) ve Clean Architecture'dır. Kökenleri backend dünyasına dayansa da, bu ilkeler sağlam ve uyarlanabilir kullanıcı arayüzleri oluşturmak için frontend geliştirmeye etkili bir şekilde uygulanabilir.
Frontend Mimarisi Nedir?
Frontend mimarisi, bir frontend uygulaması içindeki farklı bileşenlerin yapısını, organizasyonunu ve etkileşimlerini tanımlar. Uygulamanın nasıl oluşturulacağı, sürdürüleceği ve ölçeklendirileceği konusunda bir plan sunar. İyi bir frontend mimarisi şunları teşvik eder:
- Sürdürülebilirlik: Kodu anlamak, değiştirmek ve hatalarını ayıklamak daha kolaydır.
- Test Edilebilirlik: Birim ve entegrasyon testleri yazmayı kolaylaştırır.
- Ölçeklenebilirlik: Uygulamanın artan karmaşıklığı ve kullanıcı yükünü yönetmesine olanak tanır.
- Yeniden Kullanılabilirlik: Kodun uygulamanın farklı bölümlerinde yeniden kullanılmasını teşvik eder.
- Esneklik: Değişen gereksinimlere ve yeni teknolojilere uyum sağlar.
Net bir mimari olmadan, frontend projeleri hızla monolitik hale gelebilir ve yönetimi zorlaşabilir, bu da artan geliştirme maliyetlerine ve azalan çevikliğe yol açar.
Hexagonal Mimariye Giriş
Alistair Cockburn tarafından önerilen Hexagonal Mimari, bir uygulamanın temel iş mantığını veritabanları, kullanıcı arayüzü (UI) framework'leri ve üçüncü taraf API'ler gibi dış bağımlılıklardan ayırmayı amaçlar. Bunu Portlar ve Adaptörler konsepti aracılığıyla başarır.
Hexagonal Mimarinin Temel Kavramları:
- Çekirdek (Domain): Uygulamanın iş mantığını ve kullanım senaryolarını içerir. Herhangi bir dış framework veya teknolojiden bağımsızdır.
- Portlar: Çekirdeğin dış dünya ile nasıl etkileşime gireceğini tanımlayan arayüzlerdir. Çekirdeğin giriş ve çıkış sınırlarını temsil ederler.
- Adaptörler: Portların, çekirdeği belirli dış sistemlere bağlayan uygulamalarıdır. İki tür adaptör vardır:
- Sürücü Adaptörler (Birincil Adaptörler): Çekirdek ile etkileşimleri başlatır. Örnekler arasında UI bileşenleri, komut satırı arayüzleri veya diğer uygulamalar bulunur.
- Sürülen Adaptörler (İkincil Adaptörler): Dış sistemlerle etkileşim kurmak için çekirdek tarafından çağrılır. Örnekler arasında veritabanları, API'ler veya dosya sistemleri bulunur.
Çekirdek, belirli adaptörler hakkında hiçbir şey bilmez. Onlarla sadece portlar aracılığıyla etkileşime girer. Bu ayrım, çekirdek mantığı etkilemeden farklı adaptörleri kolayca değiştirmenize olanak tanır. Örneğin, sadece sürücü adaptörü değiştirerek bir UI framework'ünden (örneğin, React) diğerine (örneğin, Vue.js) geçiş yapabilirsiniz.
Hexagonal Mimarinin Faydaları:
- Geliştirilmiş Test Edilebilirlik: Temel iş mantığı, dış bağımlılıklara dayanmadan kolayca izole bir şekilde test edilebilir. Dış sistemlerin davranışını simüle etmek için sahte (mock) adaptörler kullanabilirsiniz.
- Artırılmış Sürdürülebilirlik: Dış sistemlerdeki değişikliklerin çekirdek mantık üzerindeki etkisi minimum düzeydedir. Bu, uygulamanın zamanla sürdürülmesini ve geliştirilmesini kolaylaştırır.
- Daha Fazla Esneklik: Adaptörler ekleyerek veya değiştirerek uygulamayı yeni teknolojilere ve gereksinimlere kolayca uyarlayabilirsiniz.
- Gelişmiş Yeniden Kullanılabilirlik: Temel iş mantığı, farklı adaptörlere bağlanarak farklı bağlamlarda yeniden kullanılabilir.
Clean Architecture'a Giriş
Robert C. Martin (Uncle Bob) tarafından popülerleştirilen Clean Architecture, endişelerin ayrılması ve ayrıştırma (decoupling) üzerine vurgu yapan bir başka mimari desendir. Framework'lerden, veritabanlarından, UI'dan ve herhangi bir dış etkenden bağımsız bir sistem oluşturmaya odaklanır.
Clean Architecture'ın Temel Kavramları:
Clean Architecture, uygulamayı eş merkezli katmanlar halinde organize eder; en soyut ve yeniden kullanılabilir kod merkezde, en somut ve teknolojiye özgü kod ise dış katmanlardadır.
- Varlıklar (Entities): Uygulamanın temel iş nesnelerini ve kurallarını temsil eder. Herhangi bir dış sistemden bağımsızdırlar.
- Kullanım Senaryoları (Use Cases): Uygulamanın iş mantığını ve kullanıcıların sistemle nasıl etkileşime girdiğini tanımlar. Belirli görevleri yerine getirmek için Varlıkları düzenlerler.
- Arayüz Adaptörleri (Interface Adapters): Kullanım Senaryoları ve dış sistemler arasında veriyi dönüştürür. Bu katman sunucuları (presenters), denetleyicileri (controllers) ve ağ geçitlerini (gateways) içerir.
- Framework'ler ve Sürücüler (Frameworks and Drivers): UI framework'ü, veritabanı ve diğer dış teknolojileri içeren en dış katmandır.
Clean Architecture'daki bağımlılık kuralı, dış katmanların iç katmanlara bağlı olabileceğini, ancak iç katmanların dış katmanlara bağlı olamayacağını belirtir. Bu, temel iş mantığının herhangi bir dış framework veya teknolojiden bağımsız olmasını sağlar.
Clean Architecture'ın Faydaları:
- Framework'lerden Bağımsız: Mimari, özellik dolu bir yazılım kütüphanesinin varlığına dayanmaz. Bu, framework'leri sisteminizi onların sınırlı kısıtlamalarına sokmak zorunda kalmak yerine, araç olarak kullanmanıza olanak tanır.
- Test Edilebilir: İş kuralları, UI, Veritabanı, Web Sunucusu veya başka herhangi bir dış unsur olmadan test edilebilir.
- UI'dan Bağımsız: UI, sistemin geri kalanını değiştirmeden kolayca değiştirilebilir. Bir Web UI, iş kurallarından hiçbirini değiştirmeden bir konsol UI ile değiştirilebilir.
- Veritabanından Bağımsız: Oracle veya SQL Server'ı Mongo, BigTable, CouchDB veya başka bir şeyle değiştirebilirsiniz. İş kurallarınız veritabanına bağlı değildir.
- Herhangi bir dış etkenden bağımsız: Aslında iş kurallarınız dış dünya hakkında *hiçbir şey* bilmez.
Hexagonal ve Clean Architecture'ı Frontend Geliştirmeye Uygulamak
Hexagonal ve Clean Architecture genellikle backend geliştirmeyle ilişkilendirilse de, ilkeleri mimarilerini ve sürdürülebilirliklerini iyileştirmek için frontend uygulamalarına etkili bir şekilde uygulanabilir. İşte nasıl yapılacağı:
1. Çekirdeği (Domain) Tanımlayın
İlk adım, frontend uygulamanızın temel iş mantığını tanımlamaktır. Bu, UI framework'ünden veya herhangi bir dış API'den bağımsız olan varlıkları, kullanım senaryolarını ve iş kurallarını içerir. Örneğin, bir e-ticaret uygulamasında çekirdek, ürünleri, alışveriş sepetlerini ve siparişleri yönetme mantığını içerebilir.
Örnek: Bir görev yönetimi uygulamasında, çekirdek domain şunlardan oluşabilir:
- Varlıklar: Görev, Proje, Kullanıcı
- Kullanım Senaryoları: GörevOluştur, GörevGüncelle, GörevAta, GörevTamamla, GörevleriListele
- İş Kuralları: Bir görevin bir başlığı olmalıdır, bir görev projenin üyesi olmayan bir kullanıcıya atanamaz.
2. Portları ve Adaptörleri (Hexagonal Mimari) veya Katmanları (Clean Architecture) Tanımlayın
Ardından, çekirdeği dış sistemlerden ayıran portları ve adaptörleri (Hexagonal Mimari) veya katmanları (Clean Architecture) tanımlayın. Bir frontend uygulamasında bunlar şunları içerebilir:
- UI Bileşenleri (Sürücü Adaptörler/Framework'ler ve Sürücüler): Kullanıcı ile etkileşime giren React, Vue.js, Angular bileşenleri.
- API İstemcileri (Sürülen Adaptörler/Arayüz Adaptörleri): Backend API'lerine isteklerde bulunan servisler.
- Veri Depoları (Sürülen Adaptörler/Arayüz Adaptörleri): Yerel depolama (Local storage), IndexedDB veya diğer veri depolama mekanizmaları.
- Durum Yönetimi (Arayüz Adaptörleri): Redux, Vuex veya diğer durum yönetimi kütüphaneleri.
Hexagonal Mimari Kullanarak Örnek:
- Çekirdek: Görev yönetimi mantığı (varlıklar, kullanım senaryoları, iş kuralları).
- Portlar:
TaskService(görev oluşturma, güncelleme ve alma yöntemlerini tanımlar). - Sürücü Adaptör: Çekirdek ile etkileşim kurmak için
TaskServicekullanan React bileşenleri. - Sürülen Adaptör:
TaskService'i uygulayan ve backend API'sine isteklerde bulunan API istemcisi.
Clean Architecture Kullanarak Örnek:
- Varlıklar: Görev, Proje, Kullanıcı (saf JavaScript nesneleri).
- Kullanım Senaryoları: CreateTaskUseCase, UpdateTaskUseCase (varlıkları yönetir).
- Arayüz Adaptörleri:
- Denetleyiciler (Controllers): UI'dan kullanıcı girdisini işler.
- Sunucular (Presenters): Veriyi UI'da görüntülemek için biçimlendirir.
- Ağ Geçitleri (Gateways): API istemcisi ile etkileşime girer.
- Framework'ler ve Sürücüler: React bileşenleri, API istemcisi (axios, fetch).
3. Adaptörleri (Hexagonal Mimari) veya Katmanları (Clean Architecture) Uygulayın
Şimdi, çekirdeği dış sistemlere bağlayan adaptörleri veya katmanları uygulayın. Adaptörlerin veya katmanların çekirdekten bağımsız olduğundan ve çekirdeğin onlarla yalnızca portlar veya arayüzler aracılığıyla etkileşime girdiğinden emin olun. Bu, çekirdek mantığı etkilemeden farklı adaptörleri veya katmanları kolayca değiştirmenize olanak tanır.
Örnek (Hexagonal Mimari):
// TaskService Port
interface TaskService {
createTask(taskData: TaskData): Promise;
updateTask(taskId: string, taskData: TaskData): Promise;
getTask(taskId: string): Promise;
}
// API Client Adapter
class ApiTaskService implements TaskService {
async createTask(taskData: TaskData): Promise {
// Make API request to create a task
}
async updateTask(taskId: string, taskData: TaskData): Promise {
// Make API request to update a task
}
async getTask(taskId: string): Promise {
// Make API request to get a task
}
}
// React Component Adapter
function TaskList() {
const taskService: TaskService = new ApiTaskService();
const handleCreateTask = async (taskData: TaskData) => {
await taskService.createTask(taskData);
// Update the task list
};
// ...
}
Örnek (Clean Architecture):
// Entities
class Task {
constructor(public id: string, public title: string, public description: string) {}
}
// Use Case
class CreateTaskUseCase {
constructor(private taskGateway: TaskGateway) {}
async execute(title: string, description: string): Promise {
const task = new Task(generateId(), title, description);
await this.taskGateway.create(task);
return task;
}
}
// Interface Adapters - Gateway
interface TaskGateway {
create(task: Task): Promise;
}
class ApiTaskGateway implements TaskGateway {
async create(task: Task): Promise {
// Make API request to create task
}
}
// Interface Adapters - Controller
class TaskController {
constructor(private createTaskUseCase: CreateTaskUseCase) {}
async createTask(req: Request, res: Response) {
const { title, description } = req.body;
const task = await this.createTaskUseCase.execute(title, description);
res.json(task);
}
}
// Frameworks & Drivers - React Component
function TaskForm() {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const apiTaskGateway = new ApiTaskGateway();
const createTaskUseCase = new CreateTaskUseCase(apiTaskGateway);
const taskController = new TaskController(createTaskUseCase);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await taskController.createTask({ body: { title, description } } as Request, { json: (data: any) => console.log(data) } as Response);
};
return (
);
}
4. Bağımlılık Enjeksiyonunu Uygulayın
Çekirdeği dış sistemlerden daha da ayırmak için, adaptörleri veya katmanları çekirdeğe sağlamak için bağımlılık enjeksiyonu kullanın. Bu, çekirdek kodu değiştirmeden adaptörlerin veya katmanların farklı uygulamalarını kolayca değiştirmenize olanak tanır.
Örnek:
// Inject the TaskService into the TaskList component
function TaskList(props: { taskService: TaskService }) {
const { taskService } = props;
const handleCreateTask = async (taskData: TaskData) => {
await taskService.createTask(taskData);
// Update the task list
};
// ...
}
// Usage
const apiTaskService = new ApiTaskService();
5. Birim Testleri Yazın
Hexagonal ve Clean Architecture'ın temel faydalarından biri, geliştirilmiş test edilebilirliktir. Temel iş mantığı için dış bağımlılıklara dayanmadan kolayca birim testleri yazabilirsiniz. Dış sistemlerin davranışını simüle etmek ve çekirdek mantığın beklendiği gibi çalıştığını doğrulamak için sahte (mock) adaptörler veya katmanlar kullanın.
Örnek:
// Mock TaskService
class MockTaskService implements TaskService {
async createTask(taskData: TaskData): Promise {
return Promise.resolve({ id: '1', ...taskData });
}
async updateTask(taskId: string, taskData: TaskData): Promise {
return Promise.resolve({ id: taskId, ...taskData });
}
async getTask(taskId: string): Promise {
return Promise.resolve({ id: taskId, title: 'Test Task', description: 'Test Description' });
}
}
// Unit Test
describe('TaskList', () => {
it('should create a task', async () => {
const mockTaskService = new MockTaskService();
const taskList = new TaskList({ taskService: mockTaskService });
const taskData = { title: 'New Task', description: 'New Description' };
const newTask = await taskList.handleCreateTask(taskData);
expect(newTask.title).toBe('New Task');
expect(newTask.description).toBe('New Description');
});
});
Pratik Hususlar ve Zorluklar
Hexagonal ve Clean Architecture önemli faydalar sunsa da, bunları frontend geliştirmeye uygularken akılda tutulması gereken bazı pratik hususlar ve zorluklar da vardır:
- Artan Karmaşıklık: Bu mimariler, özellikle küçük veya basit uygulamalar için kod tabanına karmaşıklık ekleyebilir.
- Öğrenme Eğrisi: Geliştiricilerin bu mimarileri etkili bir şekilde uygulamak için yeni kavramlar ve desenler öğrenmeleri gerekebilir.
- Aşırı Mühendislik (Over-Engineering): Uygulamayı aşırı mühendislikten kaçınmak önemlidir. Basit bir mimariyle başlayın ve gerektiğinde yavaş yavaş karmaşıklık ekleyin.
- Soyutlamayı Dengeleme: Doğru soyutlama seviyesini bulmak zor olabilir. Çok fazla soyutlama kodu anlaşılması zor hale getirebilirken, çok az soyutlama sıkı sıkıya bağlılığa (tight coupling) yol açabilir.
- Performans Değerlendirmeleri: Aşırı soyutlama katmanları potansiyel olarak performansı etkileyebilir. Uygulamanın profilini çıkarmak ve herhangi bir performans darboğazını belirlemek önemlidir.
Uluslararası Örnekler ve Uyarlamalar
Hexagonal ve Clean Architecture ilkeleri, coğrafi konum veya kültürel bağlamdan bağımsız olarak frontend geliştirmeye uygulanabilir. Ancak, belirli uygulamalar ve uyarlamalar proje gereksinimlerine ve geliştirme ekibinin tercihlerine bağlı olarak değişebilir.
Örnek 1: Küresel Bir E-ticaret Platformu
Küresel bir e-ticaret platformu, temel alışveriş sepeti ve sipariş yönetimi mantığını UI framework'ünden ve ödeme ağ geçitlerinden ayırmak için Hexagonal Mimari kullanabilir. Çekirdek, ürünleri yönetmekten, fiyatları hesaplamaktan ve siparişleri işlemekten sorumlu olacaktır. Sürücü adaptörler, ürün kataloğu, alışveriş sepeti ve ödeme sayfaları için React bileşenlerini içerecektir. Sürülen adaptörler, farklı ödeme ağ geçitleri (örneğin, Stripe, PayPal, Alipay) ve kargo sağlayıcıları (örneğin, FedEx, DHL, UPS) için API istemcilerini içerecektir. Bu, platformun farklı bölgesel ödeme yöntemlerine ve kargo seçeneklerine kolayca uyum sağlamasına olanak tanır.
Örnek 2: Çok Dilli Bir Sosyal Medya Uygulaması
Çok dilli bir sosyal medya uygulaması, temel kullanıcı kimlik doğrulama ve içerik yönetimi mantığını UI ve yerelleştirme framework'lerinden ayırmak için Clean Architecture kullanabilir. Varlıklar kullanıcıları, gönderileri ve yorumları temsil edecektir. Kullanım senaryoları, kullanıcıların nasıl içerik oluşturduğunu, paylaştığını ve içerikle nasıl etkileşime girdiğini tanımlayacaktır. Arayüz adaptörleri, içeriğin farklı dillere çevrilmesini ve verilerin farklı UI bileşenleri için biçimlendirilmesini ele alacaktır. Bu, uygulamanın kolayca yeni dilleri desteklemesine ve farklı kültürel tercihlere uyum sağlamasına olanak tanır.
Sonuç
Hexagonal ve Clean Architecture, sürdürülebilir, test edilebilir ve ölçeklenebilir frontend uygulamaları oluşturmak için değerli ilkeler sunar. Temel iş mantığını dış bağımlılıklardan ayırarak, zamanla geliştirilmesi daha kolay olan daha esnek ve uyarlanabilir bir kod tabanı oluşturabilirsiniz. Bu mimariler başlangıçta biraz karmaşıklık eklese de, sürdürülebilirlik, test edilebilirlik ve ölçeklenebilirlik açısından uzun vadeli faydaları, onları karmaşık frontend projeleri için değerli bir yatırım haline getirir. Basit bir mimariyle başlamayı ve gerektiğinde yavaş yavaş karmaşıklık eklemeyi ve ilgili pratik hususları ve zorlukları dikkatlice değerlendirmeyi unutmayın.
Bu mimari desenleri benimseyerek, frontend geliştiricileri dünya çapındaki kullanıcıların değişen ihtiyaçlarını karşılayabilecek daha sağlam ve güvenilir uygulamalar oluşturabilirler.
İleri Okuma
- Hexagonal Architecture: https://alistaircockburn.com/hexagonal-architecture/
- Clean Architecture: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html