Domain Types Reference
Reepolee defines a canonical vocabulary of column types — domain types — that every new table and column should draw from. Rather than each table inventing its own width for a name or its own precision for money, you pick the domain type that matches the column's meaning and get a consistent SQL type across your schema.
The types are defined as typed constants in config/domain_types/mysql.ts and
config/domain_types/sqlite.ts. A few diverge per dialect (noted below); the
rest use the same SQL definition in both files. The generator reads these
constants to produce correct column metadata, and the suffix/prefix conventions
in config/db_structure.ts (DATE_SUFIXES, DATETIME_SUFIXES, BOOLEAN_PREFIXES,
IMAGE_SUFFIXES, FILE_SUFFIXES) tell the generator how to recognise each
column's role without manual tagging.
Primary Keys & Identifiers
| Domain type | MySQL | SQLite | Notes |
|---|---|---|---|
pk_id | INT UNSIGNED AUTO_INCREMENT | INTEGER PRIMARY KEY | Canonical primary key. Migrate INT (signed) and BIGINT UNSIGNED to this |
uuid_v7 | BINARY(16) | BLOB | Time-ordered UUID (RFC 9562). Generate with Bun.randomUUIDv7Bytes() for DB storage |
foreign_key | INT UNSIGNED | INTEGER | FK columns; type matches the referenced pk_id |
Basic SQL Types
Generic type aliases (not business domains) used by Studio's column picker and default table scaffolding. Reachable via exact column-name match.
| Domain type | MySQL | SQLite |
|---|---|---|
integer | INT | INTEGER |
unsigned_integer | INT UNSIGNED | INTEGER |
bigint | BIGINT | INTEGER |
real | DOUBLE | REAL |
numeric | DECIMAL(18,6) | NUMERIC |
decimal | DECIMAL(10, 2) | DECIMAL(18, 2) |
float | FLOAT | REAL |
double | DOUBLE | REAL |
varchar | VARCHAR(255) | VARCHAR(255) |
char | CHAR(1) | CHAR(1) |
binary | BINARY(16) | BLOB |
blob | BLOB | BLOB |
longtext | LONGTEXT | TEXT |
datetime | DATETIME | DATETIME |
json | JSON | JSON |
Names
| Domain type | SQL | Purpose |
|---|---|---|
first_name | VARCHAR(100) | Person's given name |
last_name | VARCHAR(100) | Person's surname |
full_name | VARCHAR(255) | Concatenated person or organization display name |
username | VARCHAR(50) | Login handle |
Text & Descriptions
| Domain type | SQL | Purpose |
|---|---|---|
short_description | VARCHAR(255) | Brief label, tagline, module description |
long_description | TEXT | Fuller comment, description, note |
short_text | VARCHAR(255) | Short free-form text value |
text | TEXT | Unbounded long text (JSON blobs, translations, contract clauses, session data) |
markdown | TEXT COMMENT 'MARKDOWN' (MySQL) / MARKDOWN (SQLite) | Rich text authored and stored as Markdown. Generates a <markdown-editor> form field |
slug | VARCHAR(255) | URL-safe identifier derived from a title or name |
url | VARCHAR(2048) | Full external or website link |
Monetary, Percent & Quantities
| Domain type | SQL | Purpose |
|---|---|---|
amount | DECIMAL(18,2) | All monetary values. Migrate DECIMAL(10,2) → this |
percentage | DECIMAL(7,4) | Percentage / commission rates. Migrate DECIMAL(4,2), DECIMAL(5,2), DECIMAL(10,3) → this |
quantity | DECIMAL(12,3) | Order/inventory quantity, whole or fractional (e.g. 2 pcs, 0.5 liter). Unit lives in a separate column |
currency_code | CHAR(3) (MySQL) / VARCHAR(3) (SQLite) | ISO 4217 currency code (e.g. USD, EUR) |
Temporal
| Domain type | SQL (MySQL) | SQL (SQLite) | Suffix convention |
|---|---|---|---|
date | DATE | TEXT | _on, _by (e.g. incorporated_on, payment_due_by) |
timestamp | TIMESTAMP | TIMESTAMP | _at (e.g. created_at, verified_at). Default CURRENT_TIMESTAMP |
SQLite stores date as ISO-8601 TEXT (YYYY-MM-DD) rather than a native
DATE affinity.
Config constants: DATE_SUFFIXES = ["_on", "_by"], DATETIME_SUFFIXES = ["_at"].
Duration & Time Units
| Domain type | SQL | Purpose |
|---|---|---|
days | INTEGER | Duration/count measured in days |
months | INTEGER | Duration/count measured in months |
years | INTEGER | Duration/count measured in years |
hours | INTEGER | Duration/count measured in hours |
minutes | INTEGER | Duration/count measured in minutes |
Boolean
| Domain type | SQL (MySQL) | SQL (SQLite) | Detection |
|---|---|---|---|
boolean | TINYINT(1) | INTEGER | Prefix is_, has_, can_ OR any TINYINT(1) column |
Config constant: BOOLEAN_PREFIXES = ["is_", "has_", "can_"].
Contact
| Domain type | SQL | Purpose |
|---|---|---|
email | VARCHAR(255) | Email address |
phone_number | VARCHAR(30) | E.164 international standard |
locale | VARCHAR(10) | BCP-47 language/region tag (en-US) |
Codes & Identifiers
| Domain type | SQL | Examples |
|---|---|---|
code_short | VARCHAR(5) | ISO country codes, currency codes |
code | VARCHAR(20) | Lookup codes (equipment, articles, languages) |
code_long | VARCHAR(64) | Machine-generated tokens (scope keys, invitation codes) |
status_enum | VARCHAR(30) | State-machine status names |
sku | VARCHAR(64) | Product/inventory stock-keeping unit |
gtin | VARCHAR(14) | Global Trade Item Number — EAN-8/13, UPC-A, GTIN-14 barcode |
tax_id | VARCHAR(30) | VAT/company tax identifier (format varies by country) |
Address
| Domain type | SQL | Notes |
|---|---|---|
street_line_1 | VARCHAR(150) | Primary street address |
street_line_2 | VARCHAR(100) | Apartment, suite, floor |
city | VARCHAR(100) | City or municipality |
state_province | VARCHAR(100) | Regional subdivision |
postal_code | VARCHAR(20) | Postal/ZIP code |
country_code | CHAR(3) (MySQL) / VARCHAR(3) (SQLite) | ISO 3166-1 alpha-2 / alpha-3 |
country_name | VARCHAR(100) | Full display country name |
Security & System
| Domain type | SQL | Purpose |
|---|---|---|
password_hash | VARCHAR(255) | Argon2id / PHC formatted password hash |
ip_address | VARCHAR(45) | IPv4/IPv6 address stored as text |
json_data | JSON (MySQL) / TEXT (SQLite) | Serialized structured application data |
Media
| Domain type | SQL | Suffix convention |
|---|---|---|
image_path | VARCHAR(512) | _image (e.g. portrait_image, logo_image) |
file_path | VARCHAR(512) | _file (e.g. contract_file, invoice_file) |
image_path stores a browsable path (e.g. /images/teams/members/xyz.webp),
not the binary. Rendered as <image-upload> in forms and image_thumbnail() in
grids. file_path stores a browsable document path; rendered as <file-upload>
in forms and file_link() in grids.
Config constants: IMAGE_SUFFIXES = ["_image"], FILE_SUFFIXES = ["_file"].
How Domain Types Are Used
Column-name matching
When you name a column first_name, the generator matches it against the domain
type registry and picks up the correct SQL type, form widget, and display
formatting. You don't need to annotate columns — the name alone drives the
match.
Suffix and prefix conventions
Columns ending in _at are recognised as timestamps, _on and _by as dates,
is_ / has_ / can_ as booleans, _image as image paths, and _file as
file paths. These conventions live in config/db_structure.ts and the generator
uses them to produce the right form control and grid cell for each column.
Comment-driven overrides
For columns that need a specific form widget regardless of their domain type, add a MySQL column comment. Two formats are supported:
- Plain word — e.g.
COMMENT 'autocomplete',COMMENT 'textarea',COMMENT 'markdown' - JSON — e.g.
COMMENT '{type: "autocomplete", rows: 6}'. JSON takes precedence over plain-word when both are present.
SQLite uses column type naming for the same purpose (MARKDOWN type → markdown
editor).
Auditing
Run the Check domain compliance tool from reeman to audit your schema against this taxonomy:
bun reeman
# → Tools & Maintenance → Check domain compliance
It reports columns whose types deviate from the canonical domain types so you
can plan migrations. The check reads config/domain_types/ at runtime — the
same constants the generator uses — so there's no second source of truth to
maintain.
Extending
The domain type registry is your project's own source file. To add a
project-specific type, edit config/domain_types/mysql.ts and
config/domain_types/sqlite.ts together:
// config/domain_types/mysql.ts
export const DOMAIN_TYPES = {
// ... existing types ...
npi_number: "VARCHAR(10)", // US National Provider Identifier
} as const;
New types are immediately available to generators, Studio, and the compliance checker. Keep the two dialect files in sync — the TypeScript compiler will catch a missing key, but not a different SQL value for the same key.