Claude-skill-registry acc-create-domain-event
Generates DDD Domain Events for PHP 8.5. Creates immutable event records with metadata, past-tense naming. Includes unit tests.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/acc-create-domain-event" ~/.claude/skills/majiayu000-claude-skill-registry-acc-create-domain-event && rm -rf "$T"
manifest:
skills/data/acc-create-domain-event/SKILL.mdsource content
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 | |
| Confirm order | |
| Cancel order | |
| Ship order | |
| Add line | |
| Remove line | |
| Change email | |
| Register user | |
| Deactivate user | |
Bad Names (Avoid)
| Bad Name | Why | Good Name |
|---|---|---|
| Present tense | |
| Vague | |
| Too generic | |
| Doesn't say what changed | |
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