Giriş: Mimari Neden Önemlidir?
Yazılım mimarisi, bir binanın mimarisi gibidir. İyi bir mimari olmadan, başlangıçta hızlı ilerleyebilirsiniz ama zamanla her değişiklik bir kabus haline gelir. Teknik borç birikir, hata oranı artar ve yeni özellik eklemek giderek zorlaşır.
İyi bir yazılım mimarisi şu faydaları sağlar:
- Bakım Kolaylığı: 6 ay sonra koda döndğünüzde ne yaptığınızı hemen anlarsınız
- Ekip Hızı: Birden fazla kişi aynı anda çalışabilir
- Test Edilebilirlik: Kod parçalarını izole ederek test yazabilirsiniz
- Ölçeklenebilirlik: Yeni özellikler eklemek kolay ve tahmin edilebilirdir
"Mimari, gelecekteki değişiklikleri kolaylaştırmak için yapılan kararlar bütünüdür." - Robert C. Martin (Uncle Bob)
Yaygın Mimari Desenler
1. Monolith (Monolit)
Tüm uygulama tek bir kod tabanında ve tek bir deployment birimi olarak çalışır. Küçük ve orta ölçekli projeler için en pratik başlangıçtır.
Artıları: Geliştirme hızı, debugging kolaylığı, daha az operasyonel karmaşıklık
Eksileri: Çok büyüdüğünde yönetim zorlaşır
2. Layered (Katmanlı) Mimari
Uygulamayı yatay katmanlara ayırır. Her katman sadece altındaki katmanla konuşur:
- Presentation Layer: UI/API - kullanıcı ile etkileşim
- Business Logic Layer: İş kuralları ve mantık
- Data Access Layer: Veritabanı işlemleri
3. Hexagonal (Ports & Adapters)
İş mantığını dış dünyadan izole eder. Veritabanı, API, UI - hepsi "adapter"dır ve değiştirilebilir. Test yazımını kolaylaştırır.
4. Modular Monolith
Monolit yapı ama içeride modüler organizasyon. Mikroservislerin faydalarını monolit basitliği ile birleştirir. Öğrenci projeleri için ideal!
Frontend & Backend Sınırları
API Contract (Anlaşma)
Frontend ve backend arasındaki iletişim, bir contract (sözleşme) ile belirlenmelidir. Değişiklikler versiyonlanmalı:
GET /api/v1/users
{
"id": 1,
"name": "Ali Veli"
}
GET /api/v2/users
{
"id": 1,
"name": "Ali Veli",
"avatar": "https://...",
"preferences": { "theme": "dark" }
}
DTO (Data Transfer Objects)
Veritabanı modelleri ile API cevapları farklı olmalıdır. DTO'lar, hassas bilgileri filtreler ve sadece gerekli veriyi döndürür:
app.get('/users/:id', async (req, res) => {
const user = await User.findById(req.params.id);
res.json(user);
});
class UserDTO {
id: number;
name: string;
email: string;
static fromModel(user: User): UserDTO {
return {
id: user.id,
name: user.name,
email: user.email,
};
}
}
app.get('/users/:id', async (req, res) => {
const user = await User.findById(req.params.id);
res.json(UserDTO.fromModel(user));
});
Domain Driven Design (DDD) Temelleri
DDD, karmaşık iş mantığını yönetmek için bir yaklaşımdır. Temel kavramlar:
1. Entity (Varlık)
Benzersiz kimliği olan nesneler. Örnek: User, Order, Product
2. Value Object
Kimliği olmayan, değerleriyle tanımlanan nesneler. Örnek: Address, Money, DateRange
class Money {
constructor(
public readonly amount: number,
public readonly currency: string
) {}
add(other: Money): Money {
if (this.currency !== other.currency) {
throw new Error('Cannot add different currencies');
}
return new Money(this.amount + other.amount, this.currency);
}
}
3. Aggregate (Topluluk)
İlgili entity ve value object'lerin grubu. Dışarıdan sadece aggregate root üzerinden erişilir.
4. Repository Pattern
Veri erişim mantığını soyutlar. İş mantığı, veritabanının nasıl çalıştığını bilmez:
interface UserRepository {
findById(id: number): Promise<User | null>;
save(user: User): Promise<void>;
delete(id: number): Promise<void>;
}
class PostgresUserRepository implements UserRepository {
async findById(id: number): Promise<User | null> {
const result = await pool.query('SELECT * FROM users WHERE id = $1', [id]);
return result.rows[0] ? User.fromRow(result.rows[0]) : null;
}
}
class UserService {
constructor(private userRepo: UserRepository) {}
async promoteToAdmin(userId: number) {
const user = await this.userRepo.findById(userId);
if (!user) throw new Error('User not found');
user.role = 'admin';
await this.userRepo.save(user);
}
}
Basit Klasör/Kod Yapısı Önerisi
Modüler bir Node.js backend projesi için örnek klasör yapısı:
src/
├── modules/
│ ├── user/
│ │ ├── user.entity.ts # User domain model
│ │ ├── user.repository.ts # Veri erişim soyutlaması
│ │ ├── user.service.ts # İş mantığı
│ │ ├── user.controller.ts # HTTP endpoint'leri
│ │ ├── user.dto.ts # API transfer nesneleri
│ │ └── user.routes.ts # Route tanımları
│ ├── order/
│ │ ├── order.entity.ts
│ │ ├── order.repository.ts
│ │ ├── order.service.ts
│ │ └── ...
│ └── product/
│ └── ...
├── shared/
│ ├── database.ts # DB connection pool
│ ├── logger.ts # Logging utility
│ └── validation.ts # Ortak validasyon fonksiyonları
├── config/
│ └── index.ts # Environment değişkenleri
└── app.ts # Express app setup
Bu yapıda:
- Her modül kendi içinde bağımsızdır (user, order, product)
- Katmanlar açıkça ayrıştırılmıştır (entity, repository, service, controller)
- Yeni bir özellik eklerken, sadece ilgili modül klasörüne gidersiniz
Refactor Stratejileri
1. Küçük Adımlar (Baby Steps)
Büyük bir refactoring yapmak yerine, her gün küçük iyileştirmeler yapın.
2. Test-First Yaklaşımı
Refactoring öncesi test yazın, sonra kodu değiştirin. Testler bozulmadıysa, davranış aynı kalmış demektir.
3. Strangler Fig Pattern
Eski kodu aniden değiştirmek yerine, yeni kod ile kademeli olarak "boğun". Yeni özellikler yeni mimaride yazılır, eski kod zamanla emekliye ayrılır.
4. Feature Toggles
Yeni mimariyi production'a gönderin ama kapalı tutun. Test ettikten sonra açın:
if (featureFlags.newUserService) {
await newUserService.createUser(data);
} else {
await legacyUserService.createUser(data);
}
Decision Record: ADR (Architecture Decision Record)
Teknik kararları belgelemek, gelecekteki ekip üyelerinin "neden böyle yaptık?" sorusuna cevap verir.
ADR Şablonu Örneği
# ADR-001: PostgreSQL Yerine MongoDB Seçimi
## Durum
Kabul Edildi
## Bağlam
Kullanıcı profil verileri çok değişken (farklı kullanıcılar farklı alanlar).
Hızlı prototipleme ihtiyacı var.
## Karar
NoSQL veritabanı (MongoDB) kullanacağız.
## Sonuçlar
**Artılar:**
- Esnek şema
- JSON-native veri modeli
- Hızlı geliştirme
**Eksiler:**
- ACID garantisi yok
- JOIN işlemleri zor
- İlişkisel veri modellemesi sınırlı
## Alternatifler
- PostgreSQL JSONB: Dikkate alındı ama şema gereksinimi var
- DynamoDB: AWS vendor lock-in riski
Kaynaklar ve Örnek Repo'lar
- Clean Architecture (Robert C. Martin): Mimari prensipler kitabı
- Domain-Driven Design (Eric Evans): DDD'nin biblesı
- NestJS Docs: Enterprise-ready Node.js mimarisi
- Martin Fowler Blog: Mimari desenler ve refactoring
Mimari, sadece büyük şirketler için değildir. Öğrenci projelerinizde bile iyi mimari uygulamak, sizi daha iyi bir geliştirice dönüştürür. Zamanla mimari kararlarınız ötelenir ama asla kaybolmaz - bugün verdiğiniz kararlar, yarının borcu veya kazancıdır.
