# 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

### Overview

Der EntityManager ist das zentrale Interface für alle Datenbank-Operationen. Er kombiniert mehrere Patterns:
- **Unit of Work**: Change Tracking und Transaction Management
- **Identity Map**: Vermeidung von Duplikaten
- **Lazy Loading**: Performance-Optimierung durch verzögertes Laden
- **Batch Loading**: N+1 Query Prevention

### Core Features

**Service Classes** (interne Organisation):
- `EntityFinder`: Entity-Suche und Lazy Loading
- `EntityPersister`: Insert, Update, Delete Operationen
- `EntityQueryManager`: QueryBuilder Integration
- `EntityUtilities`: Hilfsmethoden und Profiling

### Basic Operations

#### Finding Entities

```php
use App\Framework\Database\EntityManager;

final readonly class UserService
{
    public function __construct(
        private EntityManager $entityManager
    ) {}

    public function getUserById(UserId $id): ?User
    {
        // Lazy Loading (Standard)
        return $this->entityManager->find(User::class, $id->value);
    }

    public function getUserByIdEager(UserId $id): ?User
    {
        // Eager Loading - lädt Relations sofort
        return $this->entityManager->findEager(User::class, $id->value);
    }

    public function getAllUsers(): array
    {
        return $this->entityManager->findAll(User::class);
    }

    public function findUserByEmail(Email $email): ?User
    {
        return $this->entityManager->findOneBy(
            User::class,
            ['email' => $email->value]
        );
    }
}
```

#### Saving Entities

```php
public function createUser(CreateUserCommand $command): User
{
    $user = User::create(
        id: $this->entityManager->generateId(),
        email: $command->email,
        name: $command->name
    );

    // save() erkennt automatisch INSERT vs UPDATE
    return $this->entityManager->save($user);
}

public function updateUser(User $user, UpdateUserCommand $command): User
{
    // Explizites UPDATE
    $user->updateProfile($command->name);
    return $this->entityManager->update($user);
}
```

#### Batch Operations

```php
public function createMultipleUsers(array $commands): array
{
    $users = [];
    foreach ($commands as $command) {
        $users[] = User::create(
            id: $this->entityManager->generateId(),
            email: $command->email,
            name: $command->name
        );
    }

    // Batch-Insert für bessere Performance
    return $this->entityManager->saveAll(...$users);
}
```

#### Deleting Entities

```php
public function deleteUser(User $user): void
{
    $this->entityManager->delete($user);
}
```

### N+1 Query Prevention

**Problem**: Lazy Loading führt zu N+1 Queries

```php
// ❌ N+1 Problem
$users = $this->entityManager->findAll(User::class);

foreach ($users as $user) {
    // Jede Iteration führt eine separate Query aus (N+1)
    echo $user->getProfile()->bio;
}
```

**Solution**: Batch Loading mit Relations

```php
// ✅ Single Query mit Batch Loading
$users = $this->entityManager->findWithRelations(
    User::class,
    criteria: [],
    relations: ['profile', 'posts'] // Lädt Relations in Batches
);

foreach ($users as $user) {
    // Kein zusätzlicher Query - Profile bereits geladen
    echo $user->getProfile()->bio;
}
```

### Lazy Loading vs Eager Loading

**Lazy Loading** (Standard):
- Relations werden bei Zugriff geladen
- Performance-Vorteil bei ungenutzten Relations
- Potentielles N+1 Problem

```php
$user = $this->entityManager->find(User::class, $id);
// Profile wird erst bei Zugriff geladen:
$bio = $user->getProfile()->bio; // Separate Query
```

**Eager Loading**:
- Relations werden sofort geladen
- Kein N+1 Problem
- Overhead bei ungenutzten Relations

```php
$user = $this->entityManager->findEager(User::class, $id);
// Profile ist bereits geladen - kein zusätzlicher Query
$bio = $user->getProfile()->bio;
```

### Identity Map

Der EntityManager nutzt eine Identity Map zur Vermeidung von Duplikaten:

```php
// Beide Aufrufe liefern die GLEICHE Instanz
$user1 = $this->entityManager->find(User::class, '123');
$user2 = $this->entityManager->find(User::class, '123');

var_dump($user1 === $user2); // true
```

**Vorteile**:
- Keine Duplikate im Speicher
- Konsistente Object Identity
- Automatisches Change Tracking

**Utility Methods**:

```php
// Detach Entity (aus Identity Map entfernen)
$this->entityManager->detach($user);

// Clear Identity Map (z.B. für Batch Processing)
$this->entityManager->clear();

// Identity Map Statistics
$stats = $this->entityManager->getIdentityMapStats();
// Returns: ['total_entities' => 150, 'entities_by_class' => [...]]
```

