Achidemy, veritabanı katmanında PostgreSQL (Neon) ve Type-safe sorgular için Drizzle ORM kullanmaktadır. Bağlantı, geliştirmede doğrudan Neon pooler URL’si veya Hyperdrive üzerinden yapılır; tek mantıksal veritabanı tüm kullanıcı, kurs, organizasyon (B2B) ve finans verisini barındırır. Sistem; eğitim içeriği, kullanıcı ilerlemesi, mesajlaşma, streak, sepet/istek listesi, quiz ve kod egzersizleri ile karmaşık finansal (Stripe Connect, Affiliate, iade) akışları yönetecek şekilde normalize edilmiştir.
Aşağıdaki diyagram, platformun kalbi olan ana tabloları ve aralarındaki yabancı anahtar (Foreign Key) ilişkilerini göstermektedir.
USER ||--o{ COURSE : "eğitmen olarak oluşturur"
USER ||--o{ ENROLLMENT : "öğrenci olarak kaydolur"
USER ||--o{ EARNINGS : "kazanç"
USER ||--o{ PAYOUT_REQUEST : "ödeme talep eder"
USER ||--o{ FOLLOWS : "takip"
USER ||--o{ CONVERSATION : "mesajlaşma"
USER ||--o{ WISHLIST : "istek listesi"
USER ||--o{ CART_ITEM : "sepet"
COURSE ||--o{ SECTION : "içerir"
SECTION ||--o{ LESSON : "içerir"
SECTION ||--o{ QUIZ : "içerir"
SECTION ||--o{ CODING_EXERCISE : "içerir"
LESSON ||--o{ LESSON_CAPTIONS : "altyazı"
COURSE ||--o{ ENROLLMENT : "öğrenci barındırır"
COURSE }o--|| PRICE_TIER : "fiyat katmanı"
PRICE_TIER ||--o{ TIER_PRICES : "para birimi fiyatları"
COURSE_BUNDLE ||--o{ BUNDLE_COURSES : "paket-kurs"
USER ||--o{ CERTIFICATE : "sertifika"
ENROLLMENT ||--o{ EARNINGS : "kazanç kaydı"
Tablo Açıklama Önemli Alanlar user Better Auth uyumlu kullanıcılar id, email, name, role (student/instructor/admin), stripeCustomerId, stripeConnectId, payoutMethod, manualPayoutDetails, country, affiliateCode, username (public profil slug), headline, bio, welcomeMessage, currentStreak, longestStreak, lastActivityDate, totalLearningMinutes, streakFreezeCount, isSubscriber, onboardingCompleted, KYC alanları: kycStatus (none/pending/approved/rejected), legalName, taxId, idDocumentUrl (ön yüz), idDocumentBackUrl (arka yüz), kycRejectionReasonsession Oturum token’ları id, userId, token, expiresAtaccount OAuth ve şifre hesapları id, userId, providerId, passwordverification E-posta/OAuth doğrulama id, identifier, value, expiresAt
Tablo Açıklama Önemli Alanlar follows Eğitmen takip (public profil) followerId, followingId (unique çift)conversations Öğrenci–eğitmen sohbetleri participant1Id, participant2Id, lastMessage, isImportantmessages Mesaj içerikleri conversationId, senderId, text, type, mediaUrl, isRead, isDelivered
Tablo Açıklama Önemli Alanlar price_tiers Fiyat katmanları (Tier 1, Tier 2, …) id, name, ordertier_prices Katman–para birimi fiyatları tierId, currency (usd, try, eur), amount (unique: tierId + currency)course_prices Kurs/paket çok para birimli fiyat (opsiyonel) courseId/bundleId, currency, amount, countryCodecourse_levels Dinamik seviye (beginner, intermediate, expert, all_levels) value, labelTr, labelEn, order, isActive
Tablo Açıklama Önemli Alanlar courses Kurslar id, instructorId, title, subtitle, description, price, priceTierId, thumbnailUrl, promoVideoId, status (draft/pending_review/published/archived), category, rating, reviewsCount, slug, learningOutcomes, prerequisites, targetAudience, language, level, isOpenForSubscriptionsections Bölümler courseId, title, orderlessons Dersler (video) sectionId, title, bunnyVideoId, order, isPreview, durationlesson_captions Video altyazıları (Bunny Stream) lessonId, languageCode, label, bunnyUrllesson_resources Ders ek dosyaları (PDF vb.) lessonId, title, fileUrl, fileSize, fileType
Tablo Açıklama Önemli Alanlar quizzes Quiz’ler (section’a bağlı) sectionId, title, description, order, passingScorequiz_questions Quiz soruları quizId, questionText, orderquiz_options Şıklar questionId, optionText, isCorrectquiz_resources Quiz ek dosyaları quizId, title, fileUrluser_quiz_attempts Öğrenci quiz sonuçları userId, quizId, score, passed, correctAnswers, totalQuestions
Tablo Açıklama Önemli Alanlar coding_exercises Kodlama egzersizleri sectionId, title, description, language, initialCode, solutionCode, hints, ordercoding_exercise_test_cases Test senaryoları exerciseId, input, expectedOutput, description, hint, isHidden, orderuser_exercise_submissions Öğrenci kod gönderimi userId, exerciseId, submittedCode, passed, passedTests, totalTests, executionTimeuser_coding_progress Egzersiz tamamlama (tekil kayıt) userId, exerciseId, courseId, completed, lastSubmittedCodeexercise_resources Egzersiz ek dosyaları exerciseId, title, fileUrl
Tablo Açıklama Önemli Alanlar enrollments Kurs/paket kayıtları userId, courseId, bundleId, enrolledAt, refundedAt, refundStatus, refundReason, stripePaymentIntentId, stripeCheckoutSessionId, referredBy (affiliate), couponCode, paidAmountMinor, paidCurrency, maxProgressPercentage. Unique: userId + courseIduser_lessons_progress Ders ilerlemesi ve izlenen süre userId, lessonId, courseId, completed, lastWatchedAt, watchedSeconds. Unique: userId + lessonIddaily_activities Günlük izleme (streak) userId, date (YYYY-MM-DD), totalWatchedSeconds. Unique: userId + datewatch_time_logs Heartbeat izlenme logları (payout/audit kaynağı) userId, courseId, lessonId, durationSeconds, createdAtcertificates Tamamlanan kurs sertifikaları userId, courseId, certificateCode (unique), fullName, courseTitle, instructorName, issueDatereviews Kurs değerlendirmeleri userId, courseId, rating, comment
Tablo Açıklama Önemli Alanlar questions Kurs soruları userId, courseId, lessonId (opsiyonel), videoTimestamp, title, contentanswers Cevaplar questionId, userId, content, isInstructorAnswercourse_notes Öğrenci video notları userId, courseId, lessonId, content, videoTimestamp
Tablo Açıklama Önemli Alanlar course_reports Kurslar için DMCA / içerik ihlali / spam şikayetleri courseId, reporterId, reason (copyright/inappropriate/spam/quality/other), description, status (pending/reviewed/resolved/rejected), createdAt
Tablo Açıklama Önemli Alanlar cart_items Sepet (kurs veya paket) userId, courseId veya bundleId (biri dolu), appliedCoupon (kupon kodu)wishlists İstek listesi userId, courseId
Tablo Açıklama Önemli Alanlar coupons Eğitmen kuponları code (unique), instructorId, courseId (null = genel indirim), bundleId, discountType (percentage/fixed_amount/free), discountValue, maxUses, usedCount, expiresAt, isActive. Detay: Kupon Sistemi
Tablo Açıklama Önemli Alanlar course_bundles Kurs paketleri instructorId, title, description, price, discountPercentage, thumbnailUrl, promoVideoId (Bunny), status, slug, discountValidFrom, discountValidUntil (indirim penceresi; vitrinde isBundleDiscountPeriodActive)bundle_courses Paket–kurs eşleşmesi (many-to-many) bundleId, courseId
Tablo Açıklama Önemli Alanlar earnings Kazançlar (satış, abonelik, affiliate) instructorId, courseId, bundleId, totalPrice, instructorShare, platformShare, affiliateShare, affiliateId, saleType, status, currency, couponCode, stripeCheckoutSessionIdpayout_requests Ödeme talepleri (eğitmen/affiliate) instructorId, amount, status, method (stripe/bank), destination, requestType (instructor/affiliate), earningIdsubscription_watch_logs Abonelik izlenme (eğitmen payı) userId, instructorId, durationSeconds, date. Unique: userId + instructorId + date
Tablo Açıklama Önemli Alanlar organizations Şirketler / tenant’lar id, name, subdomain (unique), allowedDomains (array), seatLimit, demoEndsAt, stripeCustomerId, stripeSubscriptionId, isActiveorganization_members Şirket üyeleri (RBAC) organizationId, userId, role (owner/admin/member). Unique: organizationId + userIdorganization_invitations Davet linkleri ve e-posta davetleri organizationId, email, role, token (unique), status, expiresAt, invitedBylearning_paths Şirkete özel eğitim rotaları organizationId, title, description, createdBylearning_path_courses Rota içi kurslar pathId, courseId, order
Ders videolarına eklenen altyazılar lesson_captions tablosunda tutulur: lesson_id (FK → lessons), language_code, label, isteğe bağlı bunny_url, created_at. Bunny Stream API ile senkron çalışır; detay için Video Altyazıları (Bunny Stream) sayfasına bakın.
daily_activities: Öğrencinin günlük izleme süresi (saniye) kaydedilir; streak hesaplaması bu tablo ve user.lastActivityDate / user.currentStreak / user.longestStreak ile yapılır.
user.totalLearningMinutes , user.streakFreezeCount: Öğrenci istatistikleri ve streak donması (1 gün kaçırma hakkı) için kullanılır.
POST /api/video-telemetry akışında payout’a dahil edilen izlenmeler ek kurallarla sınırlandırılır:
Self‑Watch Ban: course.instructorId === userId ise watch_time_logs / subscription_watch_logs yazılmaz (streak için daily_activities yine artar).
Human Daily Cap: Kullanıcı başına günlük paid maksimum 28.800 sn (8 saat).
Lesson 3x Cap: Ay içinde aynı lessonId için paid maksimum lessons.duration * 3 .
app/db/schema.ts — Drizzle şema; tüm tablolar ve ilişkiler.
app/db/index.ts — getDb, getDbUrl; Neon/Hyperdrive bağlantısı.
drizzle.config.ts — Drizzle Kit (migration) yapılandırması.
drizzle/ — SQL migration dosyaları.