--- status: stable last_updated: 2026-05-23 --- # Open Questions Tracker Architectural questions for dbtype, organized by theme. Resolved questions reference their ADR. ## Schema & Module ### OQ-01: Should relation entries use a naming convention? - **Origin**: [schema.md](schema.md) - **Status**: Resolved - **Priority**: ~~Medium~~ Resolved - **Context**: Currently `UsersRelations` / `TasksRelations`. Is this naming convention sufficient, or should relations be structured differently? A `relations` field on the table entry would couple relations to table definitions. - **Resolution**: Keep the `FooRelations` naming convention. Separate module entries for relations align with Drizzle's naming convention, stay within Type.Module's flat namespace, and are independently addressable via `M.Import`. A `relations` field on the table entry would break the TypeBox schema model. See [ADR-009](decisions/009-relation-naming-convention.md). - **Cross-references**: OQ-03 ### OQ-02: Should derived schemas live in the module or be extracted separately? - **Origin**: [schema.md](schema.md) - **Status**: Resolved - **Priority**: ~~Low~~ Resolved - **Context**: Insert/update schemas can be added as module entries (`InsertUsers`, `UpdateUsers`) or extracted by walking the module. Adding them to the module increases size but makes everything accessible via `Import`. Extracting separately is more flexible but requires separate code paths. - **Resolution**: Include all derived schemas (insert, update, filter) in the module by default. A `repo_from` interface will generate CRUD operation specs from the module using the operations system, and needs access to all derived schemas. The decision isn't hard to reverse — an `includeDerived: false` opt-out can be added later. See [ADR-010](decisions/010-one-module-per-database.md). - **Cross-references**: OQ-03 ### OQ-03: Should the module support multiple database namespaces? - **Origin**: [schema.md](schema.md) - **Status**: Resolved - **Priority**: ~~Low~~ Resolved - **Context**: One module per database, or one module with all tables across all databases? Probably one per database namespace, but this needs confirmation from real use cases. - **Resolution**: One module per database namespace. This keeps the module's flat namespace clean, avoids table name collisions across databases, and aligns with how Drizzle organizes schemas. See [ADR-010](decisions/010-one-module-per-database.md). ## Element & Host ### OQ-04: Should column elements support nested TypeBox schemas? - **Origin**: [elements.md](elements.md) - **Status**: Resolved - **Priority**: ~~High~~ Resolved - **Context**: The research docs proposed `DbType.String({ notNull: true, inner: Type.String({ format: 'email', maxLength: 255 }) })`. With UJSX elements, this would be ``. The flat props model works for common cases, but custom validation constraints (patterns, ranges, custom checks) may need a nested `inner` prop. - **Resolution**: Yes — flat props for common cases, `inner` as escape hatch for custom validation. When `inner` is provided, `extractTable()` uses it directly instead of calling `colToTypeBox()`. The host ignores `inner` — it's purely for the TypeBox schema. See [ADR-007](decisions/007-inner-escape-hatch.md). ### OQ-05: How to handle PG enum pre-declaration? - **Origin**: [hosts.md](hosts.md) - **Status**: Resolved - **Priority**: ~~High~~ Resolved - **Context**: PG requires `pgEnum()` at module scope before tables that reference it. Options: (A) return both enums and tables from render, (B) start with text for all enums, (C) per-column opt-in with `postgres: { nativeEnum: true }`. - **Resolution**: Option A — the PG host accumulates enums in `ctx.enums` during the render walk. The consumer includes both `ctx.enums` and `ctx.tables` in their Drizzle schema. Enum names follow the `table_column` convention. See [ADR-008](decisions/008-pg-enum-predeclaration.md). ### OQ-06: Should hosts return rendered tables or store them in context? - **Origin**: [hosts.md](hosts.md) - **Status**: Resolved - **Priority**: ~~Medium~~ Resolved - **Context**: The probe scripts use `ctx.tables` to collect rendered tables. A more functional approach would have `render()` return the rendered table directly. Need to resolve this before implementation. - **Resolution**: render() accumulates results in root.ctx (tables, enums). The context shape varies by dialect — SQLite has ctx.tables, PG has ctx.tables and ctx.enums. This is a design clarification confirmed by implementation, not a significant architectural decision requiring its own ADR. See hosts.md Rendering Pipeline. ### OQ-07: Should we support JSX file extensions? - **Origin**: [elements.md](elements.md) - **Status**: Resolved - **Priority**: ~~Low~~ Resolved - **Context**: JSX syntax (`.tsx` with `jsxImportSource: '@alkdev/ujsx'`) would be more ergonomic than `h()` calls. This requires build configuration (TSConfig `jsx`, bundler support). The `h()` API works universally; JSX is a convenience layer. - **Resolution**: Yes — support JSX/TSX as an ergonomic authoring layer. JSX desugars to `h()` calls via `jsxImportSource: '@alkdev/ujsx'`, so the runtime representation is identical. The `h()` API remains the universal fallback. See [ADR-011](decisions/011-jsx-tsx-support.md). ## Repo Adapter ### OQ-08: Per-dialect handler differences? - **Origin**: [repo-adapter.md](repo-adapter.md) - **Status**: Resolved - **Priority**: ~~Medium~~ Resolved - **Context**: PG has `.returning()` on all mutations, MySQL often doesn't. Should the adapter handle this transparently (always try `.returning()`, fall back gracefully), or should it be explicit in the config? - **Resolution**: Always use `.returning()` and gracefully fall back. For dialects that support it (PG, SQLite), return the full result rows. For dialects that don't (MySQL), fall back to returning validated input data (for inserts) or affected row count (for updates/deletes). This keeps the operations API uniform across dialects. See [ADR-012](decisions/012-always-returning-graceful-fallback.md). ### OQ-09: Relation rendering responsibility? - **Origin**: [repo-adapter.md](repo-adapter.md) - **Status**: Resolved - **Priority**: ~~Medium~~ Resolved - **Context**: Should the host render relations (new element type ``), or should the adapter generate them from the module's relation entries? The adapter knows the rendered table objects; the module knows the relation structure. Leaning toward the adapter generating them. - **Resolution**: The adapter generates relation objects from the module's relation entries and the rendered table objects. No new `` element type is needed. The adapter reads `FooRelations` entries from the module, maps them to Drizzle `relations()` calls using the rendered table objects in `ctx.tables`. See [ADR-013](decisions/013-relation-rendering-adapter.md). ## Migration & Diffing ### OQ-10: How far should migration diffing go in phase 1? - **Origin**: [module.md](module.md) - **Status**: Resolved - **Priority**: ~~Low~~ Resolved - **Context**: `Value.Diff` on serialized schemas produces structural edit lists (insert/delete/update property). Translating these to `ALTER TABLE` statements is straightforward for additive changes (new column) but complex for destructive ones (drop column, change type). Phase 1 likely skips migration generation entirely (rely on `drizzle-kit`), but the module structure should not prevent it later. - **Resolution**: Leverage `drizzle-kit` for migrations. dbtype renders to Drizzle table definitions, and `drizzle-kit generate` already handles migration generation from those. No dbtype-native migration generator in phase 1 or the foreseeable future. The module's `Value.Diff` capability remains available as a foundation for future use. See [ADR-014](decisions/014-leverage-drizzle-kit-for-migrations.md). ### OQ-11: Should `` accept `extraConfig` props? - **Origin**: [elements.md](elements.md) - **Status**: Resolved - **Priority**: ~~Low~~ Resolved - **Context**: Drizzle tables accept a third callback argument for indexes and unique constraints defined in terms of column references (e.g., `t => ({ uniqueEmail: unique().on(t.email) })`). The `` element handles most index cases, but composite unique constraints using the Drizzle callback style may not be expressible via ``. How should this map to element props, or is it needed at all? - **Resolution**: Yes — the `
` element accepts an `extraConfig` prop that mirrors Drizzle's third callback argument. It receives the rendered column builders and returns an object for indexes, unique constraints, and other column-reference-based features. This is the escape hatch pattern, consistent with `inner` on ``. See [ADR-015](decisions/015-table-extraconfig-props.md). ### OQ-12: Should the adapter accept the Type.Module bundle or individual schemas? - **Origin**: [repo-adapter.md](repo-adapter.md) - **Status**: Resolved - **Priority**: ~~Low~~ Resolved - **Context**: The adapter needs access to select, insert, update, and filter schemas. Accepting the `Type.Module` compiled bundle is convenient (everything in one place, accessible via `M.Import()`) but couples the adapter to TypeBox's module format. Accepting individual schemas is more flexible but requires more config. With ADR-010 resolving that derived schemas are included in the module, the module bundle approach is the natural default, but the adapter could expose both interfaces. - **Resolution**: Accept the Type.Module compiled bundle. `M.Import()` handles ref resolution and recursion automatically, which eliminates potential complications with separate schema wiring. Consistent with ADR-010 (derived schemas in the module) and requires less configuration. See [ADR-016](decisions/016-adapter-accepts-module-bundle.md). ## Summary Table | ID | Question | Origin | Priority | Status | |----|----------|--------|----------|--------| | OQ-01 | Relation naming convention | schema.md | ~~Medium~~ Resolved | **Resolved** — ADR-009 | | OQ-02 | Derived schemas in module or separate | schema.md | ~~Low~~ Resolved | **Resolved** — ADR-010 | | OQ-03 | Multiple database namespaces | schema.md | ~~Low~~ Resolved | **Resolved** — ADR-010 | | OQ-04 | Nested TypeBox schemas in column props | elements.md | ~~High~~ Resolved | **Resolved** — ADR-007 | | OQ-05 | PG enum pre-declaration | hosts.md | ~~High~~ Resolved | **Resolved** — ADR-008 | | OQ-06 | Host render return value vs context | hosts.md | ~~Medium~~ Resolved | **Resolved** — design clarification | | OQ-07 | JSX file extensions | elements.md | ~~Low~~ Resolved | **Resolved** — ADR-011 | | OQ-08 | Per-dialect handler differences | repo-adapter.md | ~~Medium~~ Resolved | **Resolved** — ADR-012 | | OQ-09 | Relation rendering responsibility | repo-adapter.md | ~~Medium~~ Resolved | **Resolved** — ADR-013 | | OQ-10 | Migration diffing scope | module.md | ~~Low~~ Resolved | **Resolved** — ADR-014 | | OQ-11 | Table extraConfig props | elements.md | ~~Low~~ Resolved | **Resolved** — ADR-015 | | OQ-12 | Module bundle vs. individual schemas | repo-adapter.md | ~~Low~~ Resolved | **Resolved** — ADR-016 |