### Reference Loading

**getReference()** - Lade Entity-Referenz ohne Existenz-Check:

```php
// Erstellt Proxy ohne Database Query
$user = $this->entityManager->getReference(User::class, $userId);

// Query wird erst bei Zugriff ausgeführt
$name = $user->getName(); // Jetzt wird geladen
```

**Use Cases**:
- Foreign Key Relationships
- Performance-kritische Pfade
- Wenn Existenz bereits bekannt ist

### Profiling und Debugging

```php
// Profiling aktivieren
$this->entityManager->setProfilingEnabled(true);

// Operationen ausführen
$users = $this->entityManager->findAll(User::class);

// Profiling-Statistiken abrufen
$stats = $this->entityManager->getProfilingStatistics();

// Profiling-Summary
$summary = $this->entityManager->getProfilingSummary();
// Returns: ProfileSummary mit total_queries, total_time, etc.

// Profiling-Daten löschen
$this->entityManager->clearProfilingData();
```

### Criteria API (Type-Safe Queries)

**Modern**: Verwendung von Criteria statt Arrays

```php
use App\Framework\Database\Criteria\DetachedCriteria;

// Type-safe Criteria
$criteria = DetachedCriteria::forEntity(User::class)
    ->where('email', '=', $email->value)
    ->andWhere('active', '=', true)
    ->orderBy('created_at', 'DESC')
    ->limit(10);

$users = $this->entityManager->findByCriteria($criteria);
$user = $this->entityManager->findOneByCriteria($criteria);
$count = $this->entityManager->countByCriteria($criteria);
```

### QueryBuilder Integration

Für komplexe Queries:

```php
// QueryBuilder für Entity-Klasse
$queryBuilder = $this->entityManager->createQueryBuilderFor(User::class);

$users = $queryBuilder
    ->select('*')
    ->where('email LIKE ?', ['%@example.com'])
    ->andWhere('created_at > ?', ['2024-01-01'])
    ->orderBy('name', 'ASC')
    ->limit(50)
    ->fetchAll();

// QueryBuilder für Table (ohne Entity)
$qb = $this->entityManager->createQueryBuilderForTable('users');
```

### Domain Events Integration

```php
// Domain Event aufzeichnen
$this->entityManager->recordDomainEvent($user, new UserRegisteredEvent($user));

// Events für Entity dispatchen
$this->entityManager->dispatchDomainEventsForEntity($user);

// Alle Events dispatchen
$this->entityManager->dispatchAllDomainEvents();

// Event-Statistiken
$stats = $this->entityManager->getDomainEventStats();
// Returns: ['total_events' => 23, 'events_by_entity' => [...]]
```

## Repository Pattern

### Overview

Das Repository Pattern abstrahiert Daten-Zugriff und bietet domain-spezifische Query-Methoden. Das Framework nutzt **Composition over Inheritance**.

### Base EntityRepository

**Framework-Service** für Common Operations:

```php
namespace App\Framework\Database\Repository;

final readonly class EntityRepository
{
    public function __construct(
        private EntityManager $entityManager
    ) {}

    public function find(string $entityClass, string $id): ?object
    public function findAll(string $entityClass): array
    public function findBy(string $entityClass, array $criteria, ...): array
    public function findOneBy(string $entityClass, array $criteria): ?object
    public function save(object $entity): object
    public function delete(object $entity): void
    public function transaction(callable $callback): mixed

    // Batch Loading (N+1 Prevention)
    public function findWithRelations(...): array

    // Pagination
    public function findPaginated(...): PaginatedResult

    // Batch Operations
    public function saveBatch(array $entities, int $batchSize = 100): array
    public function deleteBatch(array $entities, int $batchSize = 100): void
}
```

### Domain Repository Pattern

**Composition-basiert** statt Inheritance:

