Analitik araçlar genellikle iki tuzağa düşmektedir: ya yeterince yüzeysel olup kullanışsızdır ya da entegre edilmesi son derece karmaşık hale gelmektedir. Ben her iki durumdan da bıktım ve kendi analitik aracımı geliştirmeye karar verdim.
Tracetics – geliştiriciler için bir funnel analitik motoru oluşturma hikayem ve bu süreçteki teknik kararlarımı paylaşacağım.
Temel Problem
Temel Problem
Funnel analitiği basit görünse de: bir kullanıcı A, sonra B, sonra C yapar. A’dan C’ye geçenlerin yüzdesi nedir? Nerelerde kaybediyoruz? Pratikte, bunu iyi bir şekilde oluşturmak beklediğinizden daha karmaşık:
- Etkinlikler asenkron ve sıraya göre gelmekte;
- Funnel’ların esnek olması gerek – farklı adımlar, farklı zaman dilimleri;
- Hesaplamaların hızlı olması gerekli, binlerce etkinlik bile olsa;
- Geliştirici için entegrasyon aşaması minimal olmalı.
Hedefim: bir geliştiricinin tek bir HTTP POST ile 5 dakikadan kısa sürede izlemeye başlayabilmesi.
Mimari Genel Bakış
Mimari Genel Bakış
Browser/App → REST API → Event Storage → Queue → Funnel Engine → Dashboard
Yığın:
- Backend: Laravel 12, modüler mimari (nwidart/laravel-modules)
- Queue: Laravel Horizon + Redis
- Frontend: Next.js 14 + TypeScript
- Database: MariaDB
- Payments: Stripe
API Katmanı
API Katmanı
Entegrasyon kasıtlı olarak minimal:
POST /api/v1/events
X-OL-Tenant-Key: your-tenant-key
X-OL-App-Key: your-app-key
Content-Type: application/json
{
"event_name": "signup_completed",
"user_identifier": "user_123",
"metadata": {
"plan": "pro",
"source": "landing_page"
}
}
İki başlık, bir JSON gövdesi. Bu, tüm sözleşmedir.
Controller, anahtarları doğrular, kiracıyı ve izlenen uygulamayı tanımlar, etkinliği kalıcı hale getirir ve hemen 200 yanıtını döner. İstek döngüsünde ağır bir yük yoktur.
Funnel Motoru
Funnel Motoru
Burada işler ilginçleşiyor.
Bir kullanıcı, dashboard’da bir funnel oluşturduğunda – örneğin “visited_pricing → started_trial → upgraded” – sistemin her adım için dönüşüm oranlarını hesaplaması gerekmekte.
Bunu istek döngüsünden tamamen çıkarmayı tercih ettim. Her etkinlik yazma işlemi arka planda bir işi tetikler:
ProcessFunnelEngineJob::dispatch($event)->onQueue('funnels');
İş, etkinliği alır, o uygulama için tüm funnel’ları yükler ve kaydırmalı pencere yaklaşımı kullanarak adım başına dönüşüm oranlarını tekrar hesaplar. Sonuçlar önbelleğe alınır ve dashboard’a sunulur.
Neden bir kuyruk? İki sebep:
- Performans — API yanıtı, kaç tane funnelın yeniden hesaplanması gerektiğinden bağımsız olarak hızlı kalır.
- Dayanıklılık — Başarısız işler otomatik olarak tekrar denenir, geçici hatalarda veri kaybı olmaz.
Laravel Horizon, bize ek altyapıya ihtiyaç duymadan iş başarı oranını, başarısız işleri ve kuyruk derinliğini gerçek zamanlı olarak izleme imkanı sunar.
Çoklu Kiracı
Çoklu Kiracı
Tracetics, tasarım gereği çoklu kiracıdır. Her kiracı (şirket) kendi etkinlikleri ve funnel’ları olan birden fazla TrackedApp’a sahip olabilir. Anahtar hiyerarşisi şu şekildedir:
Tenant
└── TrackedApp (X-OL-App-Key)
└── Events
└── Funnels
└── FunnelSteps
API düzeyindeki kimlik doğrulama iki başlık kullanarak yapılır:
-
X-OL-Tenant-Key— kiracıyı tanımlar -
X-OL-App-Key— etkinliğin ait olduğu uygulamayı tanımlar
Bu, geliştirici tarafında entegrasyonu temiz tutarken, arka uçta sıkı veri izolasyonu sağlar.
Plan Sınırlamaları & Faturalama
Plan Sınırlamaları & Faturalama
Her plan, TrackedApps, Funnels ve aylık Events için sınırlamalar tanımlar. Bunlar, herhangi bir yazma işleminden önce controller düzeyinde uygulanır:
if ($tenant->trackedApps()->count() >= $tenant->plan->app_limit) {
return response()->json(['error' => 'App limit reached'], 403);
}
Stripe, abonelikleri webhooklar aracılığıyla yönetir. Buradaki ilginç zorluk: Stripe’ın daha yeni API’si current_period_end‘i items.data[0]‘ya taşıdı, bu da abone durumu için yalnızca güvenilir bir kaynak olarak webhooklara dayanmam gerektiğinin bir hatırlatıcısı oldu.
TypeScript SDK
TypeScript SDK
Hammaddeleri tercih eden geliştiriciler için sonra TypeScript SDK’sını yayınladım:
npm install tracetics-sdk
import { Tracetics } from 'tracetics-sdk';
const client = new Tracetics({
tenantKey: 'your-tenant-key',
trackedAppKey: 'your-app-key',
endpoint: 'https://tracetics.com'
});
await client.track({
event_name: 'signup_completed',
user_identifier: 'user_123'
});
SDK, tsup ile oluşturulmuştur – çift ESM/CJS çıktısı, tam TypeScript türleri, sıfır bağımlılık.
Öğrendiklerim
Öğrendiklerim
1. Yazma yolunu basit ve hızlı tutun.
API, mümkün olduğunca az şey yapmalıdır. Doğrula, kalıcı hale getir, sıraya al. Diğer her şeyin kuyrukla ilgisi olabilir.
2. Öncelikli kuyruk mimarisi hemen fayda sağlıyor.
Yavaş funnel hesaplamalarının API yanıtlarını engelleme konusunda endişelenmeme gerek yoktu. Sorumluluk ayrımı her iki tarafı da daha kolay anlaşılır ve hata ayıklanabilir hale getirdi.
3. Stripe webhookları zorunludur.
İlk başta yalnızca yönlendirme tabanlı onaylara güvenme hatasını yaptım. Webhooklar, abonelik durumu için tek güvenilir gerçek kaynaktır.
4. Çoklu kiracılık, net bir anahtar hiyerarşisi ile daha kolaydır.
Açık Tenant → Uygulama → Etkinlik sahipliği; izin kontrolünü basit yaptı ve veri izolasyonu sağlamlaştırdı.
Sırada Ne Var
Sırada Ne Var
Tracetics, tracetics.com adresinde canlı olarak mevcuttur ve ücretsiz bir plan bulunmaktadır. TypeScript SDK’sı npm’de tracetics-sdk olarak yayınlanmıştır.
Eğer benzer bir şey yaptıysanız veya mimarlık hakkında sorularınız varsa, yorumlarınızı duymaktan memnuniyet duyarım.
Kaynak: Orijinal Makale


