Skip to content

Mobil Mimari Rehberi (Mobile Architecture Guide)

Bu Rehber Nedir? (What Is This Guide?)

Mobil uygulamalarda sürdürülebilir, test edilebilir ve ölçeklenebilir mimari kurmak için kapsamlı bir rehber. React Native ve Flutter örnekleriyle mimari yaklaşımlar, offline-first stratejiler, güvenlik ve erişilebilirlik konularını kapsar.



1) Mimari Yaklaşımlar (Architectural Approaches)

Mobil uygulamalarda doğru mimari seçimi; bakım kolaylığı, test edilebilirlik ve ekip verimliliğini doğrudan etkiler.

Karşılaştırma Tablosu (Comparison Table)

Özellik (Feature)MVCMVPMVVMClean Architecture
Tam AdıModel-View-ControllerModel-View-PresenterModel-View-ViewModelUncle Bob's Clean Arch
Katman Sayısı (Layers)3333-4 (iç içe daireler)
View-Logic AyrımıZayıf — Controller şişerİyi — Presenter aracıÇok iyi — data bindingMükemmel — katı sınırlar
Test Edilebilirlik (Testability)DüşükYüksekYüksekÇok yüksek
Öğrenme Eğrisi (Learning Curve)DüşükOrtaOrtaYüksek
Bağımlılık Yönü (Dependency Direction)KarışıkView → Presenter → ModelView → ViewModel → ModelDıştan içe (outer → inner)
Reaktif Destek (Reactive Support)ManuelManuelDoğal (Observable/Stream)Framework bağımsız
Küçük Proje İçin (Small Projects)UygunFazla boilerplateUygunFazla karmaşık
Büyük Proje İçin (Large Projects)YetersizUygunÇok uygunEn uygun
Mobil Kullanım (Mobile Usage)iOS (UIKit eski stil)Android (eski Java projeleri)RN + Flutter (yaygın)RN + Flutter (kurumsal)

2) MVVM Detaylı (MVVM in Detail)

MVVM, mobil uygulamalarda en yaygın kullanılan mimari kalıptır. View ile iş mantığını ayırarak test edilebilirlik ve bakım kolaylığı sağlar.

MVVM Katmanları (MVVM Layers)

┌─────────────────────────────────────────────┐
│                   VIEW                       │
│  (Ekran / Bileşen — Screen / Component)      │
│  Kullanıcı etkileşimi, UI render             │
│  ViewModel'i gözlemler (observes)             │
└──────────────────┬──────────────────────────┘
                   │ data binding / subscribe
┌──────────────────▼──────────────────────────┐
│               VIEW MODEL                     │
│  (Durum yönetimi — State management)         │
│  İş mantığı, UI state, validasyon            │
│  Model katmanını çağırır                     │
└──────────────────┬──────────────────────────┘
                   │ repository / service call
┌──────────────────▼──────────────────────────┐
│                 MODEL                        │
│  (Veri katmanı — Data layer)                 │
│  API, veritabanı, cache                      │
└─────────────────────────────────────────────┘

React Native'de MVVM

typescript
// ── Model (types + repository) ──
interface User {
  id: string;
  name: string;
  email: string;
}

class UserRepository {
  async getUser(id: string): Promise<User> {
    const response = await api.get(`/users/${id}`);
    return response.data;
  }

  async updateUser(id: string, data: Partial<User>): Promise<User> {
    const response = await api.put(`/users/${id}`, data);
    return response.data;
  }
}

// ── ViewModel (custom hook) ──
function useUserViewModel(userId: string) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const repository = useMemo(() => new UserRepository(), []);

  const fetchUser = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const data = await repository.getUser(userId);
      setUser(data);
    } catch (err) {
      setError('Kullanıcı yüklenemedi');
    } finally {
      setLoading(false);
    }
  }, [userId, repository]);

  const updateName = useCallback(async (name: string) => {
    if (!user) return;
    try {
      const updated = await repository.updateUser(user.id, { name });
      setUser(updated);
    } catch (err) {
      setError('Güncelleme başarısız');
    }
  }, [user, repository]);

  useEffect(() => { fetchUser(); }, [fetchUser]);

  return { user, loading, error, updateName, retry: fetchUser };
}

// ── View (component) ──
function UserProfileScreen({ userId }: { userId: string }) {
  const { user, loading, error, updateName, retry } = useUserViewModel(userId);

  if (loading) return <LoadingSpinner />;
  if (error) return <ErrorView message={error} onRetry={retry} />;

  return (
    <View>
      <Text>{user?.name}</Text>
      <Button title="İsmi Güncelle" onPress={() => updateName('Yeni İsim')} />
    </View>
  );
}

Flutter'da MVVM

dart
// ── Model ──
class User {
  final String id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});

  factory User.fromJson(Map<String, dynamic> json) => User(
    id: json['id'],
    name: json['name'],
    email: json['email'],
  );
}