```php
namespace App\Domain\User\Repositories;

use App\Framework\Database\Repository\EntityRepository;

final readonly class UserRepository
{
    public function __construct(
        private EntityRepository $entityRepository
    ) {}

    public function findById(UserId $id): ?User
    {
        return $this->entityRepository->find(User::class, $id->value);
    }

    public function findByEmail(Email $email): ?User
    {
        return $this->entityRepository->findOneBy(
            User::class,
            ['email' => $email->value]
        );
    }

    public function findActiveUsers(): array
    {
        return $this->entityRepository->findBy(
            User::class,
            criteria: ['active' => true],
            orderBy: ['created_at' => 'DESC']
        );
    }

    public function save(User $user): User
    {
        return $this->entityRepository->save($user);
    }

    public function delete(User $user): void
    {
        $this->entityRepository->delete($user);
    }

    // Domain-spezifische Methode mit Batch Loading
    public function findUsersWithProfiles(array $userIds): array
    {
        return $this->entityRepository->findWithRelations(
            User::class,
            criteria: ['id' => $userIds],
            relations: ['profile', 'settings']
        );
    }

    // Pagination
    public function findPaginated(int $page, int $limit = 20): PaginatedResult
    {
        return $this->entityRepository->findPaginated(
            User::class,
            page: $page,
            limit: $limit,
            orderBy: ['created_at' => 'DESC']
        );
    }
}
```

### Repository Best Practices

**✅ Composition Over Inheritance**:
```php
// ✅ Framework Pattern - Composition
final readonly class OrderRepository
{
    public function __construct(
        private EntityRepository $entityRepository
    ) {}
}

// ❌ Avoid - Inheritance
class OrderRepository extends BaseRepository
{
    // Problematisch: Tight coupling
}
```

**✅ Domain-Specific Methods**:
```php
// ✅ Sprechende Domain-Methoden
public function findOverdueOrders(): array
{
    return $this->entityRepository->findBy(
        Order::class,
        ['status' => 'pending'],
        orderBy: ['due_date' => 'ASC']
    );
}

// ❌ Generic Methods außerhalb Domain
public function findBy(array $criteria): array
{
    // Zu generisch - nutze EntityRepository direkt
}
```

**✅ N+1 Prevention**:
```php
// ✅ Batch Loading für Relations
public function findOrdersWithItems(array $orderIds): array
{
    return $this->entityRepository->findWithRelations(
        Order::class,
        criteria: ['id' => $orderIds],
        relations: ['items', 'customer'] // Lädt in Batches
    );
}
```

## Unit of Work Pattern

### Overview

Der Unit of Work Pattern verwaltet Entities und deren Änderungen für transaktionale Konsistenz.

**Key Features**:
- **Change Tracking**: Automatische Änderungserkennung
- **Transactional Consistency**: Atomic Commits/Rollbacks
- **Batch Operations**: Optimierte Bulk-Operationen
- **Entity States**: NEW, MANAGED, DIRTY, DELETED, DETACHED

### Entity Lifecycle

```php
use App\Framework\Database\UnitOfWork\UnitOfWork;

final readonly class OrderService
{
    public function __construct(
        private EntityManager $entityManager
    ) {}

    public function createOrder(CreateOrderCommand $command): Order
    {
        $unitOfWork = $this->entityManager->unitOfWork;

        // 1. Create new entity (state: NEW)
        $order = Order::create($command);

        // 2. Persist marks entity for INSERT (state: NEW → MANAGED)
        $unitOfWork->persist($order);

        // 3. Flush writes to database (state: MANAGED)
        $unitOfWork->flush();

        // 4. Commit transaction
        $unitOfWork->commit();

        return $order;
    }
}
```

### Change Tracking

**Automatic Change Detection**:

```php
$unitOfWork = $this->entityManager->unitOfWork;

// Load entity (state: MANAGED)
$user = $this->entityManager->find(User::class, $userId);

// Modify entity
$user->updateProfile($newName);

// Change Tracking erkennt Änderung automatisch
$unitOfWork->flush(); // UPDATE query wird generiert
$unitOfWork->commit();
```

**Manual Change Tracking**:

```php
// Entity aus externer Quelle (state: DETACHED)
$user = unserialize($serializedUser);

// Merge in Unit of Work (state: DETACHED → MANAGED)
$managedUser = $unitOfWork->merge($user);

// Änderungen werden getrackt
$unitOfWork->flush();
```

### Transaction Management

**Explicit Transactions**:

```php
public function processOrder(Order $order): void
{
    $unitOfWork = $this->entityManager->unitOfWork;

    try {
        $unitOfWork->beginTransaction();

        // Multiple operations
        $order->confirm();
        $unitOfWork->persist($order);

        $this->inventoryService->reserve($order->items);
        $this->paymentService->charge($order->total);

        // Write changes to database
        $unitOfWork->flush();

        // Commit transaction
        $unitOfWork->commit();
    } catch (\Exception $e) {
        $unitOfWork->rollback();
        throw $e;
    }
}
```

**Implicit Transactions (Auto-Commit)**:

```php
// Auto-commit ist standardmäßig aktiviert
$unitOfWork->persist($entity); // Automatisch committed
```

