Tabellen komponieren
wie CSS-Klassen.

Drizzle und Prisma haben TypeScript-Schemas praxistauglich gemacht. Voltro geht den nächsten Schritt: Jeder Querschnittsbelang, den deine Tabellen teilen — Audit-Spalten, Tenant-Isolation, Soft Delete, eigene Versionierung, AI-Auto-Embedding — ist ein komponierbarer Mixin, den du mit .with() anhängst. Eine Zeile pro Belang, kein Codegen-Schritt, vollständig typisiertes Ergebnis.

posts.entity.ts
TypeScript
// database/posts.entity.ts
import { boolean, id, table, text, timestamp } from '@voltro/database'
import { audit } from '@voltro/plugin-audit'
import { tenant } from '@voltro/plugin-multitenancy'
import { softDelete } from '@voltro/plugin-soft-delete'

export const posts = table('posts', {
  id:       id(),                          // typed PostId (TypeID-branded)
  title:    text(),
  body:     text(),
  status:   text().oneOf(['draft', 'published']),
  pinned:   boolean().default(false),
  publishedAt: timestamp().nullable(),
})
  .with(audit(), tenant(), softDelete())   // ← composable mixins
  .index('byTenantStatus', ['tenantId', 'status'])
  .index('byPublished', ['publishedAt'], { where: `"status" = 'published'` })
  .reactive()

// You get for free:
//   - createdAt, updatedAt, createdBy, updatedBy → actors  (audit)
//   - tenantId reference + auto-scope reads + auto-fill   (tenant)
//   - deletedAt, deletedBy + delete()-turns-into-UPDATE   (softDelete)
//   - Reactive matcher hookup                              (.reactive)
//   - Partial index on published posts only                (.index)
//   - Branded PostId                                       (id)

Sechs Features, die mit @voltro/database mitkommen.

Komponierbare Mixins

.with(audit(), tenant(), softDelete()) — drei Zeilen Deklaration, fünfzehn Spalten + Constraints ergänzt. Mixins kennen ihre Abhängigkeiten; softDelete() erfordert transitiv audit(), und der Resolver dedupliziert, wenn beide es erreichen.

Branded TypeIDs standardmäßig

Jedes id() liefert einen brand-typisierten Wert (PostId, UserId, OrgId). Sortierbar nach Erstellzeit, URL-sicher, Präfix aus dem Tabellennamen abgeleitet. Tabellenübergreifende Zuweisung schlägt beim Kompilieren fehl.

Partielle + Expression- + FTS-Indizes

index([cols]) für einfach, index('name', cols, where) für partiell, expressionIndex('name', [expr]) für berechnet, fullTextIndex für FTS. Der Boot-Time-Audit meldet Indizes mit redundantem Präfix, die Schreibkosten verursachen.

Fünf Dialekte, ein Schema

Postgres / MariaDB / MySQL / MSSQL / SQLite — beim Boot über die DB_DIALECT-Env umgeschaltet. Die DSL emittiert das dialektspezifische DDL automatisch; du schreibst das Schema einmal und lässt es überall laufen.

Relationen + Eager Loading

Deklariere 1:1-, 1:N- und N:M-Relationen in einer *.relations.ts-Datei. Ein .with(...)-Aufruf lädt den Baum in EINEM Roundtrip per dialekt-nativer JSON-Aggregation. Pro-Eltern-Limits über korrelierte Subqueries.

Plugin-definierte Mixins

defineMixin(...) liefert Drittanbieter-Mixins (vectorEmbedding, billing.versioned usw.). Sie komponieren neben den eingebauten über dieselbe .with()-Kette.

Die Querschnittsbelange sind die Bug-Fläche.

Die Spalten, die jede Tabelle hat — createdAt, updatedAt, createdBy, deletedAt, tenantId — sind die Spalten, die zu den Bugs deines zweiten Jahres werden, sobald du sie vergisst oder inkonsistent implementierst. Voltro standardisiert sie als Mixins, damit die Implementierung überall korrekt ist oder nirgends — nie an drei Stellen halb richtig.

Die dialektübergreifende Wette:

Voltros DSL zielt auf fünf SQL-Dialekte — Postgres, MariaDB, MySQL, MSSQL, SQLite. Ein Schema, ein Query-Builder, ein Satz Mixins. Ein Wechsel von DB_DIALECT wechselt den DDL-Emitter, den JSON-agg-Compiler, die FTS-Index-Syntax. Postgres bleibt das empfohlene Ziel für native, LISTEN/NOTIFY-getriebene Reaktivität; die anderen sind für Enterprise-Beschaffung und Embedded-Workloads da.

Die Entscheidung lautet "du schreibst Voltro" — nicht "du schreibst Postgres-spezifisches Voltro". Die DSL verbirgt jede Dialekt-Abweichung; EXPLAIN auf deiner heißesten Query bleibt portabel.

Das Schema speist den Rest der Runtime.

Öffne das Framework. Schau es dir selbst an.

Jede Primitive auf dieser Seite ist heute im Framework. Klone den Starter, lass `voltro dev` laufen, in zwei Minuten ist es auf dem Bildschirm.