Ana içeriğe geç
Projelere Dön
MedAtlas logo

MedAtlas

Eğitim Uygulaması

MedAtlas mimari şeması MedAtlas Veritabanı yok — içerik derleme anında doğrulanır, sayfalar statik üretilir Çalışma anında sunucu sorgusu yok; tüm okuma yolu build çıktısıdır. KAYNAK Yapısal veri src/data/*.json 58 kas · 25 kemik · 22 sinir 14 eklem · 6 bölge origo, insertio, innervasyon Anlatı içeriği src/content/**/*.mdx 96 dosya · klinik notlar ilişkiler ID ile değil slug ile DERLEME KAPISI Zod şema doğrulaması content:check — tek geçersiz kayıt exit 1 verir, dağıtım durur TypeScript tipleri şemadan türetilir ÇIKTI generateStaticParams 18 rota şablonu · ~330 statik sayfa /kaslar · /kemikler · /eklemler · /sinirler /bolgeler · /biyomekanik · /quiz · /pano Tarayıcıda çalışan kısım Fuse.js arama — ⌘K index istemcide kurulur, arama isteği sunucuya çıkmaz masaüstü modal ve mobil overlay aynı gövde Zustand — yalnız kişisel durum yer imi, ilerleme, quiz durumu içerik asla client store'a girmez içerik sunucudan gelir, kopyalanmaz Serwist — çevrimdışı service worker önbelleği bağlantı yoksa /offline sayfası telefona kurulabilir 11 birim testi + Playwright uçtan uca akışları; içerik doğrulaması testten önce, derleme kapısında çalışır. Yazma yolu yok: içerik yalnızca repoda değişir, panelden değil.
Veritabanı yok. Yapısal veri JSON dosyalarında (58 kas, 25 kemik, 22 sinir, 14 eklem, 6 bölge), anlatı içeriği 96 MDX dosyasında durur; ikisi de derleme kapısında Zod ile doğrulanır ve tek geçersiz kayıt dağıtımı durdurur. Çıktı 18 rota şablonundan yaklaşık 330 statik sayfadır. Tarayıcı tarafında Fuse.js araması, Zustand ile yalnız kişisel durum ve Serwist ile çevrimdışı önbellek çalışır; yazma yolu yoktur.

Tıp ve sağlık bilimleri öğrencileri için anatomi atlası. Telefonda hızlı açılıyor, internetsiz de çalışıyor; arama ve quiz doğrudan tarayıcıda. İçerik yayına çıkmadan önce otomatik olarak kontrol ediliyor.

Kullanılan Teknolojiler

  • Next.js 16
  • TypeScript
  • Zod
  • MDX
  • Fuse.js
  • Zustand
  • Serwist
  • Playwright

Öne Çıkan Özellikler

  • Tamamen statik üretim — çalışma anında veritabanı sorgusu yok
  • İçerik derleme kapısında Zod ile doğrulanıyor; geçersiz tek kayıt dağıtımı durduruyor
  • ⌘K ile açılan arama tarayıcıda çalışıyor, sorgu sunucuya çıkmıyor
  • İlişkiler kimlik yerine slug üzerinden kuruluyor
  • Kişisel durum (yer imi, ilerleme, quiz) yalnız istemcide; içerik client store'a girmiyor
  • Service worker ile çevrimdışı kullanım ve telefona kurulabilirlik

Geliştirme Süresi

2 ay

Problem

Anatomi içeriği iki farklı türden oluşuyor: bir yanda tamamen yapısal veri — kasın başlangıcı, yapışma yeri, siniri, hareketi — öbür yanda serbest anlatı, klinik not, ayrım tanısı. İlkini bir metin dosyasında tutmak veriyi aranamaz ve tutarsız hâle getiriyor; ikincisini bir veritabanı tablosuna sıkıştırmak ise yazmayı imkânsızlaştırıyor. Asıl mesele ise doğruluk: eğitim içeriğinde bir alanın boş kalması ya da bir referansın var olmayan bir yapıya işaret etmesi, çalışan öğrenci için sessiz bir hata. Sayfa açılıyor, tablo görünüyor, yalnız bir satır eksik. Bu tür bir hatanın yayına çıkmadan yakalanması gerekiyordu.

Kullanıcılar

