home / skills / duc01226 / easyplatform / migration

migration skill

/.claude/skills/migration

This skill guides you to create and run idempotent database migrations following EasyPlatform conventions across EF Core, MongoDB, and PlatformDataMigration

npx playbooks add skill duc01226/easyplatform --skill migration

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
6.1 KB
---
name: migration
description: "[Implementation] ⚡⚡ Create or run database migrations"
argument-hint: [add <name> | update | list | rollback | migration-description]
infer: true
---

# Migration: $ARGUMENTS

Create or run database migrations following EasyPlatform patterns.

## Summary

**Goal:** Create idempotent data or schema migrations following EasyPlatform conventions (EF Core, PlatformDataMigration, MongoDB).

| Step | Action | Key Notes |
|------|--------|-----------|
| 1 | Analyze requirements | Identify migration type: EF Core schema, data, or MongoDB |
| 2 | Design migration | Plan rollback, paging for large datasets, performance |
| 3 | Generate migration | Follow `YYYYMMDDHHMMSS_Name` naming convention |
| 4 | Verify | Idempotent, paged, proper error handling, `OnlyForDbsCreatedBeforeDate` set |
| 5 | Wait for approval | Present design before creating files |

**Key Principles:**
- Use paging (200-500 page size) for large datasets with `dismissSendEvent: true`
- Migrations must be idempotent (safe to run multiple times)
- Never create files without explicit user approval

## Quick Commands (db-migrate)

Parse `$ARGUMENTS` for quick operations:
- `add <name>` → Create new EF Core migration
- `update` → Apply pending migrations
- `list` → List all migrations and status
- `rollback` → Revert last migration
- No argument or description → Create new migration (proceed to Phase 1)

**Database providers:**
- SQL Server: `PlatformExampleApp.TextSnippet.Persistence`
- PostgreSQL: `PlatformExampleApp.TextSnippet.Persistence.PostgreSql`
- MongoDB: Uses `PlatformMongoMigrationExecutor` (code-based, runs on startup)
  - Location: `*.Persistence.Mongo/Migrations/`

**EF Core quick commands:**
```bash
# Add migration
cd src/Backend/PlatformExampleApp.TextSnippet.Persistence
dotnet ef migrations add <MigrationName> --startup-project ../PlatformExampleApp.TextSnippet.Api

# Apply migrations
dotnet ef database update --startup-project ../PlatformExampleApp.TextSnippet.Api

# List migrations
dotnet ef migrations list --startup-project ../PlatformExampleApp.TextSnippet.Api
```

**Safety:** Warn before applying to production. Show pending changes. Recommend backup before destructive operations.

---

## Phase 1: Analyze Requirements

1. **Parse migration description** from: $ARGUMENTS
2. **Identify migration type:**
    - Schema migration (EF Core) - for SQL Server/PostgreSQL table changes
    - Data migration (PlatformDataMigrationExecutor) - for data transformations
    - MongoDB migration (PlatformMongoMigrationExecutor) - for MongoDB changes

3. **Search for existing patterns:**
    - `src/Backend/*/Persistence/Migrations/` for schema migrations
    - Search for `PlatformDataMigrationExecutor` implementations

## Phase 2: Design Migration

1. **Determine affected entities and tables**
2. **Plan rollback strategy if applicable**
3. **Consider data volume and performance:**
    - Use paging for large datasets (PageSize: 200-500)
    - Set `dismissSendEvent: true` if entity events not needed
    - Use `checkDiff: false` for bulk updates

## Phase 3: Generate Migration

Follow naming convention: `YYYYMMDDHHMMSS_MigrationName`

**For EF Core Schema Migration:**

```bash
dotnet ef migrations add MigrationName --project src/Backend/Service}.Persistence
```

**For Data Migration (SQL Server/PostgreSQL):**

```csharp
public class YYYYMMDDHHMMSS_MigrationName : PlatformDataMigrationExecutor<{Service}DbContext>
{
    public override string Name => "YYYYMMDDHHMMSS_MigrationName";
    public override DateTime? OnlyForDbsCreatedBeforeDate => new(YYYY, MM, DD);
    public override bool AllowRunInBackgroundThread => true;

    public override async Task Execute({Service}DbContext dbContext)
    {
        var queryBuilder = repository.GetQueryBuilder(q => q.Where(FilterExpr()));
        await RootServiceProvider.ExecuteInjectScopedPagingAsync(
            maxItemCount: await repository.CountAsync(q => queryBuilder(q)),
            pageSize: 200,
            ExecutePaging,
            queryBuilder);
    }

    private static async Task<List<Entity>> ExecutePaging(
        int skip, int take,
        Func<IQueryable<Entity>, IQueryable<Entity>> qb,
        IRepo<Entity> repo,
        IPlatformUnitOfWorkManager uow)
    {
        using var unitOfWork = uow.Begin();
        var items = await repo.GetAllAsync(q => qb(q).OrderBy(e => e.Id).Skip(skip).Take(take));
        // Apply transformations
        await repo.UpdateManyAsync(items, dismissSendEvent: true, checkDiff: false);
        await unitOfWork.CompleteAsync();
        return items;
    }
}
```

