# Database Patterns

Umfassende Dokumentation der Database-Patterns im Custom PHP Framework.

## Migration System: Safe Rollback Architecture

Das Framework verwendet ein intelligentes Migration-System, das zwischen sicheren und unsicheren Rollbacks unterscheidet.

### Core Concepts

**Forward-Only by Default**: Alle Migrations sind standardmäßig forward-only (nur `up()` Methode erforderlich).

**Optional Safe Rollback**: Migrations, die SICHER rückgängig gemacht werden können (ohne Datenverlust), implementieren zusätzlich das `SafelyReversible` Interface.

### Migration Interface

```php
/**
 * Base migration interface - Forward-only by default
 */
interface Migration
{
    public function up(ConnectionInterface $connection): void;
    public function getVersion(): MigrationVersion;
    public function getDescription(): string;
}
```

### SafelyReversible Interface

```php
/**
 * Optional interface for migrations that support safe rollback
 *
 * ONLY implement this if rollback is safe (no data loss)!
 */
interface SafelyReversible
{
    public function down(ConnectionInterface $connection): void;
}
```

### Safe vs Unsafe Migrations

**✅ Safe for Rollback** (implement SafelyReversible):
- Creating new tables (can be dropped)
- Adding nullable columns (can be removed)
- Creating/dropping indexes (no data affected)
- Renaming columns (data preserved)
- Adding/removing foreign keys (constraints only)

**❌ Unsafe for Rollback** (only Migration):
- Dropping columns with data
- Transforming data formats
- Changing column types (data loss risk)
- Merging/splitting tables
- Deleting data

### Example: Safe Rollback

```php
use App\Framework\Database\Migration\{Migration, SafelyReversible};

/**
 * Safe: New table can be dropped without data loss
 */
final readonly class CreateSessionsTable implements Migration, SafelyReversible
{
    public function up(ConnectionInterface $connection): void
    {
        $schema = new Schema($connection);
        $schema->create('sessions', function ($table) {
            $table->string('id', 255)->primary();
            $table->text('data');
        });
        $schema->execute();
    }

    public function down(ConnectionInterface $connection): void
    {
        $schema = new Schema($connection);
        $schema->dropIfExists('sessions');
        $schema->execute();
    }
}
```

### Example: Unsafe Migration

```php
/**
 * UNSAFE: Dropping column with data - NOT reversible
 */
final readonly class RemoveLegacyIdColumn implements Migration
{
    public function up(ConnectionInterface $connection): void
    {
        $schema = new Schema($connection);
        $schema->table('users', function ($table) {
            $table->dropColumn('legacy_id');  // Data is LOST!
        });
        $schema->execute();
    }

    // NO down() method - data cannot be recovered
}
```

### Rollback Safety Check

Der `MigrationRunner` prüft automatisch, ob eine Migration sicher rollbar ist:

```bash
$ php console.php db:rollback 1

🔄 Rolling back migrations...
⚠️  Safety Check: Only migrations implementing SafelyReversible will be rolled back.

❌ Rollback failed: Migration 2024_12_19_150000 does not support safe rollback

ℹ️  This migration cannot be safely rolled back.
   Reason: Data loss would occur during rollback.

💡 Recommendation:
   Create a new forward migration to undo the changes instead:
   php console.php make:migration FixYourChanges

📖 See docs/claude/examples/migrations/SafeVsUnsafeMigrations.md for guidelines.
```

### Fix-Forward Strategy

Statt unsichere Rollbacks: Neue Forward-Migration erstellen.

```php
// Original migration (dropped column)
final readonly class RemoveDeprecatedField implements Migration
{
    public function up(ConnectionInterface $connection): void
    {
        $schema = new Schema($connection);
        $schema->table('users', function ($table) {
            $table->dropColumn('deprecated_field');
        });
        $schema->execute();
    }
}

// Fix-Forward migration (restore column)
final readonly class RestoreDeprecatedField implements Migration, SafelyReversible
{
    public function up(ConnectionInterface $connection): void
    {
        $schema = new Schema($connection);
        $schema->table('users', function ($table) {
            $table->string('deprecated_field')->nullable();
        });
        $schema->execute();
    }

    public function down(ConnectionInterface $connection): void
    {
        // Safe: Column is empty after restoration
        $schema = new Schema($connection);
        $schema->table('users', function ($table) {
            $table->dropColumn('deprecated_field');
        });
        $schema->execute();
    }
}
```

