DB Dialect: Datenbank-Abstraktion
Aktuell: Drizzle + PostgreSQL
Drizzle ist der beste Kompromiss fuer unseren Use Case: SQL-nah, Schema-Builder, Relations, Migrations, klein (~7kb), Bun-nativ.
Drizzle unterstuetzt
| Dialect | Datenbanken |
|---|---|
postgresql | PostgreSQL, Neon, Supabase, PGlite |
mysql | MySQL, MariaDB, PlanetScale, TiDB |
sqlite | SQLite, Turso, Cloudflare D1, Bun SQLite |
Drizzle unterstuetzt NICHT
- MongoDB (Dokument-DB, kein SQL)
- CouchDB, DynamoDB, etc.
3-Stufen Plan
Stufe 1: Jetzt — Zentraler Re-Export
Alle PostgreSQL-spezifischen Imports an eine Stelle. Kein Code im Framework importiert direkt aus drizzle-orm/pg-core.
// packages/framework/src/db/dialect.ts — EINZIGE Stelle mit PG-Imports
// Drizzle PGexport { pgTable as table, serial, text, integer, boolean, timestamp } from "drizzle-orm/pg-core";export { drizzle } from "drizzle-orm/postgres-js";export type { PgTableWithColumns as TableColumns } from "drizzle-orm/pg-core";export type { PgSelect as SelectQuery } from "drizzle-orm/pg-core";
// Connectionexport { default as createClient } from "postgres";
// SQL Specificsexport const dialectConfig = { autoIncrement: "SERIAL", jsonType: "JSONB", booleanType: "BOOLEAN", timestampDefault: "NOW()",} as const;Ueberall sonst:
// VORHER (verstreut):import { pgTable, serial, text } from "drizzle-orm/pg-core";import { drizzle } from "drizzle-orm/postgres-js";import type { PgTableWithColumns } from "drizzle-orm/pg-core";
// NACHHER (zentral):import { table, serial, text, drizzle } from "../db/dialect";import type { TableColumns } from "../db/dialect";Aufwand: Klein. Nur Imports umbiegen. Keine Logik-Aenderung.
Stufe 2: Spaeter — Dialect-Packages fuer SQL
Wenn MySQL/MariaDB Support gebraucht wird:
@kumiko/framework ← Core (importiert aus dialect.ts)@kumiko/dialect-pg ← PostgreSQL (Re-Exports + PG-spezifisches)@kumiko/dialect-mysql ← MySQL/MariaDB (Re-Exports + MySQL-spezifisches)@kumiko/dialect-sqlite ← SQLite (Re-Exports + SQLite-spezifisches)// App waehlt:import { pgDialect } from "@kumiko/dialect-pg";
createApp({ db: { dialect: pgDialect, url: "postgres://..." }, features: [...],});Dialect-Package Interface:
type SqlDialect = { // Drizzle Specifics table: typeof pgTable; // oder mysqlTable, sqliteTable drizzle: (url: string) => DrizzleInstance;
// Typ-Mappings types: { serial: ColumnBuilder; text: ColumnBuilder; integer: ColumnBuilder; boolean: ColumnBuilder; timestamp: ColumnBuilder; json: ColumnBuilder; // PG: jsonb, MySQL: json, SQLite: text };
// SQL Generierung sql: { autoIncrement: string; // PG: "SERIAL", MySQL: "AUTO_INCREMENT" jsonType: string; // PG: "JSONB", MySQL: "JSON" timestampDefault: string; // PG: "NOW()", MySQL: "CURRENT_TIMESTAMP" };
// Testing createTestDb: () => Promise<TestDbHandle>; createTestConnection: (url: string) => DrizzleInstance;};Stufe 3: Zukunft — Non-SQL (MongoDB etc.)
Wenn jemand MongoDB will, braucht es eine hoehere Abstraktion. Der CrudExecutor arbeitet dann nicht mehr direkt mit Drizzle sondern mit einem generischen Interface:
type DbDialect = { connect(url: string): DbConnection;
// CRUD Operationen (abstrakt, DB-agnostisch) insert(table: string, data: Record<string, unknown>): Promise<{ id: number }>; findById(table: string, id: number, tenantId: number): Promise<Record<string, unknown> | null>; findMany(table: string, query: FindManyOptions): Promise<Record<string, unknown>[]>; update(table: string, id: number, tenantId: number, changes: Record<string, unknown>): Promise<Record<string, unknown>>; delete(table: string, id: number, tenantId: number): Promise<void>;
// Schema ensureSchema(name: string, entity: EntityDefinition): Promise<void>;
// Testing createTestDb(): Promise<TestDbHandle>;};@kumiko/dialect-pg ← Drizzle + PostgreSQL (implementiert DbDialect)@kumiko/dialect-mysql ← Drizzle + MySQL (implementiert DbDialect)@kumiko/dialect-sqlite ← Drizzle + SQLite (implementiert DbDialect)@kumiko/dialect-mongo ← MongoDB Native / Mongoose (implementiert DbDialect)Entity-Definitionen sind schon DB-agnostisch:
r.entity("order", { fields: { state: { type: "select", options: [...] }, price: { type: "number" }, },});// PostgreSQL: → SQL Table mit Columns// MongoDB: → Collection mit Document SchemaWICHTIG: Stufe 3 ist bewusst nicht designed. Zu frueh abstrahieren macht alles langsamer und komplizierter. Das Interface oben ist ein Entwurf — die echte API ergibt sich wenn der Use Case da ist.
Was sich NICHT aendert (egal welche DB)
defineFeature()API- Entity-Definitionen (fields, relations)
- Handler-Signaturen
ctx.db(immer tenant-scoped)- Config, Jobs, Delivery, etc.
Die DB ist ein Implementierungsdetail. Der Feature-Code ist DB-agnostisch.
Raw SQL: Escape Hatch
Fuer Features die direkte SQL/DB-Zugriffe brauchen:
// ctx.db.raw — Zugriff auf die native Drizzle/Mongo Connection// Typsicherheit ist dann Sache des Entwicklersconst result = await ctx.db.raw.execute(sql`SELECT ...`);Das ist bewusst raw benannt — man sieht sofort: hier verlasse ich die Abstraktion.
Betroffene Dateien (Stufe 1)
Diese Dateien importieren aktuell direkt aus drizzle-orm/pg-core:
| Datei | Was |
|---|---|
db/table-builder.ts | pgTable, serial, text, integer, boolean, timestamp |
db/crud-executor.ts | PgTableWithColumns |
db/cursor.ts | PgSelect |
pipeline/cascade-handler.ts | PgTableWithColumns |
db/connection.ts | drizzle, postgres |
db/pg-adapter.ts | drizzle, postgres |
testing/index.ts | drizzle, postgres |
files/file-ref-table.ts | pgTable, serial, text, integer, timestamp |
bundled-features/*/table.ts | pgTable, serial, text, integer, timestamp |
Plus Raw SQL mit SERIAL in 7+ Dateien (Tests + Features).
Alle muessen auf den zentralen dialect.ts Import umgestellt werden.