**For MongoDB Migration:**

```csharp
internal sealed class YYYYMMDDHHMMSS_MigrationName : PlatformMongoMigrationExecutor<{Service}DbContext>
{
    public override string Name => "YYYYMMDDHHMMSS_MigrationName";
    public override DateTime? OnlyForDbInitBeforeDate => new(YYYY, MM, DD);
    public override DateTime? ExpirationDate => new(YYYY, MM, DD); // Optional: auto-delete after date

    public override async Task Execute({Service}DbContext dbContext)
    {
        // Ensure indexes
        await dbContext.EnsureInboxBusMessageCollectionIndexesAsync(true);
        await dbContext.EnsureOutboxBusMessageCollectionIndexesAsync(true);

        // Or custom index/data operations
        var collection = dbContext.GetCollection<Entity>();
        await collection.Indexes.CreateOneAsync(
            new CreateIndexModel<Entity>(
                Builders<Entity>.IndexKeys.Ascending(e => e.Field),
                new CreateIndexOptions { Unique = true }));
    }
}
```

## Phase 4: Verify

- [ ] Migration is idempotent (safe to run multiple times)
- [ ] Large datasets use paging
- [ ] Proper error handling
- [ ] Unit of work for transactions
- [ ] `OnlyForDbsCreatedBeforeDate` set correctly
- [ ] Tested with sample data

## Phase 5: Wait for Approval

**CRITICAL:** Present your migration design and wait for explicit user approval before creating files.

---

Use `backend-data-migration` skill for detailed guidance.

## IMPORTANT Task Planning Notes

- Always plan and break many small todo tasks
- Always add a final review todo task to review the works done at the end to find any fix or enhancement needed

Overview

This skill creates and runs database migrations following EasyPlatform conventions for EF Core, PlatformDataMigration, and MongoDB. It guides you through analyzing requirements, designing idempotent migrations, generating code with the correct naming and paging, and verifying safety before execution. The skill never creates files without explicit approval and warns before production operations.

How this skill works

It inspects the requested migration type and project layout, identifies whether the work is a schema migration, a data migration, or a MongoDB migration, and generates a plan using the platform patterns. It produces migration templates (naming: YYYYMMDDHHMMSS_Name), paging-aware data migration code, and Mongo index/operation stubs, and provides commands to add, apply, list, or rollback EF Core migrations. It also verifies idempotency, paging, transaction usage, and required guard dates before asking for approval.

When to use it

  • Add or modify EF Core schema changes for SQL Server or PostgreSQL
  • Perform data transformations or backfills that must run safely across environments
  • Create MongoDB index or data changes executed on startup
  • Apply pending migrations on a deployment or rollback a recent change
  • Plan a migration that needs explicit safety review before production

Best practices

  • Design migrations idempotent so they can run multiple times without side effects
  • Use paging for large data updates (PageSize 200–500) and set dismissSendEvent: true when events aren’t needed
  • Include OnlyForDbsCreatedBeforeDate/OnlyForDbInitBeforeDate to limit scope and avoid accidental runs
  • Use unit-of-work/transactions and explicit error handling; test with sample data before applying
  • Always show pending SQL/changes and request explicit approval before touching production

Example use cases

  • Add a new column and populate it in small batches with a PlatformDataMigrationExecutor
  • Create EF Core schema migration, review SQL diff, and apply on staging before production
  • Add unique index to a MongoDB collection via PlatformMongoMigrationExecutor with expiration guard date
  • Run update to apply pending migrations on application startup or via CI job
  • Rollback last migration after an issue found in staging using the rollback command

FAQ

How do I name migrations?

Use the convention YYYYMMDDHHMMSS_MigrationName so ordering is deterministic and traceable.

How do I handle very large tables?

Process in pages (200–500 items), use dismissSendEvent: true, and checkDiff: false for bulk updates to reduce overhead and event noise.

Can migrations run automatically in production?

They can, but always require explicit approval. Show pending changes, recommend backups, and limit runs with OnlyForDbsCreatedBeforeDate to reduce risk.