Skip to content

Latest commit

 

History

History
298 lines (237 loc) · 7.97 KB

migrations.md

File metadata and controls

298 lines (237 loc) · 7.97 KB

Migrations

Back to README

Table of Contents

Using migrations

⬆️ Go to TOC

Command can generate a full domain directory structure starting from an existing database migration.

The command will parse the migration and map all fields in the data transfer object, projection and projector.

Important: the command can process only "create" migrations. Other migrations that modify table structure will be skipped.

Generate a domain using existing migration

⬆️ Go to TOC

Command can generate a full domain directory structure starting from an existing migration.

E.g. migration 2024_10_01_112344_create_tigers_table.php

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('tigers', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name')->index();
            $table->int('age');
            $table->json('meta');
            $table->timestamps();
        });
    }
    
    // etc.
};

In this example, id will be used as primary key. No aggregate will be available.

It is possible to specify the migration interactively or, more efficiently, passing it to command options. Please notice that the migration filename timestamp is not needed:

php artisan make:event-sourcing-domain Tiger --domain=Animal --migration=create_tigers_table --notifications=slack --failed-events=1 --reactor=0 --unit-test
Your choices:

| Option                     | Choice                                     |
|----------------------------|--------------------------------------------|
| Model                      | Tiger                                      |
| Domain                     | Animal                                     |
| Namespace                  | Domain                                     |
| Use migration              | 2024_10_01_112344_create_animals_table.php |
| Primary key                | id                                         |
| Create Aggregate class     | no                                         |
| Create Reactor class       | no                                         |
| Create PHPUnit tests       | yes                                        |
| Create failed events       | yes                                        |
| Model properties           | string name                                |
|                            | int age                                    |
|                            | array meta                                 |
| Notifications              | yes                                        |

Do you confirm the generation of the domain?
> yes

Domain [Animal] with model [Tiger] created successfully.

Directory structure generated (using id as primary key)

app/
├── Domain/
│   └── Animal/
│       ├── Actions/
│       │   ├── CreateTiger
│       │   ├── DeleteTiger
│       │   └── UpdateTiger
│       ├── DataTransferObjects/
│       │   └── TigerData
│       ├── Events/
│       │   ├── TigerCreated
│       │   ├── TigerCreationFailed
│       │   ├── TigerDeleted
│       │   ├── TigerDeletionFailed
│       │   ├── TigerUpdateFailed
│       │   └── TigerUpdated
│       ├── Notifications/
│       │   ├── Concerns/
│       │   │   ├── HasDataAsArray
│       │   │   └── HasSlackNotification
│       │   ├── TigerCreated
│       │   ├── TigerCreationFailed
│       │   ├── TigerDeleted
│       │   ├── TigerDeletionFailed
│       │   ├── TigerUpdateFailed
│       │   └── TigerUpdated
│       ├── Projections/
│       │   └── Tiger
│       └── Projectors/
│           └── TigerProjector
└── etc.

tests/
├── Unit/
│   └── Domain/
│       └── Animal/
│           └── TigerTest.php
└── etc.

If Spatie event sourcing is configured to auto-discover projectors, that is immediately usable:

use App\Domain\Animal\Actions\CreateTiger;
use App\Domain\Animal\DataTransferObjects\TigerData;
use App\Domain\Animal\Projections\Tiger;

# This will create a record in 'tigers' table, using projector TigerProjector
(new CreateTiger())(new TigerData(
  name: 'tiger',
  age: 7,
  meta: []
));

# Retrieve record
$tiger = Tiger::query()->where('name', 'tiger')->first();

Limitations

⬆️ Go to TOC

Update migrations

⬆️ Go to TOC

Update database migrations are not yet supported, but only create database migrations.

E.g. migration 2024_10_01_112344_update_lions_table.php

php artisan make:event-sourcing-domain Lion --domain=Animal --migration=update_lions_table

Output

ERROR  There was an error: Update migration file is not supported.

Unsupported column types

⬆️ Go to TOC

The following column types are not yet supported:

  • primary composite keys e.g. $table->primary(['id', 'parent_id']);
  • binary
  • foreignIdFor
  • foreignUlid
  • geography (from Laravel 11.x)
  • geometry
  • morphs
  • nullableMorphs
  • nullableUlidMorphs
  • nullableUuidMorphs
  • set
  • ulidMorphs
  • uuidMorphs
  • ulid

If the database migration contains any of those:

  • a warning will be generated in command line output for each of those
  • a @todo comment will be added for each of those in the data transfer object, projection and projector.

E.g. migration 2024_10_01_112344_create_lions_table.php

php artisan make:event-sourcing-domain Lion --domain=Animal --migration=create_lions_table
return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('lions', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name')->index();
            $table->int('age');
            $table->ulidMorphs('taggable');
            $table->timestamps();
        });
    }
    
    // etc.
};

Data Transfer Object

namespace App\Domain\Animal\DataTransferObjects;

class LionData
{
    public function __construct(
        public string $name,
        public int $age,
        // @todo public ulidMorphs $taggable, column type is not yet supported,
    ) {}

    // etc.
}

Projection

namespace App\Domain\Animal\Projections;

/**
 * @property int $id
 * @property string $name
 * @property int $age
 */
class Lion extends Projection
{
    protected $primaryKey = 'id';

    protected $fillable = [
        'id',
        'name',
        'age',
        // @todo 'taggable', column type 'ulidMorphs' is not yet supported
    ];

    protected $casts = [
        'id' => 'int',
        'name' => 'string',
        'age' => 'int',
        // @todo 'taggable' => 'ulidMorphs', column type is not yet supported
    ];

    // etc.
}

Projector

namespace App\Domain\Animal\Projectors;

class AnimalProjector extends Projector
{
    public function onLionCreated(LionCreated $event): void
    {
        try {
            (new Animal)->writeable()->create([
                'name' => $event->lionData->name,
                'age' => $event->lionData->age,
                // @todo 'taggable' => $event->lionData->taggable, column type 'ulidMorphs' is not yet supported
            ]);
        } catch (Exception $e) {
            Log::error('Unable to create animal', [
                'error' => $e->getMessage(),
                'event' => $event,
            ]);
        }
    }
    
    // etc.
}