// ── Repository ──
class UserRepository {
  final ApiClient _api;
  UserRepository(this._api);

  Future<User> getUser(String id) async {
    final response = await _api.get('/users/$id');
    return User.fromJson(response.data);
  }
}

// ── ViewModel (ChangeNotifier) ──
class UserViewModel extends ChangeNotifier {
  final UserRepository _repository;

  User? _user;
  bool _loading = false;
  String? _error;

  User? get user => _user;
  bool get loading => _loading;
  String? get error => _error;

  UserViewModel(this._repository);

  Future<void> fetchUser(String id) async {
    _loading = true;
    _error = null;
    notifyListeners();

    try {
      _user = await _repository.getUser(id);
    } catch (e) {
      _error = 'Kullanıcı yüklenemedi';
    } finally {
      _loading = false;
      notifyListeners();
    }
  }
}

// ── View (Widget) ──
class UserProfileScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<UserViewModel>(
      builder: (context, vm, child) {
        if (vm.loading) return const CircularProgressIndicator();
        if (vm.error != null) return ErrorWidget(message: vm.error!);

        return Column(
          children: [
            Text(vm.user?.name ?? ''),
            ElevatedButton(
              onPressed: () => vm.fetchUser(vm.user!.id),
              child: const Text('Yenile'),
            ),
          ],
        );
      },
    );
  }
}

3) Clean Architecture (Temiz Mimari)

Clean Architecture, iş mantığını framework ve altyapı detaylarından tamamen izole eder. Uncle Bob (Robert C. Martin) tarafından tanımlanmıştır.

Katman Yapısı (Layer Structure)

┌─────────────────────────────────────────────────┐
│              PRESENTATION LAYER                  │
│  (UI, Screens, Widgets, ViewModels, Controllers) │
│  Framework'e bağımlı (Framework-dependent)       │
├─────────────────────────────────────────────────┤
│                DOMAIN LAYER                      │
│  (Entities, Use Cases, Repository Interfaces)    │
│  Hiçbir şeye bağımlı değil (No dependencies)    │
├─────────────────────────────────────────────────┤
│                 DATA LAYER                       │
│  (Repository Impl, API, Local DB, DTO, Mapper)  │
│  Domain'deki interface'leri implemente eder      │
└─────────────────────────────────────────────────┘

Bağımlılık Kuralı (Dependency Rule)

Presentation ──depends on──> Domain <──depends on── Data


                   İş mantığı burada yaşar
                   (Business logic lives here)
  • Domain katmanı hiçbir katmana bağımlı değildir (Domain depends on nothing)
  • Presentation yalnızca Domain'e bağımlıdır (Presentation depends only on Domain)
  • Data yalnızca Domain'deki interface'leri implemente eder (Data implements Domain interfaces)

Use Case Örneği (Use Case Example)

typescript
// ── Domain: Entity ──
interface Product {
  id: string;
  name: string;
  price: number;
  stock: number;
}

// ── Domain: Repository Interface ──
interface ProductRepository {
  getProducts(): Promise<Product[]>;
  getProductById(id: string): Promise<Product>;
  searchProducts(query: string): Promise<Product[]>;
}

// ── Domain: Use Case ──
class GetProductsUseCase {
  constructor(private repository: ProductRepository) {}

  async execute(query?: string): Promise<Product[]> {
    if (query && query.length >= 2) {
      return this.repository.searchProducts(query);
    }
    return this.repository.getProducts();
  }
}

// ── Data: Repository Implementation ──
class ProductRepositoryImpl implements ProductRepository {
  constructor(
    private api: ApiClient,
    private localDb: LocalDatabase
  ) {}

  async getProducts(): Promise<Product[]> {
    try {
      const products = await this.api.get('/products');
      await this.localDb.saveProducts(products);
      return products;
    } catch {
      return this.localDb.getProducts(); // offline fallback
    }
  }

  async getProductById(id: string): Promise<Product> {
    return this.api.get(`/products/${id}`);
  }

  async searchProducts(query: string): Promise<Product[]> {
    return this.api.get(`/products?q=${query}`);
  }
}

4) Repository Pattern (API + Local Cache)

Repository pattern, veri kaynağını soyutlayarak API ve local cache arasında şeffaf geçiş sağlar.

Strateji Akışı (Strategy Flow)

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│   ViewModel  │────>│  Repository  │────>│   API Client │
│              │     │              │     │   (Remote)   │
└──────────────┘     │              │     └──────────────┘
                     │              │
                     │              │────>┌──────────────┐
                     │              │     │  Local DB    │
                     └──────────────┘     │  (SQLite/    │
                                          │   Hive/MMKV) │
                                          └──────────────┘

Cache Stratejileri (Cache Strategies)

