6.4 KiB
title, description, status, last_updated
| title | description | status | last_updated |
|---|---|---|---|
| Faz 2 Domain Schema ve Backend Çekirdeği | SQLite/Drizzle domain modeli, repository-service sınırı, actor yetkilendirmesi ve test sözleşmesi. | complete | 2026-07-16 |
Faz 2 Domain Schema ve Backend Çekirdeği
Faz 2, Neta'nın iş verilerini Supabase istemcisinden ayıran çalıştırılabilir backend çekirdeğini kurar. Bu faz sayfa sorgularını henüz taşımaz; Drizzle schema, migration, repository ve service katmanları UI, cookie ve Next.js request objelerinden bağımsızdır. Route Handler veya Server Action yalnızca session'ı DomainActor'a çevirip service çağırmalıdır.
Veri modeli
0003_chief_excalibur.sql migration'ı 15 domain tablosunu ekler:
- çekirdek:
clients,client_activities,projects,project_planning_sections,tasks,calendar_events,finance_transactions,journal_entries,project_revisions,chat_sessions,chat_messages; - korunacak iş verileri:
proposals,contracts,invoices,subscriptions.
Dosya metadata'sı ve instance branding bilinçli olarak Faz 3'e bırakılmıştır. ID'ler mevcut UUID'leri taşıyabilmek için text, parasal değerler integer minor unit, iş tarihleri YYYY-MM-DD, sistem zamanları UTC epoch millisecond olarak saklanır. Owner'a ait tablolarda açık owner_user_id, client portal bağında clients.auth_user_id bulunur.
Schema seviyesinde status/type enumları, negatif para ve süre değerleri, progress aralığı, revizyon kotası, tarih aralığı, currency uzunluğu, journal owner+date tekilliği ve invoice owner+number tekilliği SQLite CHECK/unique constraint'leriyle korunur. İlişkisel owner tutarlılığı service katmanında doğrulanır; istemciden gelen owner veya client kimliği güven kaynağı değildir.
Katman sınırları
| Katman | Sorumluluk | Bağımlı olmadığı şeyler |
|---|---|---|
| Schema | tablo, foreign key, index ve DB constraint | UI, Supabase |
| Repository | scope uygulanmış Drizzle sorguları ve aggregate'ler | session/cookie, Next.js |
| Service | validation, actor yetkisi, ilişki ve iş kuralları | Route Handler, React |
| Adapter | session→actor ve HTTP response dönüşümü | domain kuralı |
Ana giriş noktaları:
server/domain/actor.ts:DomainActor,OwnerScope,ClientScopeve role/disabled guard'ları;server/domain/validation.ts: paylaşılan Zod input sözleşmeleri;server/domain/errors.ts: stabil domain error code ve HTTP status eşlemesi;server/repositories/domain.ts: owner/client scope'u sorgu koşuluna dönüştüren repository'ler;server/services/domain.ts: CRUD, ilişki doğrulaması, portal görünürlüğü, revizyon ve aggregate kuralları;server/api/responses.ts:{ ok, data }ve{ ok, error }API envelope'u;server/auth/domain-actor.ts: web session adapter'ı.
Repository metoduna çıplak ownerUserId yerine tiplenmiş scope verilir. Owner kaynaklarında kimlik filtresi her sorguda uygulanır. Client proje erişimi bağlı clientId, görev erişimi ayrıca is_public_to_client = true üzerinden kısıtlanır. Calendar, finance, journal ve chat client rolüne kapalıdır.
İş kuralları
- Side project bir client'a bağlanamaz.
- Client/project/task/journal ilişkileri aynı owner altında bulunmalı ve birbiriyle uyuşmalıdır.
- Otomatik progress kullanan projeler, iptal edilmemiş görevlerdeki
done / totaloranından create/update/delete sonrasında yeniden hesaplanır. - Journal aynı owner ve iş tarihi için upsert edilir.
- Chat session ve journal context kayıtları aynı owner'a ait olmak zorundadır.
- Business preservation tablolarına yazılan client/project/proposal bağları owner scope'unda doğrulanır.
- Davet yalnızca owner'a ait gerçek bir
clientskaydı için üretilebilir. Kabul işlemi auth kayıtlarıyla birlikteclients.auth_user_iddeğerini aynı transaction'da yazar; session çözümlemesiapp_profiles.client_idile bu bağı karşılıklı doğrular.
Revizyon transaction'ı
Revizyon isteği client actor'dan clientId almaz; client kimliği actor scope'undan gelir. BEGIN IMMEDIATE transaction içinde proje-client eşleşmesi, projenin aktif olması ve reddedilmemiş tüketim kayıtlarından kalan kota kontrol edilir, sonra insert yapılır. Bu yaklaşım ayrı bir mutable sayaç tutmaz; başarısız transaction kota tüketmez ve eşzamanlı yazarlar kontrol ile insert arasına giremez.
Analytics yaklaşımı
Analytics için satırların tamamını belleğe alıp JavaScript'te toplamak yerine repository seviyesinde doğrudan SQLite aggregate sorguları kullanılır:
- ödenmiş gelir, ödenmiş gider ve planlanan/pending tutarlar koşullu
SUMile; - proje ve görev durum dağılımları
GROUP BY+COUNTile; - tüm sorgular
owner_user_idscope'u ile.
İleride dashboard zaman serileri de aynı yaklaşımda tarih aralığı ve currency filtresi eklenerek genişletilmelidir. Farklı para birimleri kur bilgisi olmadan birbirine çevrilmemelidir.
Doğrulama
npm run phase2:domain-smoke her çalışmada boş bir SQLite dosyasına gerçek migration'ları uygular, saf TypeScript domain çekirdeğini derler ve aşağıdaki senaryoları doğrular:
- owner CRUD scope'u ve cross-owner kaynak reddi;
- client'ın owner-only modüllerden reddi;
- bağlı proje/planlama görünürlüğü ve private task sızıntısının engellenmesi;
- otomatik project progress;
- project-client eşleşmesi, aktif proje kuralı, atomik quota ve quota aşımı;
- journal upsert, chat ownership ve ilişkisel owner doğrulamaları;
- owner-scope finance aggregate sonuçları;
- korunacak dört business tablosuna service üzerinden yazım;
- negatif amount ve geçersiz status için SQLite CHECK constraint'leri.
Faz 1 auth smoke'u da yerel client fixture'larıyla çalışır ve davet kabulünden sonra hem profile hem clients.auth_user_id bağını doğrular.
| Kontrol | Sonuç |
|---|---|
npm run typecheck |
Başarılı |
| Değişen Faz 2 dosyalarında targeted ESLint | 0 error, 0 warning |
npm run phase2:domain-smoke |
Başarılı |
node scripts/phase1-auth-smoke.mjs |
Başarılı |
node scripts/phase1-smoke.mjs |
Başarılı; migration, backup ve restore dahil |
pnpm db:generate |
Schema drift yok |
npm run build |
Başarılı |
Repo geneli lint, Faz 0'dan kaydedilmiş ve bu fazın değiştirmediği UI/AI dosyalarındaki baseline nedeniyle 31 error ve 18 warning ile açık kalır. Faz 2 dosyaları bu bulgulara yenisini eklemez.