For the complete documentation index, see llms.txt. This page is also available as Markdown.

Tempest Models as Aggregates

Using Tempest active-record models as Ecotone Aggregates

Ecotone comes with out-of-the-box integration for using Tempest's active-record models (those using the IsDatabaseModel trait) as State-Stored Aggregates. This is the Tempest equivalent of Eloquent on Laravel and Doctrine ORM on Symfony.

Your Models as Aggregates

Mark your model with the #[Aggregate] attribute and add Command Handlers:

use Ecotone\Modelling\Attribute\Aggregate;
use Ecotone\Modelling\Attribute\CommandHandler;
use Ecotone\Modelling\Attribute\IdentifierMethod;
use Ecotone\Modelling\Attribute\QueryHandler;
use Tempest\Database\IsDatabaseModel;
use Tempest\Database\PrimaryKey;

#[Aggregate]
final class Product
{
    use IsDatabaseModel;

    public PrimaryKey $id;
    public string $name;
    public int $price;

    #[CommandHandler] // 1. factory method
    public static function register(RegisterProduct $command): self
    {
        $product = new self();
        $product->name = $command->name;
        $product->price = $command->price;
        $product->save(); // 3. Saving

        return $product;
    }

    #[CommandHandler('product.changePrice')] // 2. action method
    public function changePrice(ChangePrice $command): void
    {
        $this->price = $command->price;
    }

    #[QueryHandler('product.getPrice')]
    public function getPrice(): int
    {
        return $this->price;
    }

    #[IdentifierMethod('id')] // 4. expose the scalar identifier
    public function getId(): int
    {
        return $this->id->value;
    }
}
  1. Calling the factory method:

  1. Calling the action method:

  1. Aggregates require state to be always valid. Tempest assigns the auto-increment PrimaryKey on save(), so call save() in the factory to obtain the identifier. If you generate identifiers outside the database, this step is not needed.

  2. #[IdentifierMethod] exposes the scalar identifier Ecotone uses to load and route to the aggregate (Tempest stores it as a PrimaryKey value object).

Repository and Business Interface

Because a Tempest model is a state-stored Aggregate, Ecotone persists it automatically through the TempestRepository when a Command Handler returns or mutates it — no repository wiring is required.

You can additionally declare a DBAL Business Interface (#[DbalQuery] / #[DbalWrite]) for read-side queries over the same connection.

Last updated

Was this helpful?