Strateji (Strategy)Açıklama (Description)Kullanım Alanı (Use Case)
Cache-FirstÖnce cache'e bak, yoksa API'ye gitSık değişmeyen veriler (profil, ayarlar)
Network-FirstÖnce API'ye git, hata olursa cache'e düşGüncel veri önemli (feed, mesajlar)
Cache-then-NetworkHemen cache'i göster, arka planda API'den güncelleEn iyi UX — anında yükleme + güncel veri
Network-OnlySadece API kullanHassas veriler (ödeme, OTP)
Cache-OnlySadece cache kullanTamamen offline senaryolar

Cache-then-Network Örneği (React Native)

typescript
class ProductRepository {
  constructor(
    private api: ApiClient,
    private cache: CacheStorage,
    private ttl: number = 5 * 60 * 1000 // 5 dakika (5 minutes)
  ) {}

  async getProducts(forceRefresh = false): Promise<{
    data: Product[];
    source: 'cache' | 'network';
  }> {
    // 1. Cache'i kontrol et (Check cache)
    if (!forceRefresh) {
      const cached = await this.cache.get<Product[]>('products');
      if (cached && !this.isExpired(cached.timestamp)) {
        // Arka planda güncelle (Update in background)
        this.refreshInBackground();
        return { data: cached.data, source: 'cache' };
      }
    }

    // 2. API'den al (Fetch from API)
    try {
      const products = await this.api.get<Product[]>('/products');
      await this.cache.set('products', products);
      return { data: products, source: 'network' };
    } catch (error) {
      // 3. Offline fallback
      const cached = await this.cache.get<Product[]>('products');
      if (cached) return { data: cached.data, source: 'cache' };
      throw error;
    }
  }

  private isExpired(timestamp: number): boolean {
    return Date.now() - timestamp > this.ttl;
  }

  private async refreshInBackground(): Promise<void> {
    try {
      const products = await this.api.get<Product[]>('/products');
      await this.cache.set('products', products);
    } catch {
      // Sessizce başarısız ol (Silently fail)
    }
  }
}

5) State Management Mimarileri Karşılaştırma (State Management Comparison)

React Native

Çözüm (Solution)Yaklaşım (Approach)Öğrenme EğrisiBoilerplatePerformansİdeal Kullanım
Context APIReact yerleşikDüşükDüşükDüşük (gereksiz render)Küçük uygulamalar, tema/dil
ZustandMinimal storeDüşükÇok düşükYüksekOrta ölçekli projeler
Redux ToolkitFlux mimarisiOrtaOrtaYüksekBüyük, karmaşık state
JotaiAtomic stateDüşükDüşükYüksekBağımsız state parçaları
MobXObservable/ReactiveOrtaDüşükYüksekReaktif veri akışları
React QueryServer stateDüşükDüşükYüksekAPI veri yönetimi

Flutter

Çözüm (Solution)Yaklaşım (Approach)Öğrenme EğrisiBoilerplatePerformansİdeal Kullanım
setStateFlutter yerleşikDüşükDüşükDüşük (tüm widget rebuild)Widget-lokal state
ProviderInheritedWidget wrapperDüşükDüşükİyiKüçük-orta projeler
RiverpodProvider 2.0, derleme güvenliOrtaOrtaÇok iyiOrta-büyük projeler
Bloc/CubitEvent-driven, stream tabanlıYüksekYüksekÇok iyiKurumsal, büyük projeler
GetXHepsi bir aradaDüşükÇok düşükİyiHızlı prototip
SignalsReactive primitivesDüşükDüşükÇok iyiModern Flutter projeleri

6) Proje Yapısı (Project Structure)

Feature-Based (Özellik Bazlı) vs Layer-Based (Katman Bazlı)

YaklaşımAvantaj (Advantage)Dezavantaj (Disadvantage)
Feature-BasedÖzellik izolasyonu, ekip paralelliği, ölçeklenebilirPaylaşılan kodun yönetimi zor
Layer-BasedAnlaşılması kolay, küçük projelere uygunBüyük projelerde dosya kalabalığı

React Native — Feature-Based Yapı

src/
├── app/                          # Uygulama konfigürasyonu
│   ├── App.tsx
│   ├── navigation/
│   │   ├── RootNavigator.tsx
│   │   ├── AuthNavigator.tsx
│   │   └── MainTabNavigator.tsx
│   └── providers/
│       └── AppProviders.tsx
├── features/                     # Özellik modülleri
│   ├── auth/
│   │   ├── screens/
│   │   │   ├── LoginScreen.tsx
│   │   │   └── RegisterScreen.tsx
│   │   ├── hooks/
│   │   │   └── useAuthViewModel.ts
│   │   ├── services/
│   │   │   └── AuthService.ts
│   │   ├── types/
│   │   │   └── auth.types.ts
│   │   └── components/
│   │       └── LoginForm.tsx
│   ├── products/
│   │   ├── screens/
│   │   ├── hooks/
│   │   ├── services/
│   │   └── components/
│   └── profile/
│       ├── screens/
│       ├── hooks/
│       ├── services/
│       └── components/
├── shared/                       # Paylaşılan kod
│   ├── components/
│   │   ├── Button.tsx
│   │   ├── Input.tsx
│   │   └── ErrorBoundary.tsx
│   ├── hooks/
│   │   ├── useNetwork.ts
│   │   └── useTheme.ts
│   ├── services/
│   │   ├── ApiClient.ts
│   │   └── StorageService.ts
│   ├── utils/
│   │   ├── formatters.ts
│   │   └── validators.ts
│   └── constants/
│       ├── colors.ts
│       └── endpoints.ts
└── types/                        # Global tipler
    └── global.d.ts

