andreo/eventsauce-migration-generator
Symfony Console command that generates Doctrine Migrations for EventSauce message storage per aggregate. Configure Doctrine Migrations, then run a single command with a table prefix, selectable schemas (event/outbox/snapshot), UUID type, and optional table-name suffixes.
Install Dependencies:
composer require andreo/eventsauce-migration-generator doctrine/migrations symfony/console
Note: Laravel 10+ supports Symfony Console via symfony/console; use laravel/symfony-cli-bridge for Artisan integration.
Configure Doctrine Migrations:
Add to config/packages/doctrine_migrations.yaml (Laravel’s Symfony bridge config):
doctrine_migrations:
storage_table_name: 'migration_versions'
storage_table_schema: 'public'
organizer: 'attribute'
Register the Command: Create a custom Artisan command to wrap the Symfony command:
php artisan make:command GenerateEventSauceMigrations
Update the generated file (app/Console/Commands/GenerateEventSauceMigrations.php):
use Andreo\EventSauce\Doctrine\Migration\Command\GenerateDoctrineMigrationForEventSauceCommand;
use Doctrine\Migrations\DependencyFactory;
use Symfony\Component\Console\Input\ArrayInput;
class GenerateEventSauceMigrations extends Command {
protected $signature = 'eventsauce:migrate {prefix?} {--schema= : event|outbox|snapshot|all} {--uuid-type= : binary|string}';
public function handle() {
$connection = \Doctrine\DBAL\DriverManager::getConnection(['url' => env('DATABASE_URL')]);
$dependencyFactory = new DependencyFactory($connection);
$command = new GenerateDoctrineMigrationForEventSauceCommand(
$dependencyFactory,
new \Andreo\EventSauce\Doctrine\Migration\Schema\TableNameSuffix('message_store')
);
$input = new ArrayInput($this->parseArgs());
$command->run($input, new \Symfony\Component\Console\Output\BufferedOutput());
}
}
First Run: Generate migrations for an aggregate with a custom prefix:
php artisan eventsauce:migrate user_aggregate --schema=event,snapshot
Define Aggregates:
Ensure your EventSauce aggregates are properly configured (e.g., UserAggregate, OrderAggregate).
Generate Migrations:
# Generate all schemas (events, snapshots, outbox) for all aggregates
php artisan eventsauce:migrate
# Generate only events and snapshots for a specific prefix
php artisan eventsauce:migrate user_ --schema=event,snapshot
Apply Migrations:
Use Laravel’s migrate command:
php artisan migrate
Repository Configuration:
Ensure your EventSauce\EventSauce repository is configured to use the generated tables:
$repository = new EventSauce\EventSauce\Repository\Doctrine\DoctrineRepository(
$entityManager,
'user_aggregate_message_store', // Matches your prefix
'user_aggregate_snapshot_store',
'user_aggregate_outbox_store'
);
Dynamic Table Naming: Use environment variables or config to manage prefixes/suffixes:
// config/eventsauce.php
'table_suffix' => env('EVENTSAUCE_TABLE_SUFFIX', 'message_store'),
Add to your pipeline (e.g., GitHub Actions):
- name: Generate EventSauce Migrations
run: php artisan eventsauce:migrate --schema=all --uuid-type=string
- name: Run Migrations
run: php artisan migrate --env=testing
Customize table suffixes per tenant:
# Generate for Tenant A
php artisan eventsauce:migrate tenant_a_ --schema=event
# Generate for Tenant B
php artisan eventsauce:migrate tenant_b_ --schema=event
Symfony Console Dependency:
laravel/symfony-cli-bridge or wrap the command as shown above.UUID Type Mismatch:
binary UUIDs, which may conflict with Laravel’s ramsey/uuid (string).--uuid-type=string in CLI or configure via TableNameSuffix:
new TableNameSuffix('message_store', 'string');
Table Name Collisions:
message_store) may clash with existing tables.user_message_store, order_message_store).Migration Bloat:
--path flag to organize migrations:
php artisan eventsauce:migrate --path=database/migrations/eventsauce
Doctrine Migrations Configuration:
doctrine_migrations.yaml can break migration generation.storage_table_name and organizer settings match your setup.Inspect Generated SQL: Enable Doctrine’s SQL logging to verify migrations:
// config/database.php
'logging' => true,
'logging_level' => PDO::SQLITE_DEBUG,
Dry Run:
Use --dry-run (if supported) or inspect generated migration files in database/migrations.
EventSauce Schema Validation: Cross-check generated tables against EventSauce’s schema docs.
Custom Schema Generation: Extend the command to support non-standard EventSauce schemas:
// Override the schema builder
$command->setSchemaBuilder(new CustomSchemaBuilder());
Pre/Post-Migration Hooks: Add logic before/after migration generation:
$command->setPreGenerator(function() {
// Add custom logic (e.g., validate aggregates)
});
Laravel Service Provider Integration:
Register the command globally in AppServiceProvider:
public function boot() {
$this->commands([
new \App\Console\Commands\GenerateEventSauceMigrations(),
]);
}
parallel:migrate (if available) or split aggregates across CI jobs.Environment Variables:
Pass dynamic config via .env:
EVENTSAUCE_TABLE_SUFFIX=message_store
EVENTSAUCE_UUID_TYPE=string
Then access in the command:
$suffix = new TableNameSuffix(env('EVENTSAUCE_TABLE_SUFFIX'));
Artisan Command Caching: Clear cached commands after adding the new one:
php artisan optimize:clear
Testing Migrations:
Use Laravel’s Schema facade to test migrations in PHPUnit:
public function testMigrationGeneration() {
Artisan::call('eventsauce:migrate', ['--schema' => 'event']);
$this->assertFileExists(database_path('migrations/..._create_user_aggregate_events_table.php'));
}
How can I help you explore Laravel packages today?