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

14 KiB

Complete YAML Entity Schema Specification

Version: 2.0 Date: 2026-02-04 Status: Comprehensive - All Relationship Types + All Features


Entity Declaration

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

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

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

fields:
  role:
    type: enum
    values: [user, admin, moderator]   # Fixed set of allowed values
    default: user

Default Values

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

fields:
  tenantId:
    type: uuid
    index: true                # Create index on this field
    unique_index: true         # Create unique index

Description

fields:
  email:
    type: email
    description: "User's email address for login and notifications"

Indexes

Simple Index

indexes:
  - fields: [email]            # Single-field index

Composite Index

indexes:
  - fields: [tenantId, slug]   # Multi-field index
    unique: true               # Composite unique constraint
    name: tenant_slug          # Optional index name

Named Index

indexes:
  - fields: [createdAt, status]
    name: recent_active

Index Types (Database-Specific)

indexes:
  - fields: [content]
    type: fulltext             # Full-text search index
    name: content_search
  - fields: [location]
    type: spatial              # Spatial/GIS index

Relationships

See RELATIONSHIPS.md for complete relationship specification.

Quick Reference

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

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

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

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

acl:
  publish:                     # Custom operation
    admin: true
  archive:
    moderator: true
  restore:
    admin: true

Validation Rules

Field-Level Validation

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)

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

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

entity: Post
timestamps: true               # Auto-add createdAt/updatedAt

# Equivalent to:
fields:
  createdAt:
    type: bigint
    generated: true
  updatedAt:
    type: bigint
    generated: true

Custom Timestamp Fields

entity: Post
timestamps:
  created: created_at          # Custom field name
  updated: modified_at
  deleted: deleted_at          # For soft deletes

Tenancy

Multi-Tenant

entity: Post
multi_tenant: true             # Auto-add tenantId field

# Equivalent to:
fields:
  tenantId:
    type: uuid
    required: true
    index: true

Tenant Isolation

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

entity: Post
versioning: true               # Add version field for optimistic locking

# Equivalent to:
fields:
  version:
    type: integer
    required: true
    default: 1

Audit Trail

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

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

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

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

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