Tek bir kullanıcı tipi ama iki farklı kullanım anı var. Çalışan öğrenci: uzun oturumlarda okuyor, bir yapıdan diğerine sürekli atlıyor — kastan sinire, sinirden bölgeye — ve arama kutusunu klavyeden açmak istiyor. Tekrar eden öğrenci: sınav öncesi, ders arasında, telefondan, çoğu zaman kötü bir bağlantıyla. İkinci kullanım biçimi çevrimdışı çalışmayı ve hızlı açılışı zorunlu kılıyor.

Rolüm

Tek geliştirici. İçerik şeması ve doğrulama hattı, veri modeli, statik üretim yapısı, arama, quiz akışı, çevrimdışı katman ve testler.

Mimari

Sistemin merkezinde bir karar var: veritabanı yok. Yapısal veriler depoda JSON olarak, anlatı içeriği MDX olarak duruyor; detay sayfasında ikisi bir yükleyici üzerinden birleşiyor. İlişkiler kimlik yerine slug ile kuruluyor, yani bir kasın siniri "sinir-42" değil, o sinirin okunabilir adı — bu hem adresleri hem de içerik dosyalarını insan tarafından düzenlenebilir tutuyor. Tüm detay sayfaları `generateStaticParams` ile derleme anında üretiliyor; çalışma anında sunucuya giden bir okuma yolu yok. Varsayılan render biçimi Server Component; `"use client"` yalnız gerçek etkileşim olan yerde. Tarayıcı tarafında üç şey çalışıyor: Fuse.js ile bulanık arama — index istemcide kuruluyor, masaüstündeki ⌘K modalı ve mobildeki tam ekran katman aynı gövdeyi paylaşıyor — Zustand'da tutulan kişisel durum ve service worker önbelleği. Zustand'da içerik yok, yalnız kullanıcıya ait olan şey var: yer imi, ilerleme, quiz durumu.

Kritik Kararlar

  1. Veritabanı yok — içerik depoda, doğrulama derleme anında

    İçerik günde yüz kez değişmiyor; dönemde birkaç kez, üstelik gözden geçirilerek değişiyor. Böyle bir içerik için veritabanı, çalışma anında sorgu, bağlantı yönetimi ve ayrı bir yönetim arayüzü maliyeti getiriyor — karşılığında verdiği tazelik ise gerekmiyor. Bunun yerine içerik sürüm kontrolünde: her değişiklik gözden geçirilebilir bir fark olarak duruyor ve geri alınabiliyor.

    Ödünleşimİçeriği değiştirmek için depoya erişim ve yeni bir dağıtım gerekiyor; alan uzmanı bir kullanıcı tek başına düzeltme yapamıyor. Bir yazım hatası bile bir build turu bekliyor.

  2. Geçersiz içerik dağıtımı durduruyor

    Doğrulama script'i bütün veri dosyalarını Zod şemalarıyla ayrıştırıyor ve tek bir geçersiz kayıtta sıfırdan farklı çıkıyor. TypeScript tipleri de aynı şemadan türetiliyor, yani şema tek kaynak: tip ile çalışma anındaki doğrulama ayrışamıyor. Eğitim içeriğinde bu kapı, "sayfa açılıyor ama bilgi eksik" hatasını yayına çıkmadan yakalayan tek mekanizma.

    ÖdünleşimŞema katı olduğu için henüz tamamlanmamış bir içerik parçası depoya giremiyor; taslak hâlinde çalışmak isteyen biri ya alanı boş bırakma iznini şemaya eklemek ya da içeriği depo dışında tutmak zorunda. Şemanın gevşetilmesi ise kapının değerini doğrudan düşürüyor.

  3. Arama tarayıcıda, sunucuda değil

    Aranan veri kümesi küçük ve sabit; derleme anında hazır olan bir index'i istemciye göndermek, her tuş vuruşunda sunucuya gitmekten hem hızlı hem de basit. Bulanık eşleşme, Latince terim ile Türkçe karşılığın aynı sorguda bulunabilmesi için gerekiyordu. Masaüstü modalı ile mobil katman aynı gövdeyi ve aynı index'i paylaşıyor.

    ÖdünleşimIndex istemciye indiriliyor; içerik büyüdükçe bu maliyet büyüyecek ve bir noktada sunucu tarafı aramaya geçmek gerekecek. Ayrıca hangi terimin arandığına dair hiçbir veri toplanmıyor — bu gizlilik açısından iyi, içerik önceliklendirmesi açısından bir körlük.

  4. Kişisel durum istemcide kalıyor, içerik hiç girmiyor

    Yer imi, ilerleme ve quiz durumu kullanıcıya ait ve sunucuda saklanmasının bir karşılığı yok — hesap yok, senkronizasyon yok. Buna karşılık içeriğin istemci store'una kopyalanması yasak: içerik sunucudan gelen tek kaynaktır, ikinci bir kopyası tutulursa iki kaynak zamanla ayrışıyor.

    ÖdünleşimKullanıcı cihaz değiştirdiğinde ilerlemesi taşınmıyor; tarayıcı verisi temizlendiğinde de kayboluyor. Hesap açmadan çalışmanın bedeli bu ve kabul edildi.

