# SvelteKit Modular Monolith + Vertical Slice Architecture Bu doküman, bir yapay zekâ ajanının SvelteKit projelerini aşağıdaki prensipler çerçevesinde tasarlaması ve geliştirmesi için tekil mimari sözleşmedir. Bu belgeyi okumadan kod yazma. Kullanıcı tarafından açıkça istenmeyen opsiyonel altyapıları projeye ekleme. Bir kuralın mevcut proje veya gereksinimlerle çeliştiğini düşünüyorsan sessizce ihlal etme; durumu ve önerini belirt. --- ## 1. Temel felsefe Bu mimari iki seviyede karar verir: ### Makro seviye: Modular Monolith Uygulama iş kabiliyetlerine göre modüllere ayrılır. Örnek modüller: ```text Users Workspaces Projects Tasks Billing Notifications ``` Her modül: - Bir veya birkaç açık iş kabiliyetinin sahibidir. - Kendi iş kurallarını ve iç uygulamasını kapsüller. - Kendi verisinin sahibidir. - Dışarıya yalnızca bilinçli bir public API veya contract açar. - Diğer modüllerin iç dosyalarına ve verisine doğrudan erişmez. Modüler monolith, tek deploy edilen uygulama olabilir; modül sınırları yine de gelecekte ayrı servis çıkarılabilecek kadar açık olmalıdır. Mikroservis eklemek bu mimarinin hedefi değildir. ### Mikro seviye: Vertical Slice Architecture Modül içindeki kod teknik katmanlara göre değil, kullanıcı davranışı veya use-case'e göre gruplanır. ```text Orders ├── features │ ├── create-order │ ├── cancel-order │ └── get-order ├── domain └── infrastructure ``` Bir feature mümkün olduğunca kendi: - input/schema doğrulamasını, - use-case orkestrasyonunu, - output mapping'ini, - ilgili repository/adaptor çağrılarını, - testlerini yakınında tutar. ### Clean Architecture ne zaman kullanılır? Clean Architecture zorunlu klasör şablonu değildir. Aşağıdaki durumlarda gerektiği kadar uygulanır: - Domain kuralları karmaşık ve yoğun ise, - Aynı domain davranışı birçok feature tarafından kullanılıyorsa, - Altyapı değişiminden bağımsız kalmak önemliyse, - Modül büyümüş ve tek feature dosyası anlaşılmaz hale gelmişse. Basit CRUD için sırf prensip gereği çok sayıda interface, service, mapper ve katman oluşturma. Önce feature içinde basit ve açık çözüm üret; gerçek tekrar ve karmaşıklık oluştuğunda ayır. --- ## 2. Değişmez mimari kuralları ### 2.1 Modül sınırları 1. Her business capability bir modülün sahibi olmalıdır. 2. Bir modül başka modülün `domain`, `features`, `infrastructure` veya iç dosyalarını import edemez. 3. Modüller arası çağrı yalnızca hedef modülün `public.ts` veya açık contract'ı üzerinden yapılır. 4. Bir modül başka modülün database tablosuna/repository'sine doğrudan erişemez. 5. Modül sınırını aşan ortak davranış önce contract veya public use-case olarak tasarlanmalıdır. 6. Modül sınırı değişiyorsa kısa bir ADR yazılmalıdır. Yanlış: ```ts import { Order } from '$lib/modules/orders/domain/order'; import { orders } from '$lib/modules/orders/infrastructure/db.server'; ``` Doğru: ```ts import { getOrderSummary } from '$lib/modules/orders/public'; ``` ### 2.2 Dependency direction Tercih edilen akış: ```text SvelteKit route/action/API ↓ Module public API ↓ Feature use-case ↓ Domain rules / module repository ↓ Infrastructure adapter ``` Kurallar: - Route ve component business logic içermez. - UI, database veya secret okuyamaz. - Business logic SvelteKit route dosyalarına dağılmaz. - `.server.ts` dosyası client component tarafından import edilmez. - Circular dependency oluşturulmaz. - Bir abstraction yalnızca gerçek bir ihtiyacı çözüyor ise eklenir. ### 2.3 Shared kod `src/lib/shared` ortak çöp kutusu değildir. Shared'e yalnızca gerçekten business-agnostic kod alınabilir: - primitive result/error tipleri, - tarih veya id gibi teknik yardımcılar, - ortak serialization yardımcıları, - teknik logging/validation altyapısı. Bir kod iki yerde kullanılıyor diye hemen shared'e taşıma. Önce iki kullanımın gerçekten aynı davranışa sahip olduğunu doğrula. Business kuralı mümkünse onu kullanan modülde kalmalıdır. --- ## 3. Önerilen SvelteKit proje yapısı Başlangıç için bu yapı yeterlidir: ```text src/ ├── lib/ │ ├── modules/ │ │ ├── users/ │ │ │ ├── public.ts │ │ │ ├── contracts.ts │ │ │ ├── features/ │ │ │ │ └── register-user/ │ │ │ └── domain/ │ │ ├── orders/ │ │ └── _template/ │ │ │ ├── shared/ │ └── server/ │ └── _optional-adapters/ │ ├── routes/ │ ├── api/ │ │ └── orders/ │ │ └── +server.ts │ └── (app)/ │ └── orders/ │ ├── +page.svelte │ └── +page.server.ts │ └── app.html ``` Bir modül için önerilen yapı: ```text src/lib/modules/orders/ ├── public.ts ├── contracts.ts ├── features/ │ ├── create-order/ │ │ ├── schema.ts │ │ ├── use-case.server.ts │ │ ├── mapper.ts │ │ ├── types.ts │ │ ├── repository.server.ts │ │ └── use-case.test.ts │ └── cancel-order/ ├── domain/ │ ├── order.ts │ └── order-errors.ts └── infrastructure/ └── orders-repository.server.ts ``` Her modülün bütün klasörleri zorunlu değildir. Basit modül şu kadar küçük olabilir: ```text modules/catalog/ ├── public.ts └── features/ └── list-products/ └── use-case.server.ts ``` --- ## 4. SvelteKit katmanlarının sorumluluğu ### Routes `src/routes` transport/UI adapter katmanıdır. Route şunları yapabilir: - request/form okumak, - authentication context almak, - input'u use-case'e geçirmek, - sonucu HTTP veya page data formatına çevirmek, - redirect veya HTTP error üretmek. Route şunları yapamaz: - sipariş toplamı hesaplamak, - domain state değiştirmek, - doğrudan database query yazmak, - başka modülün iç dosyasını import etmek. Örnek: ```ts // src/routes/api/orders/+server.ts import { createOrder } from '$lib/modules/orders/public'; export async function POST({ request }) { const input = await request.json(); const result = await createOrder(input); if (!result.ok) return Response.json(result.error, { status: 400 }); return Response.json(result.value, { status: 201 }); } ``` ### Components Svelte component'leri: - görüntüleme, - kullanıcı etkileşimi, - lokal UI state'i, - form gösterimi ile ilgilenir. Domain kuralı, secret, database veya auth provider çağrısı component içine konmaz. ### Server-only kod Şu kodlar `.server.ts` dosyalarında bulunmalıdır: - database erişimi, - secret kullanımı, - auth/session doğrulaması, - filesystem erişimi, - harici servis credential'ları, - server-only repository/adaptor'lar. Client'a gidebilecek bir dosyanın import zincirinde server-only kod bulunmamalıdır. --- ## 5. Vertical slice tasarım kuralları Yeni bir davranış eklerken: 1. Önce hangi modülün sahibi olduğunu belirle. 2. Use-case'in adını fiil + nesne şeklinde belirle: `create-project`. 3. Yeni bir feature klasörü oluştur. 4. Input validation'ı feature'a yakın tut. 5. Orkestrasyonu feature use-case içinde tut. 6. Ortak domain kuralı gerçekten ortaksa `domain` altına çıkar. 7. Sadece ihtiyaç varsa module public API'sine export et. 8. Route veya UI adapter'ını en son ekle. Basit feature: ```text features/create-project/ ├── schema.ts ├── use-case.server.ts └── types.ts ``` Karmaşık feature: ```text features/submit-order/ ├── schema.ts ├── command.ts ├── handler.server.ts ├── policy.ts ├── mapper.ts ├── repository.server.ts ├── types.ts └── submit-order.test.ts ``` Dosya sayısını bir mimari başarı ölçüsü olarak görme. Hedef, davranışın kolay bulunması ve güvenli değiştirilebilmesidir. --- ## 6. Public API ve contracts Modül dışarıya açık davranışları bilinçli olarak export eder: ```ts // src/lib/modules/orders/public.ts export { createOrder } from './features/create-order/use-case.server'; export { getOrderSummary } from './features/get-order/use-case.server'; export type { OrderSummary } from './contracts'; ``` `public.ts` üzerinden export edilmemiş bir internal symbol başka modülden kullanılamaz. `contracts.ts` şu amaçlarla kullanılabilir: - modüller arası request/response tipleri, - event contract'ları, - ortak public value object'ler. Internal database modellerini public contract olarak export etme. --- ## 7. Opsiyonel konular: önce sor, sonra ekle Aşağıdaki özellikler varsayılan olarak projeye eklenmez. Gereksinimlerde karşılığı varsa AI ajanı önce kullanıcıya seçenekleri ve etkilerini sormalı, onaydan sonra entegre etmelidir. ### 7.1 Database ve persistence Sorulacak sorular: - Kalıcı veri gerekiyor mu? - Hangi database tercih ediliyor? - ORM/query builder kullanılsın mı? - Migration çalıştırma sorumluluğu kimde? - Modül başına schema/table izolasyonu yeterli mi? Kurallar: - Database seçimi gereksinim olmadan yapılmaz. - Her modül kendi persistence adapter'ını kapsüller. - Bir modül başka modülün tablosuna doğrudan query atmaz. - Domain modeli database modeliyle otomatik olarak eşitlenmez. - Migration değişiklikleri açıkça raporlanır. Önerilen sınır: ```text feature/use-case -> module repository -> database adapter ``` ### 7.2 Authentication ve authorization Sorulacak sorular: - Kullanıcı hesabı gerekiyor mu? - Session mı, token mı kullanılacak? - Hangi roller veya permission'lar var? - Multi-tenant yapı var mı? - Auth sağlayıcısı seçildi mi? Kurallar: - Auth provider seçimini kullanıcı onayı olmadan yapma. - Secret'ları client'a gönderme. - Authorization kararını UI'a bırakma. - Yetki kontrolünü route sınırında başlat ve hassas use-case içinde tekrar doğrula. - Use-case'e provider nesnesi yerine küçük bir typed actor/context geçir. ### 7.3 UI library ve design system Sorulacak sorular: - Hazır UI library gerekli mi? - Görsel tasarım veya design system var mı? - Accessibility ve responsive hedefleri nelerdir? - Client-side state management gerekiyor mu? Varsayılan olarak plain Svelte + küçük, yerel component'lerle başla. Bir UI library seçilecekse bunun bütün projeye etkisini belirt. ### 7.4 Caching Caching ancak ölçülebilir bir ihtiyaç varsa eklenir. Sorulacak sorular: - Cache hangi veride gerekli? - Geçerlilik süresi nedir? - Invalidation kuralı nedir? - Yanlış/stale veri kabul edilebilir mi? Cache kararını module veya feature contract'ının dışında gizleme. ### 7.5 Events, queue ve background jobs Sorulacak sorular: - İşlem synchronous mı kalmalı? - Eventual consistency kabul ediliyor mu? - Retry ve idempotency nasıl sağlanacak? - Job gözlemlenebilirliği nasıl yapılacak? Event bus veya queue, sırf modüler görünmek için eklenmez. Önce doğrudan function/use-case çağrısı tercih edilir. ### 7.6 Search, files ve external integrations Harici servisler module infrastructure adapter'ı arkasında kalmalıdır. ```text feature -> module port/interface -> external adapter ``` Provider SDK'sını domain veya UI dosyalarına yayma. ### 7.7 Observability Production ihtiyacı varsa aşağıdakiler ayrıca kararlaştırılır: - structured logging, - correlation/request id, - error tracking, - metrics, - health/readiness check. Loglara secret, token, password veya kişisel veriyi yazma. --- ## 8. AI ajanı için çalışma protokolü AI ajanı her görevde şu sırayı izlemelidir. ### 8.0 Handoff dosyaları Göreve başlamadan önce şu dosyaları oku: - `AGENTS.md`: çalışma kuralları, - `ARCHITECTURE.md`: mimari sözleşme, - `progress.md`: mevcut aşama, aktif iş ve blokajlar, - `memory.md`: kalıcı kararlar, gerekçeler ve önceki ajan handoff bilgisi. `progress.md` güncel durumun kaynağıdır; eski günlük veya uzun açıklama burada tutulmaz. `memory.md` kalıcı bilgi içindir; her küçük işlemi kaydetme. Büyük kararlar için ayrıca `docs/decisions/` altında ADR oluştur. Görevi bitirirken `progress.md` dosyasını güncelle. Yalnızca gelecekteki ajanların işine yarayacak kararları, gerekçeleri ve riskleri `memory.md` içine ekle. Bu dosyalara secret, token, password, private key veya kişisel veri yazma. ### Aşama 1: Gereksinimi anla - Kullanıcı amacı nedir? - Hangi aktör bu davranışı kullanıyor? - Hangi modülün sahibi? - Yeni bir opsiyonel altyapı gerekiyor mu? ### Aşama 2: Eksik kararları sor Database, auth, payment, queue, external API veya UI library gerekmiyorsa ekleme. Gerekiyorsa kısa bir seçenek listesi ve etkisini sun; kullanıcı onayı gelmeden entegre etme. ### Aşama 3: Projeyi incele - Mevcut module catalog'u oku. - İlgili modülün `public.ts` dosyasını oku. - Benzer feature'ları incele. - Route ve server sınırlarını kontrol et. - Var olan test/check komutlarını keşfet. ### Aşama 4: Plan oluştur Plan şunları içermeli: - module ownership, - feature adı, - değişecek dosyalar, - public API etkisi, - veri/migration etkisi, - test planı, - opsiyonel kararlar. ### Aşama 5: Uygula - En küçük çalışan değişikliği yap. - Mevcut pattern'leri takip et. - Gereksiz refactor yapma. - Modül sınırını ihlal etme. - İhtiyaç yoksa yeni dependency ekleme. ### Aşama 6: Doğrula Mümkünse şu komutları çalıştır: ```bash npm run check npm run check:architecture npm run test npm run lint ``` Çalıştırılamayan komutları ve nedenini açıkça raporla. ### Aşama 7: Raporla Sonuç raporu şunları içermeli: - yapılan değişiklikler, - eklenen veya değişen boundary'ler, - kullanılan opsiyonel altyapılar, - test/check sonuçları, - bilinen riskler, - kullanıcıdan beklenen kararlar. --- ## 9. Definition of Done Bir feature tamamlanmış sayılmadan önce: - Doğru module ait olmalı. - Vertical slice olarak bulunabilir olmalı. - Route/component business logic taşımamalı. - Public API gerekiyorsa bilinçli şekilde güncellenmiş olmalı. - Cross-module internal import olmamalı. - Input ve hata davranışı tanımlı olmalı. - Server-only sınırları korunmalı. - En azından uygun seviyede test eklenmiş olmalı. - Typecheck ve architecture check başarılı olmalı. - Database/auth/migration gibi yan etkiler raporlanmış olmalı. --- ## 10. İstisna ve ADR kuralları Hiçbir mimari kural körü körüne uygulanmaz; fakat istisna görünür olmalıdır. Şu durumlarda ADR yaz: - Bir modül başka modülün verisine erişmek zorunda kalıyorsa, - Public API yerine yeni bir iletişim yöntemi ekleniyorsa, - Shared business code oluşturuluyorsa, - Module boundary değişiyorsa, - Monolith dışına servis çıkarılıyorsa, - Yeni bir global framework veya altyapı seçiliyorsa. ADR minimum formatı: ```md # ADR: Başlık ## Context ## Decision ## Alternatives ## Consequences ``` İstisna, ajan tarafından gizli bir import veya geçici workaround olarak bırakılmamalıdır. --- ## 11. Kısa görev prompt'u Bu mimari sözleşme yüklü bir projede kullanıcı şu kadar kısa bir prompt verebilmelidir: ```text Projects modülüne CreateProject özelliği ekle. Gereksinimler: - Proje adı zorunlu. - Aynı workspace içinde isim benzersiz. - Başarılı işlemde proje bilgisi dönmeli. - Kullanıcı hata mesajını görebilmeli. Bu ARCHITECTURE.md kurallarına uy, gerekli kararları önce sor, testleri ekle ve tüm kontrolleri çalıştır. ``` AI ajanı bu dokümandaki kurallar doğrultusunda modülü, vertical slice'ı, route adapter'ını ve testleri tasarlamalıdır. --- ## 12. Son ilke Bu mimarinin amacı çok klasör veya çok abstraction üretmek değildir. Amaç: ```text yüksek cohesion düşük coupling kolay keşfedilebilir feature'lar korunan module boundary'leri gerektiğinde büyüyebilen basit başlangıç ``` Önce en basit açık çözümü oluştur. Gerçek karmaşıklık ortaya çıktığında doğru parçayı ayır. Kullanıcı onayı olmadan opsiyonel altyapı veya mimari yük ekleme.