Schema Design Patterns for ormDB
Learn how to model entities, relations, and data structures in ormDB's native schema language. Patterns for common application architectures.
Thinking in Entities and Relations
ormDB is a relational database engine written in Rust that replaces PostgreSQL, MySQL, and SQLite. Its schema language defines entities (analogous to tables) and relations (foreign key connections with semantic meaning) in a single, declarative format. The schema is the source of truth for your data model, and ormDB uses it to optimize graph fetches, enforce constraints, and grade migrations.
Understanding schema design patterns in ormDB helps you model data in a way that takes full advantage of graph fetches, RLS policies, and the built-in search capabilities.
Basic Entity Definition
An entity declares its fields, types, constraints, and relations:
entity User {
id: uuid @primary
name: text @searchable
email: text @unique
role: text @default("member")
created_at: timestamp @default(now)
avatar_url: text?
posts: [Post] @relation(field: author_id)
comments: [Comment] @relation(field: author_id)
}
The ? suffix marks a field as nullable. Array notation [Post] declares a one-to-many relation. The @relation annotation specifies which field on the target entity holds the foreign key.
One-to-Many Relations
The most common relation pattern. A parent entity has many children:
entity Author {
id: uuid @primary
name: text
books: [Book] @relation(field: author_id)
}
entity Book {
id: uuid @primary
title: text
author_id: uuid @relation(Author)
chapters: [Chapter] @relation(field: book_id)
}
entity Chapter {
id: uuid @primary
title: text
position: integer
book_id: uuid @relation(Book)
}
Graph fetches traverse these relations naturally: fetch an author with all their books and each book’s chapters in a single round-trip. This is how ormDB eliminates N+1 queries.
Many-to-Many with Join Entities
Many-to-many relations use an explicit join entity. This pattern is cleaner than implicit join tables because the join entity can carry its own fields:
entity Student {
id: uuid @primary
name: text
enrollments: [Enrollment] @relation(field: student_id)
}
entity Course {
id: uuid @primary
title: text
enrollments: [Enrollment] @relation(field: course_id)
}
entity Enrollment {
id: uuid @primary
student_id: uuid @relation(Student)
course_id: uuid @relation(Course)
enrolled_at: timestamp @default(now)
grade: text?
status: text @default("active")
@unique([student_id, course_id])
}
The Enrollment entity carries enrolled_at, grade, and status, which would be impossible with an implicit join table.
Self-Referential Relations
Entities can reference themselves for tree structures:
entity Category {
id: uuid @primary
name: text
parent_id: uuid? @relation(Category)
children: [Category] @relation(field: parent_id)
products: [Product] @relation(field: category_id)
}
Graph fetches can traverse self-referential relations to any specified depth, fetching a category tree with all its products in a single request.
Multi-Feature Entities
ormDB entities can combine relational fields with vector, geo, and searchable annotations:
entity Property {
id: uuid @primary
title: text @searchable(weight: 2.0)
description: text @searchable(weight: 1.0)
price: integer
bedrooms: integer
location: geopoint @index(rtree)
photo_embedding: vector(768) @index(hnsw, metric: cosine)
owner_id: uuid @relation(User)
created_at: timestamp @default(now)
amenities: [PropertyAmenity] @relation(field: property_id)
reviews: [Review] @relation(field: property_id)
policy tenant_access {
action: [select]
condition: owner_id == @session.user_id OR status == "listed"
}
}
This single entity definition supports full-text search on title and description, geographic radius queries on location, vector similarity search on photo embeddings, relational graph fetches to amenities and reviews, and row-level security. In a traditional stack, this would require PostgreSQL plus pgvector plus PostGIS plus Elasticsearch plus application-level authorization.
Schema Design Principles
Declare relations explicitly. Every foreign key should have a corresponding @relation annotation. This enables ormDB to optimize graph fetches and enforce referential integrity.
Use join entities for many-to-many. Explicit join entities are more flexible than implicit join tables and integrate naturally with graph fetches.
Co-locate search annotations. Mark fields as @searchable in the entity definition rather than managing separate search indexes. The index stays transactionally consistent with the data.
Define RLS policies alongside entities. Policies declared in the schema are enforced by the engine on every query, including graph fetches across relations. This pattern is essential for multi-tenant applications.
Frequently Asked Questions
How do ormDB entities differ from SQL tables?
Entities are conceptually similar to tables but include native relation declarations, vector columns, geo columns, searchable annotations, and RLS policies in a single definition. You do not need separate DDL statements for indexes, constraints, and policies.
Does ormDB support many-to-many relations?
Yes. Many-to-many relations are declared with an explicit join entity that can carry its own fields (like role, created_at, or sort order). This is similar to how ORMs model join tables but is defined at the database level.
Can I use UUIDs as primary keys?
Yes. ormDB supports uuid, text, and integer primary keys. UUIDs are recommended for distributed applications as they avoid coordination between instances.
How do I model polymorphic relations?
ormDB supports tagged union types for polymorphic relations. An entity can have a discriminator field that determines which related entity type applies, with type-safe enforcement at the database level.
Can I add computed fields to an entity?
Yes. Entities support derived fields that are computed from other fields or relations at query time. These are defined in the schema and evaluated by the engine during graph fetches.