### Best Practices

1. **Default to Forward-Only**: Nur `Migration` implementieren, außer du bist SICHER, dass Rollback safe ist
2. **Document Why Safe**: Kommentiere, warum eine Migration `SafelyReversible` ist
3. **Test Rollback**: Teste `up()` → `down()` → `up()` Cycle in Development
4. **Production: Forward-Only**: Auch "safe" Rollbacks sollten in Production vermieden werden
5. **See Examples**: Vollständige Guidelines in `docs/claude/examples/migrations/SafeVsUnsafeMigrations.md`

---

## Database Value Objects

Das Framework verwendet Value Objects für alle Database-Identifier, um Type Safety und SQL-Injection-Prevention zu gewährleisten.

### Verfügbare Database Value Objects

**Core Database VOs** (`src/Framework/Database/ValueObjects/`):

- **TableName**: Validierte Tabellennamen mit optionalem Schema-Prefix
- **ColumnName**: Validierte Spaltennamen
- **IndexName**: Validierte Index-Namen
- **ConstraintName**: Validierte Constraint-Namen (Foreign Keys, Check Constraints)
- **DatabaseName**: Validierte Datenbank-Namen
- **SchemaName**: Validierte Schema-Namen (PostgreSQL)

### Value Object Patterns

Alle Database VOs folgen einheitlichen Patterns:

```php
// Immutable readonly classes
final readonly class TableName
{
    public function __construct(
        public string $value,
        public ?SchemaName $schema = null
    ) {
        $this->validate();
    }

    // Factory Method Pattern
    public static function fromString(string $value): self
    {
        return new self($value);
    }

    // __toString() für String-Interpolation
    public function __toString(): string
    {
        return $this->schema
            ? "{$this->schema}.{$this->value}"
            : $this->value;
    }

    // Equality Comparison
    public function equals(self $other): bool
    {
        return $this->value === $other->value
            && $this->schema?->equals($other->schema ?? null);
    }
}
```

### Schema Builder Integration

Der Schema Builder unterstützt sowohl VOs als auch Strings (Union Types) für Backwards Compatibility:

```php
use App\Framework\Database\Schema\Blueprint;
use App\Framework\Database\ValueObjects\{TableName, ColumnName, IndexName};

// ✅ Modern: Value Objects (Type Safe)
$schema->create(TableName::fromString('users'), function (Blueprint $table) {
    $table->string(ColumnName::fromString('email'), 255);
    $table->unique(ColumnName::fromString('email'), IndexName::fromString('uk_users_email'));
});

// ✅ Legacy: Strings (Backwards Compatible)
$schema->create('users', function (Blueprint $table) {
    $table->string('email', 255);
    $table->unique('email', 'uk_users_email');
});

// ✅ Mixed: Kombiniert möglich
$schema->create('users', function (Blueprint $table) {
    $table->string('email', 255); // String
    $table->unique(
        ColumnName::fromString('email'),  // VO
        'uk_users_email'                  // String
    );
});
```

### Variadic Parameters

Command-Methoden nutzen variadic parameters statt Arrays für natürlichere API:

```php
// ✅ Variadic Parameters - Natural
$table->dropColumn('email', 'phone', 'address');

// ❌ Array Parameters - Clunky
$table->dropColumn(['email', 'phone', 'address']);

// ✅ Auch mit VOs
$table->dropColumn(
    ColumnName::fromString('email'),
    ColumnName::fromString('phone')
);
```

### TableName mit Schema-Support

PostgreSQL unterstützt Schema-Prefixe:

```php
use App\Framework\Database\ValueObjects\{TableName, SchemaName};

// Simple table name
$table = TableName::fromString('users');
// Output: "users"

// With schema prefix
$table = new TableName(
    value: 'users',
    schema: SchemaName::fromString('public')
);
// Output: "public.users"

// Schema extraction
if ($table->hasSchema()) {
    echo $table->schema->value; // "public"
}
```

### Validation Rules

Alle Database VOs validieren ihre Eingaben:

