Skip to content

Data Governance

Dokumen ini jadi panduan teknis soal bagaimana data diatur, di-validate, dan di-protect di Soul Map Atlas. Semua implementasi atau fix yang berurusan dengan business logic dan data governance wajib update dokumen ini dan bagian overview di overview/product.md.

Data Ownership & Access Control

Role-Based Access

System pakai 3 role sederhana:

RoleDeskripsi
memberBisa akses data sendiri doang. Default role untuk user baru.
adminBisa akses data user lain untuk moderation dan support.
ownerSama kayak admin — tidak ada level di atas admin. Dipakai untuk superuser.

Role check dilakukan via string comparison di middleware. Tidak ada ACL system atau permission matrix:

ts
// ✅ Sederhana dan cukup
if (user.role !== "admin" && user.role !== "owner") {
  throw new ORPCError("FORBIDDEN");
}

Data Isolation

  • Setiap user hanya bisa lihat dan edit data milik sendiri.
  • Admin/owner bisa override isolation untuk moderation.
  • AI sessions dan journal entries strictly tied ke user ID.

Schema Validation & Type Safety

Input Validation

Semua API input wajib divalidate pakai Zod sebelum masuk ke business logic:

ts
// Contract layer
const createJournalSchema = z.object({
  text: z.string().min(5),
  moodTrend: z.object({
    stress: z.number().min(1).max(10),
    energy: z.number().min(1).max(10),
    mood: z.number().min(1).max(10),
  }).optional(),
  tags: z.array(z.string()).optional(),
});

Database Schema Single Source of Truth

  • Schema utama ada di packages/db/src/schema/.
  • Auth schema (user, session, account, verification) ada di packages/db/src/schema/auth.ts.
  • Tidak boleh ada schema definition duplikat di tempat lain.

Business Logic Layer

Layer Architecture

Route (apps/api/src/routes/)
  ↓ call
Middleware (auth, validation, context build)
  ↓ call
Service (apps/api/src/services/)
  ↓ pure business logic, no auth awareness

Services layer wajib:

  • Pure function — tidak boleh access request context atau env langsung.
  • Menerima semua data yang dibutuhkan via parameter.
  • Return typed result, throw error kalau gagal.

Example Service Pattern

ts
// services/journal.ts
export async function createJournalEntry(
  db: DbClient,
  userId: string,
  data: CreateJournalInput
): Promise<JournalEntry> {
  // Validation sudah di-route layer
  const entry = await db.insert(journalEntries).values({
    userId,
    ...data,
    createdAt: new Date(),
  }).returning();

  return entry[0];
}

Subscription & Usage Tracking

Plan Tiers

PlanFitur
freeBasic numerology, Primbon Jawa, 1 personality test
deep_insightsAkses Deep Insights untuk compatibility
premiumUnlimited personality + journal
premium_plusSemua fitur + prioritas AI sessions

Usage Counters

Usage tracking di-level user dengan counter sederhana:

ts
// Schema snippet
usage: {
  personalityUsed: number;      // default 0
  personalityLimit: number;     // free = 1, premium = unlimited
  deepInsightCompatibilityUsed: number;
  deepInsightCompatibilityCredits: number;  // one-time purchases
}

Counter Rules

  • Counter di-increment saat user memakai fitur.
  • Check limit sebelum eksekusi — jangan charge dulu baru ngecek.
  • One-time purchases (credits) dipakai sebelum subscription limit.
  • Reset counter bulanan untuk subscription users via cron (belum diimplement).

Data Privacy & Compliance

  • Setiap user harus setujui privacy policy dan terms of service saat signup.
  • Consent record disimpan dengan timestamp dan version policy.
  • Admin bisa export consent records untuk audit.

Personal Data

  • Birth date, birth time, birth city — data sensitive yang hanya boleh diakses user sendiri dan AI service (untuk kalkulasi).
  • Compatibility result disimpan encrypted-at-rest (encryption key via env).
  • Journal entries strictly private — tidak ada sharing feature.

Data Retention

  • User account dan data bisa di-delete via request ke admin.
  • Soft delete untuk audit trail (mark deletedAt, tidak hapus row).
  • Backup D1 via Cloudflare automatic backups.

AI Feature Governance

AI Session Limits

PlanAI Sessions per Month
free0 (tidak tersedia)
deep_insights5
premium20
premium_plusunlimited

AI Message Tracking

  • ai_sessions_used dan ai_messages_used di-track per bulan.
  • Reset otomatis via cron job (belum diimplement, akan di Phase 3).

AI Response Handling

  • AI response tidak disimpan permanent — hanya context window saja.
  • Kalau AI service error, return friendly error ke user, log ke monitoring.

Payment & Purchase Tracking

One-Time Purchases

  • Deep Insights Compatibility bisa dibeli one-time tanpa subscription.
  • Purchase record disimpan di array purchases dengan status: paid, pending, failed.
  • Credits tidak refundable.

Payment Status Flow

pending → paid → used

failed (bisa retry)

Webhook Security

  • Payment webhooks (Doku) wajib di-verify signature.
  • Idempotency key wajib untuk setiap transaksi.
  • Log semua webhook events untuk audit.

Audit Trail

What to Log

Semua operasi yang mengubah data wajib di-log:

  • User signup, login, logout
  • Subscription change (upgrade/downgrade/cancel)
  • Payment events
  • Admin actions (view user data, moderation)
  • AI session usage

Log Format

ts
interface AuditLog {
  actorId: string;        // siapa yang melakukan
  actorType: "user" | "admin" | "system";
  action: string;         // apa yang dilakukan
  resourceType: string;   // tipe data yang diubah
  resourceId: string;     // ID data yang diubah
  metadata: object;       // detail tambahan
  timestamp: Date;
}

Log Storage

  • Audit logs disimpan di D1 table audit_logs.
  • Retention: 1 tahun.
  • Admin bisa export via API (owner-only).

Anti-Patterns

  • ❌ Business logic di route handler — pindahkan ke services layer.
  • ❌ Direct database access dari frontend — semua query lewat API.
  • ❌ Hardcode limits di kode — gunakan config/env.
  • ❌ Skip validation sebelum business logic — Zod wajib di semua input.
  • ❌ Store plain text password — selalu hash pakai scrypt.
  • ❌ Expose internal error ke user — return generic error, log detail.

References