Flutter — Feature-Based Yapı

lib/
├── app/                          # Uygulama konfigürasyonu
│   ├── app.dart
│   ├── router/
│   │   └── app_router.dart
│   └── theme/
│       ├── app_theme.dart
│       └── app_colors.dart
├── features/                     # Özellik modülleri
│   ├── auth/
│   │   ├── presentation/
│   │   │   ├── screens/
│   │   │   │   ├── login_screen.dart
│   │   │   │   └── register_screen.dart
│   │   │   ├── widgets/
│   │   │   │   └── login_form.dart
│   │   │   └── viewmodels/
│   │   │       └── auth_viewmodel.dart
│   │   ├── domain/
│   │   │   ├── entities/
│   │   │   │   └── user.dart
│   │   │   ├── repositories/
│   │   │   │   └── auth_repository.dart
│   │   │   └── usecases/
│   │   │       └── login_usecase.dart
│   │   └── data/
│   │       ├── repositories/
│   │       │   └── auth_repository_impl.dart
│   │       ├── datasources/
│   │       │   ├── auth_remote_source.dart
│   │       │   └── auth_local_source.dart
│   │       └── models/
│   │           └── user_model.dart
│   ├── products/
│   └── profile/
├── core/                         # Paylaşılan çekirdek
│   ├── network/
│   │   ├── api_client.dart
│   │   └── interceptors.dart
│   ├── storage/
│   │   └── local_storage.dart
│   ├── utils/
│   │   └── validators.dart
│   └── widgets/
│       ├── custom_button.dart
│       └── loading_indicator.dart
└── main.dart

7) Offline-First & Data Sync (Çevrimdışı Öncelikli & Veri Senkronizasyonu)

Offline-First Stratejisi (Offline-First Strategy)

┌─────────────┐    ┌──────────────┐    ┌─────────────┐
│  Kullanıcı  │───>│  Local DB    │───>│  Sync Queue │
│  Eylemi     │    │  (Önce yaz)  │    │  (Sıraya al)│
│  (User Act) │    │  (Write first)│    │  (Enqueue)  │
└─────────────┘    └──────────────┘    └──────┬──────┘

                          ┌───────────────────▼──────┐
                          │  Bağlantı var mı?        │
                          │  (Is connected?)         │
                          ├──── Evet ───>  API Sync  │
                          ├──── Hayır ──>  Bekle     │
                          └──────────────────────────┘

Optimistic vs Pessimistic UI

Yaklaşım (Approach)Açıklama (Description)AvantajDezavantajKullanım Alanı
Optimistic UISunucu yanıtı beklenmeden UI güncellenirAnlık hissettirir, hızlı UXHata durumunda geri alma gerekirBeğeni, yorum, sepet ekleme
Pessimistic UISunucu yanıtı beklenip sonra UI güncellenirTutarlı veri, hata yokLoading gösterilir, yavaş hissettirirÖdeme, sipariş, transfer

Queue-Based Sync Örneği

typescript
interface SyncQueueItem {
  id: string;
  action: 'CREATE' | 'UPDATE' | 'DELETE';
  endpoint: string;
  payload: any;
  timestamp: number;
  retryCount: number;
  maxRetries: number;
}

class SyncManager {
  private queue: SyncQueueItem[] = [];
  private isProcessing = false;

  async enqueue(item: Omit<SyncQueueItem, 'id' | 'timestamp' | 'retryCount'>): Promise<void> {
    const queueItem: SyncQueueItem = {
      ...item,
      id: generateUUID(),
      timestamp: Date.now(),
      retryCount: 0,
    };
    this.queue.push(queueItem);
    await this.persistQueue();
    this.processQueue();
  }

  private async processQueue(): Promise<void> {
    if (this.isProcessing || this.queue.length === 0) return;
    if (!await isNetworkAvailable()) return;

    this.isProcessing = true;
    const item = this.queue[0];

    try {
      await this.executeSync(item);
      this.queue.shift(); // Başarılı — kuyruktan çıkar
      await this.persistQueue();
    } catch (error) {
      item.retryCount++;
      if (item.retryCount >= item.maxRetries) {
        this.queue.shift();
        await this.handleFailedItem(item);
      }
    } finally {
      this.isProcessing = false;
      if (this.queue.length > 0) this.processQueue();
    }
  }

