acc-create-domain-event

Generates DDD Domain Events for PHP 8.5. Creates immutable event records with metadata, past-tense naming. Includes unit tests.

181 stars

Best use case

acc-create-domain-event is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Generates DDD Domain Events for PHP 8.5. Creates immutable event records with metadata, past-tense naming. Includes unit tests.

Teams using acc-create-domain-event should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/acc-create-domain-event/SKILL.md --create-dirs "https://raw.githubusercontent.com/majiayu000/claude-skill-registry/main/skills/data/acc-create-domain-event/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/acc-create-domain-event/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How acc-create-domain-event Compares

Feature / Agentacc-create-domain-eventStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Generates DDD Domain Events for PHP 8.5. Creates immutable event records with metadata, past-tense naming. Includes unit tests.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# 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
<?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
<?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
<?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
<?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
<?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
<?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

```php
// 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

```php
// 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:

1. **Name in past tense** (what happened)
2. **Include aggregate ID** for identification
3. **Add all relevant data** needed to understand the event
4. **Use primitive types** for easy serialization
5. **Include metadata** (event ID, timestamp)
6. **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

Related Skills

adding-persistent-event

181
from majiayu000/claude-skill-registry

Adds a new type of event that gets persisted to the event log. Use this when adding new kinds of write operations to the system or when adding new events to existing code.

add-resource-events

181
from majiayu000/claude-skill-registry

Add real-time event emission to a resource service. Use when adding SSE/real-time capabilities to a resource. Triggers on "add events", "real-time events", "SSE events".

add-event-type

181
from majiayu000/claude-skill-registry

Add a new event type to the frontend feed system with corresponding React component. Use when user mentions "new event", "add event type", "event block", "new block type", or wants to display new agent output types.

add-domain-entity

181
from majiayu000/claude-skill-registry

Create domain layer components: models, repository interfaces, marshallers, and implementations. Use when: (1) adding domain model in internal/domain/model/, (2) creating repository interface in internal/domain/repository/, (3) implementing repository with marshaller in internal/infrastructure/{db}/. This is Step 2 of CRUD workflow (after add-database-table, before add-api-endpoint).

acc-event-sourcing-knowledge

181
from majiayu000/claude-skill-registry

Event Sourcing knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for Event Sourcing architecture audits.

acc-create-value-object

181
from majiayu000/claude-skill-registry

Generates DDD Value Objects for PHP 8.5. Creates immutable, self-validating objects with equality comparison. Includes unit tests.

acc-create-use-case

181
from majiayu000/claude-skill-registry

Generates Application Use Cases for PHP 8.5. Creates orchestration services that coordinate domain objects, handle transactions, and dispatch events. Includes unit tests.

acc-create-unit-test

181
from majiayu000/claude-skill-registry

Generates PHPUnit unit tests for PHP 8.5. Creates isolated tests with AAA pattern, proper naming, attributes, and one behavior per test. Supports Value Objects, Entities, Services.

acc-create-test-double

181
from majiayu000/claude-skill-registry

Generates test doubles (Mocks, Stubs, Fakes, Spies) for PHP 8.5. Creates appropriate double type based on testing needs with PHPUnit MockBuilder patterns.

acc-create-test-builder

181
from majiayu000/claude-skill-registry

Generates Test Data Builder and Object Mother patterns for PHP 8.5. Creates fluent builders with sensible defaults and factory methods for test data creation.

acc-create-strategy

181
from majiayu000/claude-skill-registry

Generates Strategy pattern for PHP 8.5. Creates interchangeable algorithm families with context class, strategy interface, and concrete implementations. Includes unit tests.

acc-create-state

181
from majiayu000/claude-skill-registry

Generates State pattern for PHP 8.5. Creates state machines with context, state interface, and concrete states for behavior changes. Includes unit tests.