Teknoloji Seçimi

Next.js App Router burada bir sunucu çatısı olarak değil, bir statik site üreticisi olarak kullanılıyor: `generateStaticParams` ile yüzlerce detay sayfası derleme anında çıkıyor ve Server Component varsayılan olduğu için istemciye giden JavaScript en aza iniyor. Zod'un iki işi var — hem doğrulama kapısı hem tip kaynağı; ikisinin tek yerden gelmesi, şemayı değiştirdiğinde tipin de değişmesini garanti ediyor. MDX, klinik anlatının yazı gibi yazılmasına izin verirken bileşen gömmeyi de açık bırakıyor. Fuse.js küçük ve istemcide çalışabilecek kadar hafif. Serwist, çevrimdışı katmanı service worker yazmadan kuruyor. Görsel tarafta etkileşimli SVG, yakınlaştırma ve kaydırmayı raster bir görselin bulanıklığına düşmeden veriyor.

Güvenlik

Uygulamanın yazma yolu yok: form, oturum, hesap ve kullanıcı veritabanı bulunmuyor. Dolayısıyla saklanan kişisel veri de yok — ilerleme ve yer imi kullanıcının cihazından çıkmıyor. İçerik üretimi sırasında kullanılan model çağrıları yalnız script'lerde; çalışma anında hiçbir dış servise istek gitmiyor. Bu, saldırı yüzeyini içeriğin doğruluğuna indiriyor — ve o da derleme kapısıyla korunuyor.

Performans

Bu proje için saklanmış, tarih damgalı bir Lighthouse çıktısı bulunmadığı için buraya sayfa hızı skoru yazılmadı. Yapısal tarafta ölçü şu: okuma yolunda sunucu sorgusu yok, sayfalar derleme çıktısı olarak sunuluyor. Arama index'i ve quiz durumu istemcide olduğu için etkileşim gecikmesi ağ turuna bağlı değil. Service worker son görülen sayfaları önbellekte tutuyor; bağlantı koptuğunda uygulama boş ekran yerine kendi çevrimdışı sayfasını gösteriyor.

Sonuçlar

Aktif geliştirme
2026-04 → 2026-06 · 12 commit
TypeScript kaynak dosyası
139
Test dosyası
11 — birim testleri ve Playwright uçtan uca akışları
İçerik hacmi
96 MDX dosyası · 58 kas · 25 kemik · 22 sinir · 14 eklem · 6 bölge
Rota
18 sayfa şablonu — atlas modülleri, biyomekanik, quiz, pano ve çevrimdışı sayfası
Mimari garanti
Şemaya uymayan tek bir içerik kaydı dağıtımı durdurur
Mimari garanti
İçerik istemci store'una kopyalanmaz; tek kaynak derleme çıktısıdır

Bugün Ne Farklı Yapardım

Doğrulama kapısını baştan koydum ve bu projede en çok işe yarayan karar oydu; aynı deseni sonradan Rahmina'da içerik onay kapısı olarak tekrarladım — orada kural "şemaya uygun mu" değil, "gözden geçirildi mi" oldu. Buradaki eksik tam olarak o: şema bir alanın dolu olduğunu doğruluyor ama içeriğin doğru olduğunu doğrulamıyor. Bugün her içerik bloğuna bir gözden geçirme durumu ekler ve yayın kapısını "onaysız içerik varsa build düşer" hâline getirirdim. İkinci nokta arama: index'in istemciye inmesi bugünkü hacimde doğru karar, ama bu kararın ne zaman bozulacağını ölçen bir şey yok. Bugün index boyutunu bir eşikle sınırlar, eşik aşıldığında derlemeyi uyarır hâle getirirdim — kararın kendisi kadar, kararın ne zaman geçersizleşeceği de kod içinde durmalı.

Prototip

Nereden başlayacağınızı birlikte bulalım

Ne yapmak istediğinizi anlatın; ne kadar süreceğini ve en mantıklı ilk adımı baştan söylerim.