  private async executeSync(item: SyncQueueItem): Promise<void> {
    switch (item.action) {
      case 'CREATE': await api.post(item.endpoint, item.payload); break;
      case 'UPDATE': await api.put(item.endpoint, item.payload); break;
      case 'DELETE': await api.delete(item.endpoint); break;
    }
  }

  private async persistQueue(): Promise<void> {
    await AsyncStorage.setItem('sync_queue', JSON.stringify(this.queue));
  }

  private async handleFailedItem(item: SyncQueueItem): Promise<void> {
    // Başarısız öğeyi logla veya kullanıcıya bildir
    console.error(`Senkronizasyon başarısız: ${item.endpoint}`, item);
  }
}

Yerel Veritabanı Seçenekleri (Local Database Options)

Veritabanı (Database)PlatformTür (Type)İdeal Kullanım
SQLite (Expo SQLite)RNİlişkisel (Relational)Karmaşık sorgular, büyük veri
MMKVRNKey-ValueBasit ayarlar, token, tercihler
WatermelonDBRNİlişkisel (reactive)Offline-first, büyük veri setleri
HiveFlutterNoSQL Key-ValueHızlı okuma/yazma, basit veriler
IsarFlutterNoSQLBüyük veri, full-text arama
Drift (Moor)Flutterİlişkisel (Relational)Karmaşık sorgular, tip güvenliği

8) Navigation Patterns (Navigasyon Kalıpları)

Tür (Type)Açıklama (Description)Kullanım Alanı
StackEkranlar üst üste yığılır, geri tuşu ile dönüşDetay sayfaları, form akışları
TabAlt/üst sekmeler ile paralel ekranlarAna ekranlar (Home, Search, Profile)
DrawerYan menüden açılan navigasyonAyarlar, kategori listesi
ModalMevcut ekranın üzerine açılan panelFiltre, onay, hızlı işlem
Deep LinkURL ile doğrudan belirli ekrana yönlendirmeBildirim, paylaşım, marketing

Auth Flow Navigasyon Yapısı (Auth Flow Structure)

┌─────────────────────────────────────────────┐
│                Root Navigator                │
│                                              │
│  ┌─── Token var mı? (Has token?) ─────┐    │
│  │                                      │    │
│  │  Hayır (No)         Evet (Yes)      │    │
│  │     │                   │            │    │
│  │     ▼                   ▼            │    │
│  │  Auth Stack         Main Tab Nav     │    │
│  │  ├── Splash         ├── Home Stack   │    │
│  │  ├── Login          ├── Search       │    │
│  │  ├── Register       ├── Cart         │    │
│  │  └── ForgotPass     └── Profile Stack│    │
│  │                          ├── Profile │    │
│  │                          ├── Settings│    │
│  │                          └── Orders  │    │
│  └──────────────────────────────────────┘    │
└─────────────────────────────────────────────┘

Deep Linking Yapılandırması (Deep Linking Configuration)

typescript
// React Native — React Navigation deep linking
const linking = {
  prefixes: ['myapp://', 'https://myapp.com'],
  config: {
    screens: {
      MainTab: {
        screens: {
          Home: 'home',
          ProductDetail: 'products/:id',
          Profile: 'profile',
        },
      },
      Auth: {
        screens: {
          Login: 'login',
          ResetPassword: 'reset-password/:token',
        },
      },
    },
  },
};
dart
// Flutter — GoRouter deep linking
final router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
      routes: [
        GoRoute(
          path: 'products/:id',
          builder: (context, state) {
            final id = state.pathParameters['id']!;
            return ProductDetailScreen(productId: id);
          },
        ),
      ],
    ),
    GoRoute(
      path: '/login',
      builder: (context, state) => const LoginScreen(),
    ),
  ],
  redirect: (context, state) {
    final isLoggedIn = authNotifier.isLoggedIn;
    final isLoginRoute = state.matchedLocation == '/login';

    if (!isLoggedIn && !isLoginRoute) return '/login';
    if (isLoggedIn && isLoginRoute) return '/';
    return null;
  },
);

9) API & Network Layer (API ve Ağ Katmanı)

Client Abstraction (İstemci Soyutlaması)

typescript
// React Native — API Client with Axios
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';

class ApiClient {
  private client: AxiosInstance;

  constructor(baseURL: string) {
    this.client = axios.create({
      baseURL,
      timeout: 15000,
      headers: { 'Content-Type': 'application/json' },
    });

    this.setupInterceptors();
  }

  private setupInterceptors(): void {
    // Request interceptor — token ekleme (attach token)
    this.client.interceptors.request.use(async (config) => {
      const token = await SecureStore.getItemAsync('access_token');
      if (token) {
        config.headers.Authorization = `Bearer ${token}`;
      }
      return config;
    });

    // Response interceptor — token yenileme (refresh token)
    this.client.interceptors.response.use(
      (response) => response,
      async (error) => {
        const originalRequest = error.config;
        if (error.response?.status === 401 && !originalRequest._retry) {
          originalRequest._retry = true;
          try {
            const newToken = await this.refreshToken();
            originalRequest.headers.Authorization = `Bearer ${newToken}`;
            return this.client(originalRequest);
          } catch {
            // Oturumu sonlandır (Force logout)
            await this.forceLogout();
          }
        }
        return Promise.reject(error);
      }
    );
  }

