Red icon indicating copy to clipboard operation
Red copied to clipboard

Implement comprehensive versioned model registry and ClassHOW-based migration system with native :ver<> syntax support

Open Copilot opened this issue 5 months ago • 18 comments

  • [x] Implement basic versioned model registry system
  • [x] Create Red::ModelRegistry with registration and retrieval functions
  • [x] Add comprehensive tests for registry functionality
  • [x] Create integration tests showing migration workflow
  • [x] Add documentation for versioned models usage
  • [x] Review extensive discussion thread for alternative approaches
  • [x] Implement multi-step migration system for zero-downtime migrations
  • [x] Implement trait-based model registration system
  • [x] Add syntactic sugar DSL for migration specifications
  • [x] Integrate Red::AST for type-safe SQL generation
  • [x] Build comprehensive CLI tooling for migration management
  • [x] Support for tables, indexes, constraints, and foreign keys
  • [x] Implement ClassHOW-based migration DSL with auto-generation support
  • [x] Implement ^migration method for CLI-based populate SQL generation
  • [x] Add native :ver<> syntax support for file-per-version organization
  • [x] Remove unnecessary 'is ver' trait and simplify native :ver<> syntax
  • [ ] Evaluate incorporating collection/schema-based versioning approach
  • [ ] Consider integrating AST-based migration enhancements
  • [ ] Explore CompUnit::Repository integration for schema management
  • [ ] Investigate more sophisticated zero-downtime deployment patterns

Enhanced Multi-Step Migration System with ClassHOW DSL and Native :ver<> Support

Implemented a sophisticated 10-phase migration system discussed in issue #15, with comprehensive database operation support, modern Raku patterns, native ClassHOW-based DSL, and native :ver<> syntax support for file-per-version organization.

Core Components:

  • Red::MultiStepMigration - Core migration orchestration with Red::AST integration
  • Red::MigrationManager - High-level migration management with DSL syntax
  • Red::MigrationStatus - Database tracking of migration state
  • Red::Cli::Migration - Command-line tooling for migration management
  • MetamodelX::Red::MigrationHOW - ClassHOW implementation for native Raku migration syntax
  • Red::Migration::DSL - Syntactic sugar DSL with multiple syntax options
  • Trait-based model registration via Red::Traits
  • ^migration method - CLI-focused SQL generation for populate operations
  • Native :ver<> syntax support - Natural Raku versioning for file-per-version organization

File-per-Version Organization (SIMPLIFIED):

# File: lib/User-v1.rakumod
model User:ver<1.0> {
    has Str $.name is column;
}

# File: lib/User-v2.rakumod  
model User:ver<2.0> {
    has Str $.name is column;
    has Str $.email is column;
}

# Usage with smart file loading:
my $user-v1 = require-model-version('User', '1.0');
my $user-v2 = require-model-version('User', '2.0');
$user-v2.^migrate(from => $user-v1);

Latest Changes:

  • Removed unnecessary is ver trait - Native :ver<> syntax is sufficient
  • Enhanced smart file loading - require-model-version now searches predictable file locations
  • Simplified model declarations - No additional traits needed for basic versioning
  • Automatic registration - Models found in predictable locations are auto-registered

10-Phase Migration Process:

  1. BEFORE-START: Initial state
  2. CREATED-TABLES: Create new tables
  3. CREATED-COLUMNS: Add new nullable columns
  4. CREATED-INDEXES: Add performance indexes
  5. POPULATED-COLUMNS: Populate columns and make NOT NULL
  6. UPDATED-CONSTRAINTS: Add foreign keys and check constraints
  7. DELETED-COLUMNS: Remove old columns
  8. DELETED-INDEXES: Remove old indexes
  9. DELETED-TABLES: Remove old tables
  10. COMPLETED: Migration finished

Key Features:

  • Native Raku ClassHOW-based DSL: True language integration using metamodel instead of functions
  • Auto-generation from model differences: Using ^migrate method for automatic migration creation
  • CLI-focused ^migration method: Generates Red::AST specifically for populate operations
  • Native :ver<> syntax: Natural Raku versioning when models are in separate files
  • Smart file loading: Automatic discovery of models in predictable locations
  • Multiple syntax options: Including colon syntax (:type<VARCHAR>, :255size)
  • Red::AST integration: Type-safe SQL generation with fallback to string expressions
  • Comprehensive database operations: Tables, columns, indexes, foreign keys, check constraints
  • CLI tooling: Template generation, status monitoring, safety checks, batch operations
  • Zero-downtime migrations through gradual schema evolution
  • Migration-aware code with handle-migration() function for seamless transitions
  • Automatic fallback behavior during transition phases
  • Safety checks for deployment readiness
  • Comprehensive status tracking and monitoring

Simplified File Organization:

lib/
├── Models/
│   ├── User-v1.rakumod    # model User:ver<1.0> { ... }
│   ├── User-v2.rakumod    # model User:ver<2.0> { ... }
│   └── User-v3.rakumod    # model User:ver<3.0> { ... }
└── Migrations/
    ├── user-v1-to-v2.raku
    └── user-v2-to-v3.raku

^migration Method for CLI Integration:

# CLI generates populate SQL using ^migration method
my $ast = UserV2.^migration(from => UserV1, target-column => "hashed_password");

# Used in migration populate specifications
populate users => { hashed_password => { ast => $ast } };

ClassHOW-Based Migration Syntax:

# Native Raku syntax using ClassHOW
migration user-security-upgrade {
    table users {
        new-column hashed_password { :type<VARCHAR>, :255size }
        new-column is_active { :type<bool>, :default }
        new-indexes :columns["email"], :unique
        populate -> $new, $old {
            $new.hashed_password = "hash:" ~ $old.plain_password
        }
        delete-columns <plain_password>;
    }
}

# Auto-generation from model differences
model UserV1 is model-version('User:1.0') { ... }
model UserV2 is model-version('User:2.0') { ... }
UserV2.^migrate(from => UserV1);  # Automatically generates migration

Simplified Native :ver<> Syntax:

# Recommended: One file per version (no traits needed)
# File: lib/Models/User-v1.rakumod
model User:ver<1.0> {
    has Int $.id is serial;
    has Str $.name is column;
}

# File: lib/Models/User-v2.rakumod  
model User:ver<2.0> {
    has Int $.id is serial;
    has Str $.name is column;
    has Str $.email is column;
}

# Usage - smart loading finds models automatically:
my $user-v1 = require-model-version('User', '1.0');
my $user-v2 = require-model-version('User', '2.0');
$user-v2.^migrate(from => $user-v1);

CLI Integration with ^migration:

# Generate population SQL for specific transformations
my $ast = migration-population-sql(UserV1, UserV2, "hashed_password");

# Auto-generate migration templates with model context
migration-generate "password-hashing", 
    from-model => UserV1, 
    to-model => UserV2;

Traditional Function-Based Syntax (also supported):

# Function-based DSL syntax
migration "user-security-upgrade" => {
    description "Add password hashing and user activation";
    new-columns users => { 
        hashed_password => { type => "VARCHAR(255)" },
        is_active => { type => "BOOLEAN DEFAULT TRUE" }
    };
    new-indexes users => [
        { columns => ["email"], unique => True }
    ];
    populate users => {
        hashed_password => {
            ast => Red::AST::Function.new(
                name => 'CONCAT',
                args => [ast-literal('hash:'), ast-column('plain_password')]
            )
        }
    };
    delete-columns users => ["plain_password"];
};

💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot avatar Sep 09 '25 03:09 Copilot