**Disable Auto-Commit** für Batch Operations:

```php
$unitOfWork->setAutoCommit(false);

try {
    $unitOfWork->beginTransaction();

    foreach ($orders as $order) {
        $unitOfWork->persist($order); // Kein Auto-Commit
    }

    $unitOfWork->flush();   // Batch-Write
    $unitOfWork->commit();  // Atomic Commit
} catch (\Exception $e) {
    $unitOfWork->rollback();
    throw $e;
} finally {
    $unitOfWork->setAutoCommit(true);
}
```

### Entity States

```php
use App\Framework\Database\UnitOfWork\EntityState;

// Check Entity State
$state = $unitOfWork->getEntityState($entity);

match ($state) {
    EntityState::NEW => 'Entity marked for INSERT',
    EntityState::MANAGED => 'Entity being tracked, no changes',
    EntityState::DIRTY => 'Entity modified, pending UPDATE',
    EntityState::DELETED => 'Entity marked for DELETE',
    EntityState::DETACHED => 'Entity not managed by UnitOfWork',
};

// Check if entity is managed
if ($unitOfWork->contains($entity)) {
    // Entity is managed (NEW, MANAGED, DIRTY, or DELETED)
}
```

### Detaching Entities

```php
// Detach entity from Unit of Work
$unitOfWork->detach($user);

// Entity state: DETACHED
// Changes to entity are no longer tracked
```

**Use Cases**:
- Long-running processes
- Serialization/Caching
- Read-only operations
- Memory management in batch processing

### Bulk Operations

```php
// Bulk Insert
$unitOfWork->bulkInsert($entities); // Optimized batch INSERT

// Bulk Update
$unitOfWork->bulkUpdate($entities); // Optimized batch UPDATE

// Bulk Delete
$unitOfWork->bulkDelete($entities); // Optimized batch DELETE
```

## Query Optimization

### N+1 Query Problem

**Erkennnung**: Jede Iteration führt zusätzliche Query aus

```php
// ❌ N+1 Problem
$orders = $this->entityManager->findAll(Order::class); // 1 Query

foreach ($orders as $order) {
    echo $order->getCustomer()->name; // N Queries (1 pro Order)
}
// Total: 1 + N Queries
```

### Solution: Batch Loading

```php
// ✅ Batch Loading
$orders = $this->entityManager->findWithRelations(
    Order::class,
    criteria: [],
    relations: ['customer', 'items'] // Preload relations
);

foreach ($orders as $order) {
    echo $order->getCustomer()->name; // Kein Query - bereits geladen
}
// Total: 2-3 Queries (1 für Orders, 1-2 für Relations)
```

### Eager vs Lazy Loading

**Strategic Loading**:

```php
// Heavy Operation - Eager Loading
public function getOrderDetails(OrderId $id): OrderDetails
{
    $order = $this->entityManager->findEager(Order::class, $id->value);
    // Alle Relations sofort geladen
    return OrderDetails::fromOrder($order);
}

// Light Operation - Lazy Loading
public function listOrders(): array
{
    // Relations werden nur bei Bedarf geladen
    return $this->entityManager->findAll(Order::class);
}
```

### Pagination

```php
use App\Framework\Database\Repository\EntityRepository;

public function getUsers(int $page): PaginatedResult
{
    return $this->entityRepository->findPaginated(
        User::class,
        page: $page,
        limit: 20,
        orderBy: ['created_at' => 'DESC']
    );
}

// PaginatedResult enthält:
// - items: array (Entities)
// - total: int (Total count)
// - page: int (Current page)
// - limit: int (Items per page)
// - totalPages: int (Calculated)
```

### Query Caching

```php
// EntityManager mit Cache
$entityManager = new EntityManager(
    // ...
    cacheManager: $cacheManager
);

// Find verwendet Cache automatisch
$user = $entityManager->find(User::class, $id);
// Beim zweiten Aufruf: Cache Hit
```

## Connection Pooling

### Overview

Connection Pooling verbessert Performance durch Wiederverwendung von Datenbankverbindungen.

**Features**:
- Min/Max Connection Limits
- Connection Health Monitoring
- Automatic Retry mit Exponential Backoff
- Idle Connection Cleanup
- Connection Warmup

### Configuration

```php
use App\Framework\Database\Config\PoolConfig;

$poolConfig = new PoolConfig(
    minConnections: 2,        // Minimum pool size
    maxConnections: 10,       // Maximum pool size
    maxIdleTime: 300,         // Idle timeout (seconds)
    healthCheckInterval: 60,  // Health check frequency
    enableWarmup: true        // Pre-create min connections
);
```

