Tek Laravel projesinde üç arayüz: site, panel ve müşteri portalı
Bir firma "bize bir web sitesi lazım" dediğinde çoğu zaman tek bir şeyden bahsetmiyor. Sardur Mühendislik projesinde ihtiyaç üç parçaya ayrılıyordu: herkesin göreceği bir kurumsal site, firmanın içeriği ve kayıtları yöneteceği bir panel, müşterilerin kendi bakım geçmişlerine, temizlik sonrası görsel ve videolarına ve raporlarına ulaşacağı bir portal.
İlk refleksim bunları ayrı uygulamalar olarak kurmaktı. Sonunda üçünü tek bir Laravel projesinde topladım; proje bugün yayında ve bu kararın doğru olduğunu düşünüyorum.
Neden tek proje?
Üç ayrı uygulama; üç ayrı dağıtım, üç ayrı bağımlılık listesi ve aralarında veri taşımak için bir API demek. Bu ayrım büyük ekiplerde anlamlı olabilir; tek kişilik bir projede ise çoğunlukla yalnızca maliyet.
Asıl belirleyici olan şuydu: üçü de aynı veriyi kullanıyor. Panelde düzenlenen bir hizmet açıklaması kurumsal sitede görünüyor; panelde oluşturulan bir bakım kaydı ve ona eklenen rapor, portalda müşterinin karşısına çıkıyor. Ortada paylaşılması gereken tek bir alan modeli var ve onu iki kez yazmak istemedim.
Ayrımı rotalarda kurmak
Tek proje, her şeyin birbirine karıştığı anlamına gelmiyor. Ayrım rota grupları ve ara katmanlarla net biçimde kuruluyor. Aşağıdaki örnek sadeleştirilmiş bir gösterim:
// Herkese açık kurumsal site
Route::get('/hizmetler', [ServiceController::class, 'index'])->name('services');
// Müşteri portalı — yalnızca giriş yapmış müşteriler
Route::middleware(['auth', 'role:customer'])
->prefix('portal')
->name('portal.')
->group(function () {
Route::get('/bakimlar', [MaintenanceController::class, 'index'])->name('maintenance');
});
Yönetim panelini elle yazmadım; Filament kullandım. Bu tek karar projenin süresini belirgin şekilde kısalttı: listeleme ve düzenleme ekranları, dosya yükleme, yetkilendirme ve arama hazır geliyor. Böylece tasarım emeğini müşterinin göreceği tarafa, yani kurumsal siteye ve portala ayırabildim.
Aynı veri, farklı sorular
Portal ile panelin kesiştiği yer projenin en ilginç kısmıydı. İkisi de aynı bakım kayıtlarını gösteriyor ama farklı sorulara cevap veriyor:
- Panel: "Hangi müşteride, hangi tesiste, ne zaman bakım yapıldı?"
- Portal: "Benim tesisimde en son ne zaman bakım yapıldı, nasıl göründü, raporu nerede?"
Bunu iki ayrı model yazarak değil, yetkilendirme ve sorgu kapsamıyla çözdüm. Müşteri kullanıcısının sorguları, modele tanımlı bir kapsamla otomatik olarak kendi kayıtlarına sınırlanıyor (yine sadeleştirilmiş):
public function scopeVisibleTo(Builder $query, User $user): Builder
{
return $user->isStaff()
? $query
: $query->where('customer_id', $user->customer_id);
}
Kapsamı modele koymak, "bir yerde filtrelemeyi unutmak" hatasını baştan imkânsız hâle getiriyor.
Raporlar: dosya yükleme bir güvenlik kararıdır
Bakım raporları PDF olarak panele yükleniyor ve müşteri portaldan indiriyor. Burada dosya yüklemeyi basit bir form alanı gibi değil, bir erişim kararı olarak ele almak gerekiyor:
- Dosyalar herkese açık klasörde değil, doğrudan adresle ulaşılamayan bir depoda tutuluyor.
- İndirme isteği her seferinde sunucu tarafında kontrol ediliyor: rapor, isteyen müşterinin kendi kaydına mı ait?
- Boyut ve tür sınırları yükleme anında uygulanıyor.
Depolama, sınırlar ve erişim izni birbirinden bağımsız üç ayrıntı değil; birlikte tasarlanması gereken tek bir karar.
Tek tıkla bilgilendirme
Projeye sonradan eklenen ve müşteri tarafında en çok fark yaratan özellik bu oldu. Temizlik bittikten sonra firma sahibi sahadan çektiği görsel ve videoları panelde ilgili kayda yüklüyor, ardından tek bir düğmeyle müşteriye bilgilendirme e-postası gönderiyor. Müşteri de yapılan işi portalda görsel kanıtıyla birlikte inceleyebiliyor.
Burada önemli bulduğum karar, gönderimin otomatik tetiklenmemesi: e-posta ancak firma sahibi içeriği yükleyip "hazır" dediğinde gidiyor. Böylece müşteriye yarım ya da eksik bir kayıt hiçbir zaman ulaşmıyor; zamanlama tamamen işi yapan kişinin kontrolünde kalıyor. Teknik tarafta ise e-posta, portalın kullandığı aynı yetkilendirme kurallarına dayanıyor: bildirim yalnızca kaydın sahibi olan müşteriye gidiyor.
Üç ilke
Bu tür projelerde işe yarayan, bir sonraki projede de baştan uygulayacağım ilkeler:
- Yetkilendirme görünümde yapılmaz. Menüde bir bağlantıyı gizlemek erişim kontrolü değildir; adresi bilen herkes sayfaya ulaşır. Kontrol rotada ve sorguda olmalı.
- Değişebilecek bilgi kodda durmaz. Firma adı, iletişim bilgileri, çalışma saatleri gibi içerikler veritabanında tutulup panelden düzenlenebilir olmalı. Böylece firma, küçük değişiklikler için geliştiriciye bağımlı kalmaz.
- Dosya erişimi ilk günden tasarlanır. Rapor gibi müşteriye özel belgeler varsa, depolama ve indirme yetkisi projenin başında kurgulanmalı.
Sonuç
Tek projede üç arayüz, tek kişilik bir ekip için doğru ölçek. Sınırı klasör yapısıyla değil; rota grupları, ara katmanlar ve sorgu kapsamlarıyla çizdiğiniz sürece arayüzler birbirine karışmıyor ve ortak veri modeli projenin en güçlü yanı hâline geliyor.