Files
neta/docs/self-hosted-redesign/phase-2-domain-core.md
T

95 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Faz 2 Domain Schema ve Backend Çekirdeği
description: SQLite/Drizzle domain modeli, repository-service sınırı, actor yetkilendirmesi ve test sözleşmesi.
status: complete
last_updated: 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`, `ClientScope` ve 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 / total` oranı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 `clients` kaydı için üretilebilir. Kabul işlemi auth kayıtlarıyla birlikte `clients.auth_user_id` değerini aynı transaction'da yazar; session çözümlemesi `app_profiles.client_id` ile 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 `SUM` ile;
- proje ve görev durum dağılımları `GROUP BY` + `COUNT` ile;
- tüm sorgular `owner_user_id` scope'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.