  async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
    const response = await this.client.get<T>(url, config);
    return response.data;
  }

  async post<T>(url: string, data?: any): Promise<T> {
    const response = await this.client.post<T>(url, data);
    return response.data;
  }

  async put<T>(url: string, data?: any): Promise<T> {
    const response = await this.client.put<T>(url, data);
    return response.data;
  }

  async delete<T>(url: string): Promise<T> {
    const response = await this.client.delete<T>(url);
    return response.data;
  }

  private async refreshToken(): Promise<string> {
    const refreshToken = await SecureStore.getItemAsync('refresh_token');
    const response = await axios.post(`${this.client.defaults.baseURL}/auth/refresh`, {
      refresh_token: refreshToken,
    });
    const { access_token } = response.data;
    await SecureStore.setItemAsync('access_token', access_token);
    return access_token;
  }

  private async forceLogout(): Promise<void> {
    await SecureStore.deleteItemAsync('access_token');
    await SecureStore.deleteItemAsync('refresh_token');
    // Navigate to login
  }
}

Retry Stratejisi (Retry Strategy)

typescript
async function withRetry<T>(
  fn: () => Promise<T>,
  options: {
    maxRetries?: number;
    baseDelay?: number;
    maxDelay?: number;
    retryableStatuses?: number[];
  } = {}
): Promise<T> {
  const {
    maxRetries = 3,
    baseDelay = 1000,
    maxDelay = 10000,
    retryableStatuses = [408, 429, 500, 502, 503, 504],
  } = options;

  let lastError: any;
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error: any) {
      lastError = error;
      const status = error?.response?.status;
      const isRetryable = !status || retryableStatuses.includes(status);

      if (attempt === maxRetries || !isRetryable) throw error;

      // Exponential backoff with jitter
      const delay = Math.min(
        baseDelay * Math.pow(2, attempt) + Math.random() * 1000,
        maxDelay
      );
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
  throw lastError;
}

10) Responsive UI & Adaptif Tasarım (Responsive UI & Adaptive Design)

Ekran Boyutu Sınıflandırması (Screen Size Classification)

Kategori (Category)Genişlik (Width)Örnek Cihaz (Example Device)
Küçük Telefon (Small Phone)< 360 dpiPhone SE, eski Android
Normal Telefon (Phone)360-414 dpiPhone 14, Pixel 7
Büyük Telefon (Phablet)414-600 dpiPhone 14 Pro Max, Galaxy Ultra
Küçük Tablet (Small Tablet)600-840 dpiPad Mini, Galaxy Tab
Tablet840-1200 dpiPad Pro 11"
Büyük Tablet / Masaüstü> 1200 dpiPad Pro 12.9", Masaüstü

Responsive Tasarım Kuralları (Responsive Design Rules)

typescript
// React Native — Responsive utility
import { Dimensions, PixelRatio, Platform, StatusBar } from 'react-native';

const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');

// Referans tasarım boyutu (Reference design size)
const BASE_WIDTH = 375; // iPhone 14 genişliği
const BASE_HEIGHT = 812;

// Ölçekleme fonksiyonları (Scaling functions)
export const scale = (size: number): number =>
  (SCREEN_WIDTH / BASE_WIDTH) * size;

export const verticalScale = (size: number): number =>
  (SCREEN_HEIGHT / BASE_HEIGHT) * size;

export const moderateScale = (size: number, factor = 0.5): number =>
  size + (scale(size) - size) * factor;

// Cihaz tipi belirleme (Device type detection)
export const isTablet = (): boolean => {
  const ratio = PixelRatio.get();
  const adjustedWidth = SCREEN_WIDTH * ratio;
  return adjustedWidth >= 1000;
};

// Safe area değerleri (Safe area values)
export const getStatusBarHeight = (): number => {
  if (Platform.OS === 'ios') return 44;
  return StatusBar.currentHeight ?? 24;
};

Orientation Desteği (Orientation Support)

typescript
// React Native — Orientation hook
import { useWindowDimensions } from 'react-native';

function useOrientation() {
  const { width, height } = useWindowDimensions();
  const isPortrait = height >= width;
  const isLandscape = width > height;

  return {
    isPortrait,
    isLandscape,
    screenWidth: width,
    screenHeight: height,
    columns: isTablet() ? (isLandscape ? 4 : 3) : (isLandscape ? 3 : 2),
  };
}

Tablet Layout Stratejisi (Tablet Layout Strategy)

Telefon (Phone):              Tablet:
┌──────────────┐              ┌─────────┬──────────────┐
│    Liste     │              │  Liste  │    Detay     │
│   (List)     │   ──────>   │ (Master)│   (Detail)   │
│              │              │         │              │
│              │              │         │              │
│              │              │  1/3    │     2/3      │
└──────────────┘              └─────────┴──────────────┘