**TableName Validation**:
- Keine leeren Strings
- Alphanumerisch + Underscore
- Beginnt nicht mit Ziffer
- Max. 63 Zeichen (PostgreSQL Limit)
- Reservierte Keywords verboten (siehe `RESERVED_KEYWORDS`)

**ColumnName Validation**:
- Keine leeren Strings
- Alphanumerisch + Underscore
- Beginnt nicht mit Ziffer
- Max. 63 Zeichen
- Reservierte Keywords verboten

**IndexName/ConstraintName**:
- Gleiche Rules wie ColumnName
- Prefix-Conventions: `idx_`, `uk_`, `fk_`, `ck_` empfohlen

**Example Validation**:
```php
// ✅ Valid
TableName::fromString('users');
TableName::fromString('user_profiles');

// ❌ Invalid - Exception geworfen
TableName::fromString('');           // Leer
TableName::fromString('123users');   // Beginnt mit Ziffer
TableName::fromString('user-table'); // Ungültiges Zeichen
TableName::fromString('select');     // Reserviertes Keyword
```

## Migration Best Practices

### Migration mit Database VOs

Neue Migrationen können VOs verwenden für zusätzliche Type Safety:

```php
use App\Framework\Database\Migration\Migration;
use App\Framework\Database\Schema\{Blueprint, Schema};
use App\Framework\Database\ValueObjects\{TableName, ColumnName, IndexName, ConstraintName};
use App\Framework\Database\Schema\ForeignKeyAction;

final class CreateUserProfilesTable implements Migration
{
    public function up(Schema $schema): void
    {
        $schema->create(TableName::fromString('user_profiles'), function (Blueprint $table) {
            // Primary Key
            $table->string(ColumnName::fromString('ulid'), 26)
                ->primary();

            // Foreign Key
            $table->string(ColumnName::fromString('user_id'), 26);

            // Relationship
            $table->foreign(ColumnName::fromString('user_id'))
                ->references(ColumnName::fromString('ulid'))
                ->on(TableName::fromString('users'))
                ->onDelete(ForeignKeyAction::CASCADE);

            // Index
            $table->index(
                ColumnName::fromString('user_id'),
                IndexName::fromString('idx_user_profiles_user_id')
            );

            // Composite Index mit variadic parameters
            $table->index(
                ColumnName::fromString('user_id'),
                ColumnName::fromString('created_at'),
                IndexName::fromString('idx_user_profiles_lookup')
            );

            // Timestamps
            $table->timestamps();
        });
    }

    public function down(Schema $schema): void
    {
        $schema->dropIfExists(TableName::fromString('user_profiles'));
    }
}
```

### Migration Naming Conventions

**Index Names**:
```php
// Pattern: idx_{table}_{columns}
IndexName::fromString('idx_users_email')
IndexName::fromString('idx_users_email_active')

// Unique constraints: uk_{table}_{columns}
IndexName::fromString('uk_users_email')

// Composite
IndexName::fromString('idx_orders_user_created')
```

**Foreign Key Constraints**:
```php
// Pattern: fk_{table}_{referenced_table}
ConstraintName::fromString('fk_user_profiles_users')
ConstraintName::fromString('fk_orders_users')
```

**Check Constraints**:
```php
// Pattern: ck_{table}_{column}_{condition}
ConstraintName::fromString('ck_users_age_positive')
ConstraintName::fromString('ck_orders_total_min')
```

### Backwards Compatibility

Bestehende Migrationen funktionieren ohne Änderungen:

```php
// ✅ Legacy-Code bleibt voll funktionsfähig
$schema->create('users', function (Blueprint $table) {
    $table->string('email');
    $table->unique('email', 'uk_users_email');
});

// Union Types ermöglichen schrittweise Migration
// string|TableName, string|ColumnName, etc.
```

### Drop Operations

Variadic parameters machen Drop-Operationen natürlich:

```php
$schema->table('users', function (Blueprint $table) {
    // Drop multiple columns
    $table->dropColumn('old_field', 'deprecated_field', 'unused_field');

    // Drop foreign key by constraint name
    $table->dropForeign('fk_users_company');

    // Drop foreign key by column (finds constraint automatically)
    $table->dropForeign('company_id');

    // Drop index by name
    $table->dropIndex('idx_users_email');

    // Drop index by columns (finds index automatically)
    $table->dropIndex('email', 'active');
});
```

