D1 Database Design
Product Factory uses Cloudflare D1 (SQLite-based) databases with a domain-separated design. Each service worker has its own D1 database to maintain data isolation and independent scaling.
Database per Service
| Service | Database | Tables |
|---|---|---|
worker-auth | auth-db | user, session, account, verification |
worker-billing | billing-db | credit_accounts, credit_transactions, orders, pricing_plans, coupons, subscriptions, checkin_configs, checkin_records |
worker-ai | ai-db | ai_providers, ai_models, ai_api_keys, ai_usage_logs |
worker-content | content-db | posts, banners, friend_links |
worker-support | support-db | tickets, ticket_messages, notifications |
worker-admin | admin-db | projects |
worker-gateway | — | No dedicated D1 database; routes only via service bindings |
ORM Layer
All database access uses Drizzle ORM with the D1 adapter. Table definitions live in @repo/db-schema:
import { createDb } from "@repo/db-schema";
// In a worker route handler:const db = createDb(c.env.DB);const users = await db.select().from(userTable).where(eq(userTable.id, userId));Schema Package
The @repo/db-schema package provides:
- Table definitions — 22 Drizzle table schemas covering all service domains.
createDb(d1)— Factory function that wraps a D1 binding with Drizzle.createMockD1()— Test helper using better-sqlite3 for in-memory testing.
Repository Pattern
Each active domain package (billing-core, content-core, support-core, ai-core, project-core) provides repository classes that encapsulate database queries:
import { CreditRepository } from "@repo/billing-core";
const creditRepo = new CreditRepository(db);const balance = await creditRepo.getBalance(userId, appKey);Repositories accept an AppDatabase type from @repo/db-schema and expose domain-specific query methods. Worker route handlers create the database instance and pass it to repositories.
Migrations
Schema migrations are managed with drizzle-kit. Historical SQL migrations are preserved in infra/migrations/ for reference but are no longer used for new changes.
To generate a new migration:
pnpm --filter db-schema drizzle-kit generateTo apply migrations:
pnpm db:migrate