11) Accessibility — Erişilebilirlik

Erişilebilirlik, uygulamanızı engelli kullanıcılar dahil herkes için kullanılabilir kılmaktır. Hem etik bir sorumluluk hem de birçok ülkede yasal gerekliliktir.

Screen Reader Desteği (Screen Reader Support)

tsx
// React Native — Erişilebilir bileşen
<TouchableOpacity
  accessible={true}
  accessibilityLabel="Sepete ekle butonu"
  accessibilityHint="Bu ürünü alışveriş sepetinize ekler"
  accessibilityRole="button"
  accessibilityState={{ disabled: outOfStock }}
  onPress={handleAddToCart}
>
  <Text>Sepete Ekle</Text>
</TouchableOpacity>

// Canlı duyuru (Live announcement)
import { AccessibilityInfo } from 'react-native';
AccessibilityInfo.announceForAccessibility('Ürün sepete eklendi');
dart
// Flutter — Erişilebilir bileşen
Semantics(
  label: 'Sepete ekle butonu',
  hint: 'Bu ürünü alışveriş sepetinize ekler',
  button: true,
  enabled: !outOfStock,
  child: ElevatedButton(
    onPressed: outOfStock ? null : handleAddToCart,
    child: const Text('Sepete Ekle'),
  ),
)

Erişilebilirlik Kontrol Listesi (Accessibility Checklist)

Kriter (Criteria)Açıklama (Description)Minimum Standart
Renk Kontrastı (Color Contrast)Metin ile arka plan arasındaki kontrast oranıWCAG AA: 4.5:1 (normal metin), 3:1 (büyük metin)
Dokunma Alanı (Touch Target)Dokunulabilir öğelerin minimum boyutu44x44 pt (iOS), 48x48 dp (Android)
Büyük Font Desteği (Large Font)Sistem font boyutu ayarına uyumMetin 200%'e kadar ölçeklenmeli
Screen Reader EtiketleriTüm etkileşimli öğelerde accessibilityLabelHer buton, giriş, resim için zorunlu
Odak Sırası (Focus Order)Tab/swipe ile mantıksal gezinme sırasıSoldan sağa, yukarıdan aşağıya
Hareket Azaltma (Reduce Motion)Animasyonları devre dışı bırakma seçeneğiprefers-reduced-motion desteği
Renk Körlüğü (Color Blindness)Bilgiyi sadece renkle iletmemeİkon, metin veya desen eklenmeli

Test Araçları (Testing Tools)

Araç (Tool)PlatformAçıklama (Description)
Accessibility InspectoriOSXcode'da erişilebilirlik denetleyicisi
Accessibility ScannerAndroidGoogle'ın erişilebilirlik tarama aracı
VoiceOveriOSApple ekran okuyucu ile manuel test
TalkBackAndroidGoogle ekran okuyucu ile manuel test
axe / eslint-plugin-jsx-a11yRNOtomatik erişilebilirlik lintleme
Semantics DebuggerFluttershowSemanticsDebugger: true ile görsel denetim

12) App Size Optimization (Uygulama Boyutu Optimizasyonu)

Android Optimizasyonları

Teknik (Technique)Açıklama (Description)Kazanım (Savings)
AAB KullanımıAPK yerine App Bundle — cihaza özel dağıtım%15-25 daha küçük
ProGuard / R8Kullanılmayan kod ve sınıfları temizleme%10-30 kod küçültme
Resource ShrinkingKullanılmayan kaynakları (resim, string) temizlemeDeğişken
WebP FormatıPNG/JPEG yerine WebP kullanımı%25-35 daha küçük resimler
Dynamic Feature Modulesİsteğe bağlı modülleri gerektiğinde indirmeİlk yükleme boyutu azalır

React Native Özel Optimizasyonlar

Teknik (Technique)Açıklama (Description)
Hermes EngineBytecode derleme ile daha küçük bundle ve hızlı başlatma
RAM BundlesSadece gerekli modülleri yükleme (lazy loading)
Asset SıkıştırmaResimleri derleme öncesi optimize etme (sharp, imagemin)
Kullanılmayan Import'ları TemizlemeTree shaking ve dead code elimination

Flutter Özel Optimizasyonlar

Teknik (Technique)Açıklama (Description)
--split-debug-infoDebug sembollerini ayırma — APK boyutunu azaltır
--obfuscateKod obfuscation ile boyut küçültme
Deferred ComponentsBileşenleri gerektiğinde indirme
--tree-shake-iconsKullanılmayan Material ikonlarını temizleme

iOS Optimizasyonları (App Thinning)

Teknik (Technique)Açıklama (Description)
App SlicingCihaza özel kaynak dağıtımı (1x, 2x, 3x)
BitcodeApple sunucularında yeniden optimizasyon
On-Demand ResourcesKaynakları gerektiğinde indirme (oyun seviyeleri vb.)

