Files
2026-03-09 22:30:41 +00:00

710 lines
14 KiB
Markdown

# Complete YAML Entity Schema Specification
**Version**: 2.0
**Date**: 2026-02-04
**Status**: Comprehensive - All Relationship Types + All Features
---
## Entity Declaration
```yaml
entity: EntityName
version: "1.0"
description: "Entity purpose"
package: package_name # Optional - which package owns this entity
table: custom_table_name # Optional - override default table name
```
---
## Field Types
### Primitive Types
| Type | Description | Prisma | SQL | MongoDB |
|------|-------------|--------|-----|---------|
| `uuid` | UUID v4 | String | UUID | String |
| `cuid` | CUID (Collision-resistant UID) | String | VARCHAR | String |
| `string` | Short text (< 255 chars) | String | VARCHAR | String |
| `text` | Long text (unlimited) | String | TEXT | String |
| `email` | Email address | String | VARCHAR | String |
| `url` | URL | String | VARCHAR | String |
| `integer` | 32-bit integer | Int | INTEGER | Number |
| `bigint` | 64-bit integer | BigInt | BIGINT | Long |
| `float` | Floating point | Float | FLOAT | Double |
| `decimal` | Fixed-point decimal | Decimal | DECIMAL | Decimal128 |
| `boolean` | True/false | Boolean | BOOLEAN | Boolean |
| `timestamp` | Unix timestamp (milliseconds) | BigInt | BIGINT | Date |
| `date` | Date only (YYYY-MM-DD) | DateTime | DATE | Date |
| `datetime` | Date + time | DateTime | TIMESTAMP | Date |
| `json` | JSON object | Json | JSONB | Object |
| `enum` | Enum with fixed values | String | ENUM | String |
| `bytes` | Binary data | Bytes | BYTEA | Binary |
### Special Types
| Type | Description | Example |
|------|-------------|---------|
| `slug` | URL-friendly identifier | `my-article-title` |
| `phone` | Phone number | `+1-555-0123` |
| `currency` | Money amount (uses Decimal) | `19.99` |
| `color` | Hex color code | `#FF5733` |
| `coordinates` | Lat/long (uses Json) | `{lat: 37.7749, lng: -122.4194}` |
---
## Field Attributes
### Required Attributes
```yaml
fields:
id:
type: uuid
primary: true # Is this the primary key?
required: true # Can this field be null?
unique: true # Must values be unique?
generated: true # Auto-generated by database?
nullable: false # Explicit nullable (opposite of required)
```
### Constraints
```yaml
fields:
username:
type: string
min_length: 3 # Minimum string length
max_length: 50 # Maximum string length
pattern: "^[a-zA-Z0-9_]+$" # Regex validation
min: 0 # Minimum number value
max: 100 # Maximum number value
```
### Enum Values
```yaml
fields:
role:
type: enum
values: [user, admin, moderator] # Fixed set of allowed values
default: user
```
### Default Values
```yaml
fields:
status:
type: string
default: draft # Default value for new records
isActive:
type: boolean
default: true
viewCount:
type: integer
default: 0
metadata:
type: json
default: {}
```
### Index Hints
```yaml
fields:
tenantId:
type: uuid
index: true # Create index on this field
unique_index: true # Create unique index
```
### Description
```yaml
fields:
email:
type: email
description: "User's email address for login and notifications"
```
---
## Indexes
### Simple Index
```yaml
indexes:
- fields: [email] # Single-field index
```
### Composite Index
```yaml
indexes:
- fields: [tenantId, slug] # Multi-field index
unique: true # Composite unique constraint
name: tenant_slug # Optional index name
```
### Named Index
```yaml
indexes:
- fields: [createdAt, status]
name: recent_active
```
### Index Types (Database-Specific)
```yaml
indexes:
- fields: [content]
type: fulltext # Full-text search index
name: content_search
- fields: [location]
type: spatial # Spatial/GIS index
```
---
## Relationships
See [RELATIONSHIPS.md](./RELATIONSHIPS.md) for complete relationship specification.
### Quick Reference
```yaml
relations:
# Belongs to (N:1)
author:
type: belongs-to
entity: User
foreign_key: authorId
on_delete: cascade # cascade | set_null | restrict | no_action
on_update: cascade
# Has many (1:N)
posts:
type: has-many
entity: Post
foreign_key: authorId
cascade_delete: true
# Many-to-many (M:N)
roles:
type: many-to-many
entity: Role
through: UserRole
foreign_key: userId
target_key: roleId
# Self-referential
parent:
type: belongs-to
entity: Category
foreign_key: parentId
nullable: true
# Polymorphic
commentable:
type: polymorphic
foreign_key: commentableId
type_key: commentableType
entities: [Post, Video, Product]
```
---
## Access Control (ACL)
### Simple ACL
```yaml
acl:
create:
public: true # Anyone can create
read:
user: true # Authenticated users can read
update:
admin: true # Only admins can update
delete:
admin: true
```
### Row-Level Security
```yaml
acl:
read:
self: true # User can read own records
row_level: "userId = $user.id"
update:
self: true
row_level: "userId = $user.id OR role = 'admin'"
delete:
admin: true
moderator: true
row_level: "authorId = $user.id"
```
### Role Hierarchy
```yaml
acl:
create:
user: true # user, moderator, admin, god can create
read:
public: true # Everyone (including unauthenticated)
update:
moderator: true # moderator, admin, god can update
delete:
admin: true # admin, god can delete
```
### Custom Permissions
```yaml
acl:
publish: # Custom operation
admin: true
archive:
moderator: true
restore:
admin: true
```
---
## Validation Rules
### Field-Level Validation
```yaml
fields:
email:
type: email
validation:
- rule: email # Built-in email validation
message: "Invalid email address"
- rule: regex
pattern: "^[^@]+@[^@]+\\.[^@]+$"
message: "Must be a valid email format"
age:
type: integer
validation:
- rule: range
min: 18
max: 120
message: "Age must be between 18 and 120"
username:
type: string
validation:
- rule: length
min: 3
max: 20
- rule: regex
pattern: "^[a-zA-Z0-9_]+$"
message: "Only letters, numbers, and underscores allowed"
- rule: blacklist
values: [admin, root, system]
message: "Username is reserved"
```
### Built-in Validation Rules
| Rule | Parameters | Description |
|------|------------|-------------|
| `required` | - | Field must have a value |
| `email` | - | Valid email format |
| `url` | - | Valid URL format |
| `length` | min, max | String length range |
| `range` | min, max | Number range |
| `regex` | pattern | Regular expression match |
| `enum` | values | Must be one of values |
| `unique` | - | Value must be unique |
| `blacklist` | values | Value cannot be in list |
| `whitelist` | values | Value must be in list |
---
## Hooks (Lifecycle Events)
```yaml
hooks:
beforeCreate:
- validate_email # Function name in runtime
- generate_slug
afterCreate:
- send_welcome_email
- create_profile
beforeUpdate:
- validate_changes
afterUpdate:
- invalidate_cache
beforeDelete:
- check_dependencies
afterDelete:
- cleanup_files
```
---
## Soft Deletes
```yaml
entity: Post
soft_delete: true # Enable soft deletes
deleted_at_field: deletedAt # Field name (default: deletedAt)
fields:
deletedAt:
type: bigint
nullable: true
description: "Timestamp when record was soft-deleted"
```
When `soft_delete: true`:
- `delete()` sets `deletedAt` to current timestamp
- `list()` automatically filters out deleted records
- `restore()` operation clears `deletedAt`
---
## Timestamps
### Auto Timestamps
```yaml
entity: Post
timestamps: true # Auto-add createdAt/updatedAt
# Equivalent to:
fields:
createdAt:
type: bigint
generated: true
updatedAt:
type: bigint
generated: true
```
### Custom Timestamp Fields
```yaml
entity: Post
timestamps:
created: created_at # Custom field name
updated: modified_at
deleted: deleted_at # For soft deletes
```
---
## Tenancy
### Multi-Tenant
```yaml
entity: Post
multi_tenant: true # Auto-add tenantId field
# Equivalent to:
fields:
tenantId:
type: uuid
required: true
index: true
```
### Tenant Isolation
```yaml
entity: Post
multi_tenant: true
tenant_field: organizationId # Custom tenant field name
tenant_isolation: strict # strict | soft | none
```
**Isolation Levels**:
- `strict`: All queries automatically filter by tenantId, cannot be disabled
- `soft`: Default filter by tenantId, but can be overridden
- `none`: tenantId field exists but no automatic filtering
---
## Versioning
### Optimistic Locking
```yaml
entity: Post
versioning: true # Add version field for optimistic locking
# Equivalent to:
fields:
version:
type: integer
required: true
default: 1
```
### Audit Trail
```yaml
entity: Post
audit_trail: true # Track all changes
# Creates PostHistory table with:
# - historyId (primary key)
# - postId (FK to Post)
# - changes (JSON of field changes)
# - changedBy (userId)
# - changedAt (timestamp)
# - operation (create/update/delete)
```
---
## Caching
```yaml
entity: User
cache:
enabled: true
ttl: 3600 # Cache lifetime in seconds
strategy: write-through # write-through | write-behind | cache-aside
key_pattern: "user:{id}" # Redis key pattern
```
---
## Computed Fields
```yaml
entity: User
computed:
fullName:
type: string
expression: "firstName || ' ' || lastName"
description: "Computed from firstName + lastName"
age:
type: integer
expression: "EXTRACT(YEAR FROM AGE(CURRENT_DATE, birthDate))"
```
---
## Polymorphic Types
```yaml
entity: Media
polymorphic_type: true # This entity can be referenced polymorphically
fields:
id: {type: uuid, primary: true}
type: {type: enum, values: [image, video, audio]}
url: {type: string, required: true}
```
---
## Complete Example
```yaml
entity: BlogPost
version: "2.0"
description: "Blog post with comments and tags"
package: blog_forge
table: blog_posts # Custom table name
timestamps: true # Auto createdAt/updatedAt
soft_delete: true # Enable soft deletes
multi_tenant: true # Add tenantId
versioning: true # Optimistic locking
audit_trail: true # Track changes
fields:
id:
type: cuid
primary: true
generated: true
title:
type: string
required: true
max_length: 200
description: "Post title"
validation:
- rule: length
min: 10
max: 200
slug:
type: slug
unique: true
generated: true
description: "URL-friendly identifier"
content:
type: text
required: true
validation:
- rule: length
min: 100
message: "Content must be at least 100 characters"
status:
type: enum
values: [draft, published, archived]
default: draft
index: true
authorId:
type: uuid
required: true
index: true
categoryId:
type: cuid
nullable: true
viewCount:
type: integer
default: 0
min: 0
publishedAt:
type: bigint
nullable: true
metadata:
type: json
default: {}
indexes:
- fields: [slug]
unique: true
- fields: [status, publishedAt]
name: published_posts
- fields: [authorId, createdAt]
name: author_recent
relations:
author:
type: belongs-to
entity: User
foreign_key: authorId
on_delete: restrict
category:
type: belongs-to
entity: Category
foreign_key: categoryId
nullable: true
on_delete: set_null
comments:
type: has-many
entity: Comment
foreign_key: postId
cascade_delete: true
tags:
type: many-to-many
entity: Tag
through: PostTag
foreign_key: postId
target_key: tagId
acl:
create:
user: true
read:
public: true
update:
self: true
row_level: "authorId = $user.id"
moderator: true
delete:
self: true
row_level: "authorId = $user.id"
admin: true
publish:
moderator: true
hooks:
beforeCreate:
- generate_slug
- set_published_date
afterCreate:
- notify_subscribers
beforeUpdate:
- validate_status_transition
afterUpdate:
- invalidate_cache
beforeDelete:
- archive_content
cache:
enabled: true
ttl: 1800
strategy: write-through
computed:
readTime:
type: integer
expression: "LENGTH(content) / 200"
description: "Estimated read time in minutes"
```
---
## Migration from Prisma YAML
Since C++ DBAL can now generate Prisma schema from YAML, the existing `prisma/schema.yaml` is redundant.
**Before** (dual maintenance):
```
dbal/shared/api/schema/entities/*.yaml ← Source of truth
prisma/schema.yaml ← Manually kept in sync
prisma/schema.prisma ← Generated from schema.yaml
```
**After** (single source):
```
dbal/shared/api/schema/entities/*.yaml ← ONLY source of truth
/tmp/dbal-prisma/schema.prisma ← Auto-generated by C++ DBAL
```
**Action**: Delete `prisma/schema.yaml` and `prisma/codegen/` scripts. Use C++ DBAL generator instead.
---
## Future Features (TODO)
1. **Database-Specific Extensions**:
- PostgreSQL: Array types, JSONB operators, full-text search
- MongoDB: Embedded documents, geospatial queries
- MySQL: Full-text indexes, spatial types
2. **Advanced Validation**:
- Cross-field validation (`endDate > startDate`)
- Async validation (unique check via API)
- Custom validation functions
3. **Triggers**:
- Database-level triggers
- Application-level event handlers
4. **Sharding**:
- Horizontal partitioning by tenant
- Range/hash partitioning strategies
5. **Graph Queries**:
- Recursive CTEs for tree structures
- Graph traversal operations