Bir özelliği fikirden üç platforma: uçtan uca akışım
Bir özelliği API, web ve mobilde aynı anda teslim ettiğimde izlediğim olgunlaşmış, tekrarlanabilir süreci anlatıyorum.
Bir özelliği üç platformda birden teslim etmek — Laravel API, React web arayüzü ve React Native mobil uygulama — her seferinde ilk kez yapılan bir iş gibi görünmez artık. Bir ritim oluştu. Bu ritmi ilk kez bilinçli olarak yazmaya çalışıyorum; hem kendi referansım hem de benzer pozisyondaki başkalarına belki bir iz bırakır diye.
Süreç zaman içinde olgunlaştı. Erken dönemde her özellik biraz kaotikti: hangi uçta başlamalıyım, ne kadar detaylı tanımlamalıyım, ne zaman koda geçmeliyim soruları net değildi. Şimdi bunların cevapları neredeyse otomatik.
Başlangıç noktası: sözleşme
Her yeni özellik için ilk adım API contract’ını yazmak. Kod yazmadan önce. Bir metin editöründe, bazen gerçek bir OpenAPI taslağında, bazen sadece birkaç paragraf notla:
- Bu özellik hangi resource üzerinde çalışıyor?
- Hangi endpoint’ler gerekiyor?
- İstek ve yanıt gövdeleri nasıl görünüyor?
- Hata durumları neler, HTTP durum kodları ne olacak?
Bu adımı atlamak cazipor — “küçük bir özellik, hemen yazayım” dürtüsü her zaman var. Ama atladığımda web ve mobil taraf API’nin şekline sonradan uyum sağlamaya çalışıyor ve bu sürtünme pahalıya geliyor.
Laravel tarafı: önce API
Sözleşme netleşince Laravel’de API katmanına geçiyorum. Temel akış:
Migration ve model. Yeni bir veri yapısı gerekiyorsa önce migration, ardından model. Relation’ları baştan doğru kurmak, ilerleyen aşamalarda düzeltme maliyetini düşürüyor.
Form Request ile doğrulama. Doğrulama mantığını controller’da değil, ayrı bir Form Request sınıfında tutuyorum. Bu hem test edilebilirlik hem de controller’ı ince tutmak için:
class StoreEventRequest extends FormRequest
{
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'starts_at' => ['required', 'date', 'after:now'],
'ends_at' => ['required', 'date', 'after:starts_at'],
];
}
}
Action sınıfı. Business logic’i controller’dan ayırıp bir Action sınıfına taşımak, aynı mantığı birden fazla giriş noktasından (web controller, API controller, artisan komutu) çağırmayı mümkün kılıyor.
API Resource. Model verisini doğrudan döndürmek yerine bir Resource sınıfı üzerinden biçimlendirmek. Hem field exposure’ı kontrol ediyor hem de yanıt yapısını API contract’ına kilitlemiş oluyor.
class EventResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'starts_at' => $this->starts_at->toIso8601String(),
'ends_at' => $this->ends_at->toIso8601String(),
];
}
}
TypeScript tipleri: köprü
API sözleşmesi netleşince web ve mobil taraf için TypeScript tip tanımlarını yazıyorum. Bu tanımlar hem web hem React Native projesinde aynı:
interface Event {
id: number;
title: string;
starts_at: string;
ends_at: string;
}
interface CreateEventPayload {
title: string;
starts_at: string;
ends_at: string;
}
Bu tipleri ortak bir pakette ya da monorepo yapısında paylaşmak ideal; yoksa her iki projede ayrı tutuyorum ama kaynak olarak API sözleşmesini referans alıyorum.
Web ve mobil: paralel, bağımsız
API çalışır hale gelince web ve mobil tarafı paralel ilerletebiliyorum. İkisi aynı API’yi tüketiyor ama arayüz kararları bağımsız.
Web tarafında useQuery ile veri çekme, form için useActionState (React 19 ile birlikte) standart hale geldi.
Mobil tarafta FlatList, navigasyon geçişleri ve platform bazlı davranış farklılıkları (iOS ve Android) ayrıca ele alınıyor. Bu farkları en baştan bekliyorum, sürpriz olarak karşılamıyorum.
Test ve onay
Bir özelliği tamamlanmış saydığım an: API’de unit/feature test var, web arayüzünde temel akış çalışıyor, mobilde iOS ve Android fiziksel cihazda onaylandı. Bu üç koşul sağlanmadan özellik “bitti” değil.
Bu ritmin asıl değeri hız değil. Bir sorunun nerede çıktığını hızlıca izole edebilmek — API’de mi, web’de mi, mobilde mi — ve her katmanda bağımsız hareket edebilmek. Tekrarlanabilirlik bunu mümkün kılıyor.