### Usage

```php
use App\Framework\Database\ConnectionPool;

$pool = new ConnectionPool($driverConfig, $poolConfig, $timer);

// Get connection from pool
$connection = $pool->getConnection();

try {
    // Use connection
    $result = $connection->query('SELECT * FROM users');
} finally {
    // Return connection to pool (automatic with PooledConnection)
    $connection->release();
}
```

### Health Monitoring

```php
// Pool automatically monitors connection health
$pool->performHealthCheck();

// Get pool statistics
$stats = $pool->getPoolStatistics();
// Returns:
[
    'current_connections' => 5,
    'max_connections' => 10,
    'connections_in_use' => 3,
    'healthy_connections' => 5,
    'total_created' => 12,
    'total_destroyed' => 7
]
```

### Retry Logic

```php
// Automatic retry with exponential backoff
$retryStrategy = ExponentialBackoffStrategy::create()
    ->withMaxAttempts(3)
    ->withBaseDelay(Duration::fromMilliseconds(100));

// Connection pool uses retry automatically
$connection = $pool->getConnectionWithRetry($retryStrategy);
```

### Production Configuration

```php
// Production Pool Config
$productionPoolConfig = new PoolConfig(
    minConnections: 5,         // Keep 5 connections ready
    maxConnections: 50,        // Scale up to 50 under load
    maxIdleTime: 600,          // 10 minutes idle timeout
    healthCheckInterval: 30,   // Check every 30 seconds
    enableWarmup: true,        // Pre-warm connections
    connectionTimeout: 5000    // 5 second timeout
);
```

## Transaction Management

### Basic Transactions

```php
// EntityManager Transaction Helper
$this->entityManager->transaction(function (EntityManager $em) {
    $order = Order::create($command);
    $em->save($order);

    $this->inventoryService->reserve($order->items);
    $this->paymentService->charge($order->total);

    // Automatic commit on success, rollback on exception
});
```

### Manual Transaction Control

```php
$unitOfWork = $this->entityManager->unitOfWork;

$unitOfWork->beginTransaction();

try {
    // Operations
    $unitOfWork->persist($entity1);
    $unitOfWork->persist($entity2);

    $unitOfWork->flush();
    $unitOfWork->commit();
} catch (\Exception $e) {
    $unitOfWork->rollback();
    throw $e;
}
```

### Nested Transactions (Savepoints)

```php
$unitOfWork->beginTransaction();

try {
    $order = Order::create($command);
    $unitOfWork->persist($order);

    // Nested transaction (savepoint)
    $unitOfWork->beginTransaction();
    try {
        $this->inventoryService->reserve($order->items);
        $unitOfWork->commit(); // Commit savepoint
    } catch (\Exception $e) {
        $unitOfWork->rollback(); // Rollback to savepoint
        throw $e;
    }

    $unitOfWork->commit(); // Commit main transaction
} catch (\Exception $e) {
    $unitOfWork->rollback(); // Rollback main transaction
    throw $e;
}
```

### Transaction Isolation Levels

```php
use App\Framework\Database\TransactionIsolation;

$unitOfWork->beginTransaction(
    isolation: TransactionIsolation::READ_COMMITTED
);

// Available isolation levels:
// - READ_UNCOMMITTED (lowest isolation)
// - READ_COMMITTED (default)
// - REPEATABLE_READ
// - SERIALIZABLE (highest isolation)
```

### Transaction Best Practices

**✅ Short Transactions**:
```php
// ✅ Keep transactions short
$this->entityManager->transaction(fn(EntityManager $em) =>
    $em->save($entity)
);
```

**❌ Long Transactions**:
```php
// ❌ Avoid long-running transactions
$this->entityManager->transaction(function (EntityManager $em) {
    $this->sendEmail($user); // External I/O - BAD
    $this->processHeavyComputation(); // CPU-intensive - BAD
    $em->save($entity);
});
```

**✅ Batch with Periodic Commits**:
```php
// ✅ Batch processing with periodic commits
$unitOfWork->setAutoCommit(false);
$batchSize = 100;

for ($i = 0; $i < count($items); $i++) {
    $unitOfWork->persist($items[$i]);

    if (($i + 1) % $batchSize === 0) {
        $unitOfWork->flush();
        $unitOfWork->commit();
        $unitOfWork->beginTransaction();
    }
}

$unitOfWork->flush();
$unitOfWork->commit();
$unitOfWork->setAutoCommit(true);
```

## 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
