Files
dbtype/docs/architecture
glm-5.1 dd2ec9df3c Add SDD architecture docs for dbtype
Phase 0 architecture specification following the alkdev documentation
pattern from @alkdev/flowgraph. Documents the validated architecture
(UJSX elements → Type.Module → Drizzle hosts) based on e2e probe results.

Docs added:
- README: Project overview, architecture, current state
- architecture/README: Index, design decisions, relationships
- architecture/schema: Type.Module as bundle, construction, serialization
- architecture/hosts: HostConfig per dialect, column mapping, symbolic defaults
- architecture/elements: UJSX element types, props, function components
- architecture/module: Module mechanics, format registration, diffing
- architecture/repo-adapter: from-dbtype operations adapter (phase 2)
- architecture/build-distribution: Package structure, exports
- architecture/open-questions: 10 open questions across all topics
- ADRs 001-005: UJSX as IR, Type.Module, HostConfig, format, repo adapter
2026-05-22 11:34:58 +00:00
..
2026-05-22 11:34:58 +00:00
2026-05-22 11:34:58 +00:00
2026-05-22 11:34:58 +00:00
2026-05-22 11:34:58 +00:00

status, last_updated
status last_updated
draft 2026-05-22

@alkdev/dbtype Architecture

Schema-first multi-dialect database type system. Define your database schema once as a UJSX element tree, validate it with TypeBox, and render it to any Drizzle dialect (SQLite, PostgreSQL, MySQL) — all from a single source of truth.

Why This Exists

Every project that uses Drizzle ORM across multiple backends defines schemas repeatedly: once per dialect (SQLite for local dev, PostgreSQL for production), once again for input validation (TypeBox, Zod, etc.), and once more for API contracts (GraphQL types, OpenAPI schemas). The existing drizzle-typebox only goes one direction (Drizzle → TypeBox) and requires defining schemas in Drizzle's dialect-specific API first.

The gap: there is no schema-first, dialect-agnostic way to define a database schema that simultaneously produces validation schemas, Drizzle table definitions, and (via the operations layer) CRUD operation specs — all from one source of truth.

Core Principle

The element tree is the schema. The module is the bundle. The host is the dialect.

  • The UJSX element tree (<table>, <column>) is the authoring surface — composable, reusable, and TypeBox-validatable
  • The Type.Module is the schema bundle — all tables, relations, and derived schemas in one namespace with Type.Ref resolving cross-table references
  • The HostConfig is the dialect adapter — render the same tree to sqliteTable, pgTable, mysqlTable, or any future target

Relationship to Sibling Packages

Package Relationship
@alkdev/typebox Type system foundation. Type.Module, Type.Ref, Value.Check/Diff/Edit, FormatRegistry
@alkdev/ujsx Element authoring. h(), createComponent, HostConfig, createRoot, reconciler
@alkdev/operations CRUD generation. OperationSpec, OperationRegistry, from-dbtype adapter (future)
@alkdev/pubsub Event transport. Used by operations call protocol
drizzle-orm Peer dependency. Dialect-specific column builders consumed by hosts

Current State

Phase 0: Exploration — Architecture probing complete. Probe scripts in scripts/probe-e2e.ts validate the core architecture. No implementation yet.

What Exists

  • Fork of drizzle-typebox with @alkdev/typebox support (current src/)
  • Research docs in docs/research/ (architecture, type-map, column diffs, typedef kind pattern)
  • E2E probe script validating: UJSX → Type.Module → Drizzle rendering pipeline

What Doesn't Exist Yet

  • Core dbtype package (the implementation)
  • Host configs for SQLite, PostgreSQL, MySQL
  • Schema extraction functions (createSelectSchema, createInsertSchema, createUpdateSchema from element trees)
  • Repo/CRUD generation adapter (from-dbtype for @alkdev/operations)
  • @alkdev/ujsx as a declared dependency (currently dev-only)

Architecture Documents

Document Content
schema.md Type.Module structure, element tree types, column type vocabulary, schema derivation
hosts.md HostConfig implementations per dialect, symbolic defaults, Drizzle rendering
elements.md UJSX element definitions, function components, props schemas, common components
module.md Type.Module as the bundle, incremental construction, serialization, migration diffing
repo-adapter.md from-dbtype adapter for @alkdev/operations, filter/schema generation, access control
build-distribution.md Package structure, sub-path exports, dependencies, tree-shaking
open-questions.md Cross-cutting unresolved questions

Design Decisions

