Kimsenin Üzerine Almak İstemediği Sorun
Kimsenin Üzerine Almak İstemediği Sorun
2024 sonlarında AB’nin NIS2 direktifi yürürlüğe girmeye başladı. Kendini “kritik altyapı” olarak düşünmeyen binlerce orta ölçekli Avrupa şirketi birdenbire kapsamda yer aldığını gördü: üreticiler, lojistik sağlayıcıları, MSP’ler, orta ölçekli SaaS sağlayıcıları. Direktif oldukça geniş, ulusal geçişleri farklılık gösteriyor, ancak pratikteki soru her yerde aynı: nereden başlamalıyız?
Eğer 60 kişilik bir Belçika üreticisi iseniz, muhtemelen bir CISO’ya veya belirli bir güvenlik analizine sahip değilsiniz. Şu an bir IT yöneticisi üç farklı görev yürütmekte ve şimdi bir risk değerlendirmesi, bir olay yanıt planı ve yönetim raporlama çerçevesi sunması gerekiyor — bazı Üye Devletlerde geçtiğimiz süreye karşı.
Büyük danışmanlık firmaları size altı basamaklı bir danışmanlık süreci sunmayı seve seve yaparlar. Büyük GRC platformları (OneTrust, ServiceNow IRM ve benzerleri) KOBİ’ler için erişilemez derecede yüksek fiyatlarla satılmakta. “Pahalı bir danışman” ve “sorunu görmezden gel ve denetlenmeyi um” arasında bir boşluk var. Bu boşlukta son birkaç aydır çalıştığım projeyi oluşturuyorum.
Neden Laravel 12 + Livewire 3 (ve kabul etmediğim alternatifler)
Neden Laravel 12 + Livewire 3 (ve kabul etmediğim alternatifler)
Uzun zamandır Laravel geliştiricisiyim. Grazulex ismi altında birkaç açık kaynak paketi sürdürüyorum, bu yüzden framework seçimi üzerine çok fazla düşünmeye ihtiyacım yoktu — ama NIS2 gibi düzenlemelere tabi bir B2B SaaS için Laravel 12 + Livewire 3’ün neden uygun olduğunu açıklamak istiyorum.
Stack:
- Laravel 12 — backend. Eloquent, kuyruklar, politikalar, yayım, her şey.
- Livewire 3 — sunucu üzerinde render edilen tepkisellik. No SPA, no ayrı API, no client-side state.
- Filament 3 — dahili yönetim paneli. Kiracılar, denetimler, destek talepleri, faturalama.
- Tailwind CSS — stil önerimi oluşturmak için.
- MySQL 8 — ana veri deposu. Postgres de güzel olurdu; barındırma varsayımları MySQL’ye yönlendirdi.
- Laravel Horizon — asenkron işler için panel (PDF oluşturma, zamanlanmış değerlendirmeler, e-posta).
Ciddi şekilde değerlendirdiğim ve vazgeçtiğim iki stack:
- Laravel + Inertia + Vue. Inertia’yı seviyorum ama Vue bileşenlerine geçtiğiniz anda yapı aracına, tip tanımlarına ve paralel bir zihinsel modele ihtiyaç duyuyorsunuz. Hızlı bir şekilde gönderim yapmaya çalışan solo geliştirici için, bu hiçbir fayda sağlamayan bir yüktür.
- Next.js + Supabase. Moda olan, hızlı prototip oluşturma olanağı sunan, ancak uyum çalışmaları için acı veren bir çözüm. Postgres’deki satır düzeyinde güvenlik en iyi seçenek olabilir — ama bu, alan düzeyinde fark geçmişine ve öngörülebilir, framework-yerel yetkilendirmelere ihtiyaç duyduğunuzda, sıkıntılı hale geliyor. Laravel’in politika + Eloquent gözlemci ekosistemini istedim.
2026’da Livewire 3 gerçekten iyi. wire:model iyileştirmeleri, morph tabanlı DOM farklılıkları ve Alpine.js entegrasyonu, birden fazla aşamalı risk değerlendirme sihirbazı oluşturmama olanak tanıyor; şartlı alanlar, doğrulama ve canlı ilerleme göstergeleri ile — bir satır özel JavaScript yazmadan.
Söylemeye Değer Dört Teknik Karar
Söylemeye Değer Dört Teknik Karar
1. Durum makineleri, sabit kodlanmış — yapılandırılamaz
1. Durum makineleri, sabit kodlanmış — yapılandırılamaz
nis2you kapsamındaki her uyum nesnesi (değerlendirmeler, olaylar, denetimler, eylem planları) tanımlanmış bir yaşam döngüsünden geçiyor: draft → in_review → validated → published → archived. Başlangıçta, her kiracı için bunu yapılandırılabilir hale getirme konusunda hevesliydim. “Bazı müşteriler farklı bir iş akışını tercih edebilir.”
Bunu yapmadım ve yaptığım için mutluyum. Yapılandırılabilir iş akışları, bir iş akışı motorunu gerektiriyor, bu da YAML dosyaları, onları düzenlemeniz için bir UI ve birisi YAML’i bozduğunda destek biletleri demek. Tek geliştirici SaaS için, düz PHP’de geçiş bekçeleri ile sabit kodlanmış durumlar oluşturmak çok daha basit:
final class AssessmentState
{
public const DRAFT = 'draft';
public const IN_REVIEW = 'in_review';
public const VALIDATED = ;
public const PUBLISHED = ;
public const ARCHIVED = ;
public static function canTransition(string $from, string $to): bool
{
return match ($from) {
self::DRAFT => in_array($to, [self::IN_REVIEW, self::ARCHIVED]),
self::IN_REVIEW => in_array($to, [self::DRAFT, self::VALIDATED]),
self::VALIDATED => in_array($to, [self::PUBLISHED, self::IN_REVIEW]),
self::PUBLISHED => $to === self::ARCHIVED,
self::ARCHIVED => false,
default => false,
};
}
}
Bir Eloquent gözlemci ile yasak geçişlerde hata oluşturarak birleştirdiğinizde, bu 2,000 satırlık bir iş akışı motorunun yerine geçen yaklaşık 80 satırlık kod. Eğer bir kiracı gerçekten farklı bir iş akışına ihtiyaç duyarsa, bu, bir yapılandırma ekranından ziyade bire bir bir konuşmadır.
2. PDF Oluşturma: DomPDF üzerinden, Horizon ile Kuşaklanan
2. PDF Oluşturma: DomPDF üzerinden, Horizon ile Kuşaklanan
NIS2 teslimatları belgelerden oluşur. Risk değerlendirmeleri, olay raporları, tedarikçi envanterleri — bir noktada, kiminse imzaladığı bir PDF gereklidir. PHP’de PDF oluşturma yavaş ve bellek tüketicidir. Inline yapmak isteyerek istekleri etkiler.
Kurulum: Bir Blade şablonu belgeyi render eder, DomPDF bunu PDF’ye dönüştürür, her şey bir kuyruklu iş olarak çalışır ve Livewire tamamlanmayı dinler:
final class GenerateAssessmentPdf implements ShouldQueue
{
public function __construct(public Assessment $assessment) {}
public function handle(): void
{
$html = view'pdf.assessment', [
=> $this->assessment,
])->render);
$pdf = Pdf::loadHTML$html)->setPaper'a4');
Storage::disk's3')->put"tenants/$this->assessment->tenant_id/assessments/$this->assessment->id.pdf",
$pdf->output);
$this->assessment->update'pdf_generated_at' => now)]);
AssessmentPdfReady::dispatch$this->assessment);
}
}
Horizon bana yeniden denemeler, hata takibi ve bir gerçek zamanlı panel sağlar. Livewire bileşeni AssessmentPdfReady‘yi dinliyor ve kullanıcı arayüzünü yeniden yüklemeden güncelliyor. Kullanıcılar “ürün hazırlanıyor…” → “hazır, indirmek için tıklayın” mesajını çok uzun süreli bir HTTP isteği tutmadan görebiliyor.
3. Denetim İzleme: Spatie ActivityLog ile — Alan Düzeyinde Farklar İçin Uzatılmış
3. Denetim İzleme: Spatie ActivityLog ile — Alan Düzeyinde Farklar İçin Uzatılmış
Varsayılan spatie/laravel-activitylog paketi, model başına oluşturma/güncelleme/silme işlemlerini günlüğe kaydediyor. NIS2 için bu yeterli değil. Denetçilerin, hangi alanın değiştiğini, kimin değiştirdiğini ve önceki değerin ne olduğunu bilmesi gerekiyor. İki yıllık saklama, pratik bir minimumdur.
Bunu, her denetlenebilir model üzerinde uygulanacak küçük bir Auditable trait ile uzattım:
trait Auditable
{
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->logFillable()
->logOnlyDirty()
->dontSubmitEmptyLogs()
->setDescriptionForEvent(
fn (string $event) => "{$this->getMorphClass()}.{$event}"
);
}
public function tapActivity(Activity $activity, string $event): void
{
$activity->properties = $activity->properties->merge([
=> $this->tenant_id ?? null,
=> request->ip),
=> auth->id),
]);
}
}
Etkinlik günlüğü, her ay bölümlere ayrılan bir tabloda saklanıyor. On iki aydan daha eski bölümler arşivleniyor. Bu, uygulamadaki en yüksek ROI’ye sahip parçaydı — denetçiler bunu çok seviyor ve bu, beta sürecinde “kullanıcı bunu gerçekten onayladı mı?” sorusuna cevap verdi.
4. Çoklu Kiracı: Şimdilik Kolon Tabanlı, V1.5 Yol Haritasında Şemaya Göre Kiracı
4. Çoklu Kiracı: Şimdilik Kolon Tabanlı, V1.5 Yol Haritasında Şemaya Göre Kiracı
Her kiracıya bağlı model, bir tenant_id taşır. Global kapsamlar otomatik olarak doğrulanmış oturuma bağlı olarak uygulanır. En basit mümkün model ve mevcut ölçekte mükemmel çalışıyor.
V1’de kesinlikle şema başına göndermedim. Bunun lehine argümanlar (sert veri izolasyonu, daha kolay müşteri bazında yedeklemeler) gerçek fakat önceden gelir. Düzenlenmiş bir müşteri nihayet gerçek şema izolasyonu talep ettiğinde, V1.5 bunu stancl/tenancy aracılığıyla tanıtacak. O zamana kadar kolon kapsamı ve her politikada kapsamlı Pest testleri yeterlidir — bu da operasyonel hikayeyi basit tutar.
Üç Dürüst Ders
Üç Dürüst Ders
1. Filament bana en az dört hafta kazandırdı. Özel bir yönetim panelleri oluşturmayı düşündüm “çünkü Filament sınırlayıcı olabilir.” Hayır, değil. Kiracıları, destek taleplerini ve denetim incelemelerini yönetmek için iç panel bir hafta sonu sürdü. Özel bir yapı büyük ihtimalle Şubat ayının tamamını alırdı.
2. Livewire’ın zorlukları hala gerçek. Özellikle: wire:model üzerinden bağlanan büyük diziler, belirgin bir hidratasyon maliyeti oluşturuyor. Karmaşık iç içe yapıların düzleştirilmesini ve ağır durumu sunucuya taşıma yöntemini uyguladım. “drop-in reactivity” vaadi bir sınır taşır — bu sınır nerede olduğunu bilmek gerekiyor bir kullanıcı şikayet etmeden önce.
3. Uyum UX’i kendi disiplini. Geliştiriciler formları sever; uyum memurları onlara tahammül eder. Tek büyük UX yatırımı, kullanıcıyı adım adım, bağlam, örnekler ve açık şimdilik atla butonu ile yönlendiren bir “rehberli değerlendirme” moduydu. Tamamlanma oranı onu yayınladığım hafta üç kat arttı. Ders: düzenlenmiş B2B’de form üründür.
Kapanış
Kapanış
nis2you, birkaç Belçika KOBİ’si ve danışmanları ile özel beta aşamasındadır. Stack — Laravel 12, Livewire 3, Filament, Horizon — gösterişli olmaktan uzak, üretken ve işletimi ucuz. Eğer yalnız bir geliştirici olarak düzenlenmiş bir B2B SaaS gönderiyorsanız, bunu şiddetle öneririm.
Gerçekten bu alanda hangi stack’i seçerdiniz merak ediyorum — farklı yaklaşımlarınızı yorum kısmına bırakabilirsiniz. Eğer NIS2 müşterilerinizin gündemindeyse, nis2you.com‘da işler yaşanıyor.
Jean-Marc Strauven — Belçikalı Laravel geliştirici, Grazulex paketlerini sürdürücü.
Kaynak: Orijinal Makale
- Kimsenin Üzerine Almak İstemediği Sorun
- Neden Laravel 12 + Livewire 3 (ve kabul etmediğim alternatifler)
- Söylemeye Değer Dört Teknik Karar
- 1. Durum makineleri, sabit kodlanmış — yapılandırılamaz
- 2. PDF Oluşturma: DomPDF üzerinden, Horizon ile Kuşaklanan
- 3. Denetim İzleme: Spatie ActivityLog ile — Alan Düzeyinde Farklar İçin Uzatılmış
- 4. Çoklu Kiracı: Şimdilik Kolon Tabanlı, V1.5 Yol Haritasında Şemaya Göre Kiracı
- Üç Dürüst Ders
- Kapanış


