| name | acc-create-domain-event |
| description | Generates DDD Domain Events for PHP 8.5. Creates immutable event records with metadata, past-tense naming. Includes unit tests. |
Domain Event Generator
Generate DDD-compliant Domain Events with metadata and tests.
Domain Event Characteristics
- Immutable:
final readonly class
- Past tense: Describes something that happened
- Self-contained: All data needed to understand what happened
- Metadata: Event ID, timestamp, causation/correlation IDs
- No behavior: Pure data, no logic
Template
<?php
declare(strict_types=1);
namespace Domain\{BoundedContext}\Event;
use Domain\Shared\Event\DomainEvent;
final readonly class {Name}Event implements DomainEvent
{
public function __construct(
{properties}
public EventMetadata $metadata
) {}
public function aggregateId(): string
{
return $this->{aggregateIdProperty};
}
public function occurredAt(): DateTimeImmutable
{
return $this->metadata->occurredAt;
}
}
Event Metadata
<?php
declare(strict_types=1);
namespace Domain\Shared\Event;
final readonly class EventMetadata
{
public function __construct(
public string $eventId,
public DateTimeImmutable $occurredAt,
public ?string $causationId = null,
public ?string $correlationId = null,
public int $version = 1,
public ?string $userId = null
) {}
public static function create(): self
{
return new self(
eventId: self::generateId(),
occurredAt: new DateTimeImmutable()
);
}
public static {
(
eventId: ::(),
occurredAt: (),
causationId: ,
correlationId: ??
);
}
{
= ();
[] = (([]) & | );
[] = (([]) & | );
(, ((), ));
}
}
Domain Event Interface
<?php
declare(strict_types=1);
namespace Domain\Shared\Event;
interface DomainEvent
{
public function aggregateId(): string;
public function occurredAt(): DateTimeImmutable;
}
Common Domain Events
Order Events
<?php
declare(strict_types=1);
namespace Domain\Order\Event;
use Domain\Shared\Event\DomainEvent;
use Domain\Shared\Event\EventMetadata;
final readonly class OrderCreatedEvent implements DomainEvent
{
public function __construct(
public string $orderId,
public string $customerId,
public DateTimeImmutable $createdAt,
public EventMetadata $metadata
) {}
public function aggregateId(): string
{
return $this->orderId;
}
public function occurredAt(): DateTimeImmutable
{
return $this->metadata->occurredAt;
}
}
{
{}
{
->orderId;
}
{
->metadata->occurredAt;
}
}
{
{}
{
->orderId;
}
{
->metadata->occurredAt;
}
}
{
{}
{
->orderId;
}
{
->metadata->occurredAt;
}
}
{
{}
{
->orderId;
}
{
->metadata->occurredAt;
}
}
User Events
<?php
declare(strict_types=1);
namespace Domain\User\Event;
final readonly class UserRegisteredEvent implements DomainEvent
{
public function __construct(
public string $userId,
public string $email,
public string $name,
public DateTimeImmutable $registeredAt,
public EventMetadata $metadata
) {}
public function aggregateId(): string
{
return $this->userId;
}
public function occurredAt(): DateTimeImmutable
{
return $this->metadata->occurredAt;
}
}
final readonly class UserEmailChangedEvent implements DomainEvent
{
{}
{
->userId;
}
{
->metadata->occurredAt;
}
}
{
{}
{
->userId;
}
{
->metadata->occurredAt;
}
}
Test Template
<?php
declare(strict_types=1);
namespace Tests\Unit\Domain\Order\Event;
use Domain\Order\Event\OrderCreatedEvent;
use Domain\Shared\Event\EventMetadata;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
#[Group('unit')]
#[CoversClass(OrderCreatedEvent::class)]
final class OrderCreatedEventTest extends TestCase
{
public function testCreatesEventWithAllData(): void
{
$metadata = EventMetadata::create();
$createdAt = ();
= (
orderId: ,
customerId: ,
createdAt: ,
metadata:
);
::(, ->orderId);
::(, ->customerId);
::(, ->createdAt);
::(, ->metadata);
}
{
= ->();
::(, ->());
}
{
= ::();
= (
orderId: ,
customerId: ,
createdAt: (),
metadata:
);
::(->occurredAt, ->());
}
{
(
orderId: ,
customerId: ,
createdAt: (),
metadata: ::()
);
}
}
Event Naming Conventions
Past Tense Naming
| Action | Event Name |
|---|
| Create order | OrderCreatedEvent |
| Confirm order | OrderConfirmedEvent |
| Cancel order | OrderCancelledEvent |
| Ship order | OrderShippedEvent |
| Add line | OrderLineAddedEvent |
| Remove line | OrderLineRemovedEvent |
| Change email | UserEmailChangedEvent |
| Register user | UserRegisteredEvent |
| Deactivate user | UserDeactivatedEvent |
Bad Names (Avoid)
| Bad Name | Why | Good Name |
|---|
CreateOrderEvent | Present tense | OrderCreatedEvent |
OrderEvent | Vague | OrderConfirmedEvent |
OrderStatusChangedEvent | Too generic | OrderConfirmedEvent |
UpdateOrderEvent | Doesn't say what changed | OrderAddressChangedEvent |
Event Data Guidelines
Include All Relevant Data
final readonly class OrderConfirmedEvent
{
public function __construct(
public string $orderId,
public int $totalCents, // Include computed values
public string $currency,
public int $lineCount, // Summary data
public DateTimeImmutable $confirmedAt,
public EventMetadata $metadata
) {}
}
final readonly class OrderConfirmedEvent
{
public function __construct(
public string $orderId, // Only ID, no details
public EventMetadata $metadata
) {}
}
Use Primitive Types
final readonly class OrderCreatedEvent
{
public function __construct(
public string $orderId, // Not OrderId
public string $customerId, // Not CustomerId
public int $totalCents, // Not Money
public string $currency,
public EventMetadata $metadata
) {}
}
final readonly class OrderCreatedEvent
{
public function __construct(
public OrderId $orderId, // Serialization complex
public Money $total, // Serialization complex
public EventMetadata $metadata
) {}
}
Generation Instructions
When asked to create a Domain Event:
- Name in past tense (what happened)
- Include aggregate ID for identification
- Add all relevant data needed to understand the event
- Use primitive types for easy serialization
- Include metadata (event ID, timestamp)
- Generate tests for structure and interface
Usage
To generate a Domain Event, provide:
- Name (e.g., "OrderConfirmed", "UserRegistered")
- Bounded Context (e.g., "Order", "User")
- Aggregate ID field
- Data fields needed
- Any computed/summary data to include