ADR Decision
001 UJSX element tree as the IR, not a separate DbType builder API
002 Type.Module as the schema bundle, not a custom registry
003 HostConfig for dialect rendering, not a transform registry
004 Column formats are annotations, not validators — register explicitly
005 CRUD generation as an operations adapter, not a core feature

Open Questions

All unresolved questions tracked in open-questions.md.

Source Structure (Planned)

src/
  core/
    elements.ts          # h() wrappers, createComponent for table/column/index/fk
    schema.ts            # extractTable, createSelectSchema, createInsertSchema, createUpdateSchema
    module.ts            # buildModule, module construction helpers
    column-types.ts      # DbColumnType union, colToTypeBox mapping
    defaults.ts          # Symbolic default resolution ('now', 'uuid', 'autoincrement')
    guards.ts            # Type guards for element types
  hosts/
    sqlite.ts            # HostConfig for drizzle-orm/sqlite-core
    pg.ts                # HostConfig for drizzle-orm/pg-core
    mysql.ts             # HostConfig for drizzle-orm/mysql-core
  repo/
    from-dbtype.ts       # FromDbType adapter for @alkdev/operations
    filters.ts           # Filter schema generation per column type
    handlers.ts          # CRUD handler generation (findMany, insertOne, etc.)
  index.ts

Key Design Decisions

1. UJSX Element Tree as the IR, Not a Separate Builder API

The architecture docs in docs/research/ proposed a DbTypeBuilder class with methods like DbType.String(), DbType.Table(). The probe results show that UJSX elements (<table>, <column>) with props serve the same purpose — they carry all the metadata (type, notNull, primaryKey, default, references) and compose via function components (IdCol, AuditCols).

A separate builder API would be redundant: both the element tree and the builder produce the same TypeBox + metadata. The element tree is strictly more capable (function components for composition, reconciler for migration diffing in the future) and already exists as @alkdev/ujsx.

Alternative considered: DbTypeBuilder (the research docs pattern). Rejected because it duplicates what UJSX already provides and cannot compose as naturally.

2. Type.Module as the Schema Bundle

All table schemas and their relations live in a single Type.Module call. Type.Ref resolves cross-table references including mutual/circular ones. This eliminates the circular-import problem that the storage_sqlite pattern solves with a separate relations.ts file.

The module is also serializable as JSON Schema with $defs, enabling Value.Diff for schema migration and FromSchema for round-tripping.

Alternative considered: A custom registry with separate schema objects. Rejected because Type.Module already does everything needed (ref resolution, validation, serialization) and doesn't require a new abstraction.

3. HostConfig for Dialect Rendering, Not a Transform Registry

The research docs proposed a TransformRegistry with priority-sorted rules. UJSX's HostConfig serves the same purpose — each dialect is a host, createInstance maps element types to Drizzle column builders, appendChild assembles columns into tables.

This leverages existing infrastructure (createRoot, createComponent, reconciler) rather than building a parallel dispatch system. It also positions the project for future migration support via prepareUpdate/commitUpdate.

Alternative considered: TransformRegistry from docs/research/architecture.md. Rejected because HostConfig is the same pattern with more capabilities (reconciler, function components, context) and already exists in @alkdev/ujsx.

4. Column Formats as Annotations

Type.String({ format: 'uuid' }) and Type.String({ format: 'email' }) are annotations by default in TypeBox. Value.Check does not enforce format unless validators are explicitly registered via FormatRegistry.Set. This is correct JSON Schema behavior.

For dbtype, formats serve as metadata that hosts can use (e.g., the PG host maps format: 'uuid' to uuid() column type, the SQLite host maps it to text() with $defaultFn). Validation is opt-in via format registration.

5. CRUD Generation as an Operations Adapter

The repo pattern (auto-generated CRUD for each table) is not a core feature of dbtype. It's an adapter for @alkdev/operations that consumes the same element tree and module bundle. This keeps dbtype focused on schema definition and rendering, while the operations integration is a separate concern.

Document Lifecycle

From To Condition
draft reviewed All open questions resolved
reviewed stable Implementation complete and verified by tests
stable deprecated Superseded by new architecture

References

  • Research: docs/research/architecture.md, docs/research/typemap-architecture.md, docs/research/dizzle-column-diffs.md, docs/research/typedef-kind-pattern.md
  • Probe results: scripts/probe-e2e.ts
  • Sibling projects: @alkdev/typebox, @alkdev/ujsx, @alkdev/operations, @alkdev/pubsub
  • Reference implementation: @alkdev/drizzle-typebox (current src/), drizzle-graphql (workspace)