13) Güvenlik Mimarisi (Security Architecture)

Token Saklama (Token Storage)

Yöntem (Method)PlatformGüvenlik SeviyesiAçıklama
KeychainiOSYüksekDonanım şifrelemeli güvenli depo
Keystore / EncryptedSharedPreferencesAndroidYüksekDonanım destekli şifreleme
expo-secure-storeRN (Expo)YüksekKeychain/Keystore wrapper
react-native-keychainRN (CLI)YüksekNative Keychain/Keystore erişimi
flutter_secure_storageFlutterYüksekKeychain/Keystore wrapper
AsyncStorage / SharedPreferencesRN / FlutterDüşükŞifrelenmemiş — token için KULLANMAYIN

Token Saklama Kuralı (Token Storage Rule)

Access token ve refresh token asla AsyncStorage, SharedPreferences veya MMKV gibi şifrelenmemiş depolarda saklanmamalıdır. Her zaman platform'un güvenli depolama mekanizmasını kullanın.

Certificate Pinning (Sertifika Sabitleme)

typescript
// React Native — SSL Pinning (react-native-ssl-pinning)
import { fetch } from 'react-native-ssl-pinning';

const response = await fetch('https://api.example.com/data', {
  method: 'GET',
  sslPinning: {
    certs: ['my_server_cert'], // .cer dosyası assets içinde
  },
  headers: {
    Authorization: `Bearer ${token}`,
  },
});
dart
// Flutter — Certificate Pinning (Dio + dio_http2_adapter)
final dio = Dio();
(dio.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () {
  final client = HttpClient();
  client.badCertificateCallback = (cert, host, port) {
    // Sertifika parmak izini doğrula (Verify certificate fingerprint)
    final expectedFingerprint = 'AB:CD:EF:...';
    final certFingerprint = sha256(cert.der);
    return certFingerprint == expectedFingerprint;
  };
  return client;
};

Biyometrik Kimlik Doğrulama (Biometric Authentication)

typescript
// React Native — expo-local-authentication
import * as LocalAuthentication from 'expo-local-authentication';

async function authenticateWithBiometrics(): Promise<boolean> {
  // 1. Donanım desteğini kontrol et (Check hardware support)
  const hasHardware = await LocalAuthentication.hasHardwareAsync();
  if (!hasHardware) return false;

  // 2. Kayıtlı biyometri var mı? (Is biometric enrolled?)
  const isEnrolled = await LocalAuthentication.isEnrolledAsync();
  if (!isEnrolled) return false;

  // 3. Kimlik doğrulama (Authenticate)
  const result = await LocalAuthentication.authenticateAsync({
    promptMessage: 'Kimliğinizi doğrulayın',
    cancelLabel: 'İptal',
    fallbackLabel: 'PIN Kullan',
    disableDeviceFallback: false,
  });

  return result.success;
}

Root/Jailbreak Tespiti (Root/Jailbreak Detection)

Kontrol (Check)iOS (Jailbreak)Android (Root)
Dosya Varlığı/Applications/Cydia.app, /private/var/stash/system/app/Superuser.apk, /sbin/su
Yazma TestiSistem dizinine yazma denemesi/system dizinine yazma denemesi
Kütüphane KontrolüMobileSubstrate varlığısu binary erişimi
Paket Yöneticisiapt, dpkg varlığıMagisk, SuperSU varlığı
KütüphaneIOSSecuritySuiterootbeer, RNRootDetection

Kod Obfuscation (Kod Karartma)

groovy
// Android — build.gradle (ProGuard/R8)
android {
    buildTypes {
        release {
            minifyEnabled true       // Kod küçültme (Code shrinking)
            shrinkResources true     // Kaynak küçültme (Resource shrinking)
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                          'proguard-rules.pro'
        }
    }
}
bash
# Flutter — Obfuscation ile build
flutter build apk \
  --obfuscate \
  --split-debug-info=build/debug-info \
  --tree-shake-icons

Güvenlik Kontrol Listesi (Security Checklist)

Kontrol (Check)Açıklama (Description)Öncelik (Priority)
Güvenli Token SaklamaKeychain / Keystore kullanımıKritik
Certificate PinningAPI iletişiminde sertifika sabitlemeYüksek
Kod ObfuscationRelease build'de kod karartmaYüksek
Root/Jailbreak TespitiCihaz güvenlik kontrolüOrta
Biyometrik AuthHassas işlemler için biyometrik doğrulamaOrta
Network Security ConfigCleartext traffic engelleme (HTTP yasak)Yüksek
Clipboard TemizlemeHassas verileri clipboard'dan silmeOrta
Screenshot EngellemeHassas ekranlarda ekran görüntüsü engeliOrta
Debug TespitiDebugger bağlantısını tespit etmeDüşük
Secure LoggingProduction'da hassas verileri loglamamakYüksek

Developer Guides & Technical References