acc-create-domain-event
1
总安装量
1
周安装量
#52472
全站排名
安装命令
npx skills add https://github.com/dykyi-roman/awesome-claude-code --skill acc-create-domain-event
Agent 安装分布
opencode
1
claude-code
1
Skill 文档
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 function withCausation(string $causationId, ?string $correlationId = null): self
{
return new self(
eventId: self::generateId(),
occurredAt: new DateTimeImmutable(),
causationId: $causationId,
correlationId: $correlationId ?? $causationId
);
}
private static function generateId(): string
{
$data = random_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
}
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;
}
}
final readonly class OrderLineAddedEvent implements DomainEvent
{
public function __construct(
public string $orderId,
public string $productId,
public string $productName,
public int $quantity,
public int $unitPriceCents,
public string $currency,
public EventMetadata $metadata
) {}
public function aggregateId(): string
{
return $this->orderId;
}
public function occurredAt(): DateTimeImmutable
{
return $this->metadata->occurredAt;
}
}
final readonly class OrderConfirmedEvent implements DomainEvent
{
public function __construct(
public string $orderId,
public int $totalCents,
public string $currency,
public DateTimeImmutable $confirmedAt,
public EventMetadata $metadata
) {}
public function aggregateId(): string
{
return $this->orderId;
}
public function occurredAt(): DateTimeImmutable
{
return $this->metadata->occurredAt;
}
}
final readonly class OrderCancelledEvent implements DomainEvent
{
public function __construct(
public string $orderId,
public string $reason,
public DateTimeImmutable $cancelledAt,
public EventMetadata $metadata
) {}
public function aggregateId(): string
{
return $this->orderId;
}
public function occurredAt(): DateTimeImmutable
{
return $this->metadata->occurredAt;
}
}
final readonly class OrderShippedEvent implements DomainEvent
{
public function __construct(
public string $orderId,
public string $trackingNumber,
public string $carrier,
public DateTimeImmutable $shippedAt,
public EventMetadata $metadata
) {}
public function aggregateId(): string
{
return $this->orderId;
}
public function occurredAt(): DateTimeImmutable
{
return $this->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
{
public function __construct(
public string $userId,
public string $previousEmail,
public string $newEmail,
public EventMetadata $metadata
) {}
public function aggregateId(): string
{
return $this->userId;
}
public function occurredAt(): DateTimeImmutable
{
return $this->metadata->occurredAt;
}
}
final readonly class UserDeactivatedEvent implements DomainEvent
{
public function __construct(
public string $userId,
public string $reason,
public DateTimeImmutable $deactivatedAt,
public EventMetadata $metadata
) {}
public function aggregateId(): string
{
return $this->userId;
}
public function occurredAt(): DateTimeImmutable
{
return $this->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 = new DateTimeImmutable();
$event = new OrderCreatedEvent(
orderId: 'order-123',
customerId: 'customer-456',
createdAt: $createdAt,
metadata: $metadata
);
self::assertSame('order-123', $event->orderId);
self::assertSame('customer-456', $event->customerId);
self::assertSame($createdAt, $event->createdAt);
self::assertSame($metadata, $event->metadata);
}
public function testReturnsAggregateId(): void
{
$event = $this->createEvent();
self::assertSame('order-123', $event->aggregateId());
}
public function testReturnsOccurredAt(): void
{
$metadata = EventMetadata::create();
$event = new OrderCreatedEvent(
orderId: 'order-123',
customerId: 'customer-456',
createdAt: new DateTimeImmutable(),
metadata: $metadata
);
self::assertEquals($metadata->occurredAt, $event->occurredAt());
}
private function createEvent(): OrderCreatedEvent
{
return new OrderCreatedEvent(
orderId: 'order-123',
customerId: 'customer-456',
createdAt: new DateTimeImmutable(),
metadata: EventMetadata::create()
);
}
}
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
// GOOD: All data needed to understand what happened
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
) {}
}
// BAD: Missing important data
final readonly class OrderConfirmedEvent
{
public function __construct(
public string $orderId, // Only ID, no details
public EventMetadata $metadata
) {}
}
Use Primitive Types
// GOOD: Primitive types for serialization
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
) {}
}
// BAD: Value Objects in events (serialization issues)
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