A separate migration runner

The problem it solves

Four EF Core contexts share one PostgreSQL database — the application's, Billing's, the identity host's and the gateway's — plus Quartz.NET's tables and Wolverine's outbox. If each service migrated its own schema on start-up, two replicas could race on the same migration, and a service could start against a schema another service had not finished.

Blouppy.MigrationRunner applies every migration, builds the messaging tables, then exits. Aspire starts it first and starts the API, the identity host and the gateway only once it has completed. It exits 1 on a failure, so nothing starts against a schema in an unknown state.

In production it is a deliberate step: the runner refuses to touch a Production database unless Migrations:AllowInProduction is set.

What it costs

  • One more project, which references every context's assembly.
  • A step in deployment: the compose file runs it before the services.

What you can relax

  • Run it standalone, without Aspire, given the connection string:

    ConnectionStrings__blouppy-db="Host=…;Database=blouppy;Username=…;Password=…" \
      dotnet run --project back/Blouppy.MigrationRunner
    
  • Seeding is not its job, and stays with the services that own the data — so you can run the runner against any environment without writing sample rows.