## EntityManager Usage

TODO: Document EntityManager and UnitOfWork pattern

## Repository Pattern

TODO: Document repository implementation and usage

## Query Optimization

TODO: Document N+1 prevention and batch loading

## Connection Pooling

TODO: Document connection pool configuration

## Transaction Management

TODO: Document transaction patterns and best practices

## Database Testing

### Testing mit Database VOs

```php
use App\Framework\Database\ValueObjects\{TableName, ColumnName};

it('creates table with value objects', function () {
    $tableName = TableName::fromString('test_users');

    $this->schema->create($tableName, function (Blueprint $table) {
        $table->string(ColumnName::fromString('email'));
    });

    expect($this->schema->hasTable($tableName))->toBeTrue();
});

it('validates table name format', function () {
    TableName::fromString('123invalid'); // Should throw
})->throws(\InvalidArgumentException::class);
```

### Test Cleanup

```php
afterEach(function () {
    // Cleanup mit VOs
    $this->schema->dropIfExists(TableName::fromString('test_users'));
    $this->schema->dropIfExists(TableName::fromString('test_profiles'));
});
```

## Performance Considerations

### Value Object Overhead

Database VOs haben minimalen Performance-Overhead:

- **Creation**: ~0.01ms pro VO (Validation einmalig)
- **String Conversion**: ~0.001ms via `__toString()`
- **Memory**: ~200 bytes pro VO Instance
- **Recommendation**: VOs sind für alle Database Operations akzeptabel

### Caching Strategies

```php
// VO Instances können gecached werden
final class TableNameCache
{
    private static array $cache = [];

    public static function get(string $name): TableName
    {
        return self::$cache[$name] ??= TableName::fromString($name);
    }
}

// Usage
$users = TableNameCache::get('users');
$profiles = TableNameCache::get('user_profiles');
```

## Migration Pattern Recommendations

### Simple Tables (String-basiert OK)

Für einfache Tabellen ohne komplexe Constraints:

```php
$schema->create('simple_logs', function (Blueprint $table) {
    $table->id();
    $table->string('message');
    $table->timestamps();
});
```

### Complex Tables (VOs empfohlen)

Für Tabellen mit Relationships, Constraints, Composite Indexes:

```php
use App\Framework\Database\ValueObjects\{TableName, ColumnName, IndexName, ConstraintName};

$schema->create(TableName::fromString('complex_orders'), function (Blueprint $table) {
    // Type Safety für alle Identifier
    $table->string(ColumnName::fromString('ulid'), 26)->primary();
    $table->string(ColumnName::fromString('user_id'), 26);

    $table->foreign(ColumnName::fromString('user_id'))
        ->references(ColumnName::fromString('ulid'))
        ->on(TableName::fromString('users'))
        ->onDelete(ForeignKeyAction::CASCADE);

    // Composite indexes mit explicit naming
    $table->index(
        ColumnName::fromString('user_id'),
        ColumnName::fromString('status'),
        ColumnName::fromString('created_at'),
        IndexName::fromString('idx_orders_user_status_created')
    );
});
```

## SQL Injection Prevention

Database VOs bieten eingebauten Schutz:

```php
// ✅ VOs validieren Input - SQL Injection unmöglich
$tableName = TableName::fromString($_GET['table']); // Wirft Exception bei Injection-Versuch

// ❌ Raw strings sind gefährlich
$query = "SELECT * FROM {$_GET['table']}"; // SQL Injection möglich!

// ✅ VOs in Queries nutzen
$query = "SELECT * FROM {$tableName}"; // Validated and safe
```

## Framework Compliance

Database VOs folgen allen Framework-Prinzipien:

- ✅ **Readonly Classes**: Alle VOs sind `final readonly`
- ✅ **Immutability**: Keine State-Mutation nach Construction
- ✅ **No Inheritance**: `final` classes, composition over inheritance
- ✅ **Value Objects**: Keine Primitive Obsession
- ✅ **Type Safety**: Union Types für Backwards Compatibility
- ✅ **Framework Integration**: `__toString()` für seamless integration
