/symfony-domain-modeling
Symfony domain modeling — entities, value objects, domain events, aggregates, domain exceptions. Triggers on: entity, value object, domain event, aggregate, domain exception, domain model, domain logic, business rule, invariant
$ npx -y skills add aligundogdu/symfony-hexagonal-skill --skill symfony-domain-modeling --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/symfony-domain-modeling
Context preview
The summary Claude sees to decide when to auto-load this skill.
Symfony domain modeling — entities, value objects, domain events, aggregates, domain exceptions. Triggers on: entity, value object, domain event, aggregate, domain exception, domain model, domain logic, business rule, invariant
SKILL.md
symfony-domain-modeling.SKILL.mddescription: "Symfony domain modeling — entities, value objects, domain events, aggregates, domain exceptions. Triggers on: entity, value object, domain event, aggregate, domain exception, domain model, domain logic, business rule, invariant"
Symfony Domain Modeling
You are an expert in Domain-Driven Design within Symfony hexagonal architecture. Use this skill when users need to create or modify domain model elements.
When to Activate
- User wants to create an entity or aggregate root
- User asks about value objects
- User needs domain events
- User mentions domain exceptions or business rules
- User discusses aggregates or invariants
Entity Patterns
Rules
- Private constructor + static factory method(s)
- No Doctrine annotations/attributes — mapping is in Infrastructure
- Record domain events on state changes
- Protect invariants in methods, not in external validators
- Use value objects for typed properties (no primitive obsession)
Template
namespace App\Domain\{Module}\Entity;
final class {Entity}
{
private array $domainEvents = [];
private function __construct(
private {EntityId} $id,
// typed properties using value objects
) {
}
public static function create(/* params */): self
{
// validate invariants
$entity = new self(/* params */);
$entity->recordEvent(new {Entity}Created(/* event data */));
return $entity;
}
// Business methods that protect invariants
public function doAction(/* params */): void
{
// guard clause / invariant check
// state change
$this->recordEvent(new {ActionDone}(/* event data */));
}
public function pullDomainEvents(): array
{
$events = $this->domainEvents;
$this->domainEvents = [];
return $events;
}
private function recordEvent(object $event): void
{
$this->domainEvents[] = $event;
}
}Value Object Patterns
Rules
- Always `final readonly class`
- Self-validating in constructor (throw domain exception if invalid)
- Immutable — no setters
- Implement `equals()` for comparison
- Use for: IDs, email, money, dates, status enums
Template
namespace App\Domain\{Module}\ValueObject;
use App\Domain\{Module}\Exception\Invalid{ValueObject}Exception;
final readonly class {ValueObject}
{
public function __construct(
public string $value,
) {
if (/* validation fails */) {
throw new Invalid{ValueObject}Exception($value);
}
}
public function equals(self $other): bool
{
return $this->value === $other->value;
}
public function __toString(): string
{
return $this->value;
}
}Domain Event Patterns
Rules
- Past-tense naming: `UserRegistered`, `OrderPlaced`, `PaymentFailed`
- Immutable (`final readonly class`)
- Carry only the data needed by handlers (IDs, not full entities)
- Created inside entities on state changes
Template
namespace App\Domain\{Module}\Event;
final readonly class {PastTenseEvent}
{
public function __construct(
public string $entityId,
public \DateTimeImmutable $occurredAt = new \DateTimeImmutable(),
// additional event-specific data
) {
}
}Domain Exception Patterns
namespace App\Domain\{Module}\Exception;
final class {DomainException} extends \DomainException
{
public static function because{Reason}(/* context */): self
{
return new self(sprintf('Descriptive message: %s', $context));
}
}References
See `references/` for detailed patterns:
- `entity-patterns.md` — Full entity examples with aggregates
- `value-object-patterns.md` — Common value object implementations
- `domain-event-patterns.md` — Event design and dispatch patterns
Read more
description: "Symfony domain modeling — entities, value objects, domain events, aggregates, domain exceptions. Triggers on: entity, value object, domain event, aggregate, domain exception, domain model, domain logic, business rule, invariant"
Symfony Domain Modeling
You are an expert in Domain-Driven Design within Symfony hexagonal architecture. Use this skill when users need to create or modify domain model elements.
When to Activate
- User wants to create an entity or aggregate root
- User asks about value objects
- User needs domain events
- User mentions domain exceptions or business rules
- User discusses aggregates or invariants
Entity Patterns
Rules
- Private constructor + static factory method(s)
- No Doctrine annotations/attributes — mapping is in Infrastructure
- Record domain events on state changes
- Protect invariants in methods, not in external validators
- Use value objects for typed properties (no primitive obsession)
Template
namespace App\Domain\{Module}\Entity;
final class {Entity}
{
private array $domainEvents = [];
private function __construct(
private {EntityId} $id,
// typed properties using value objects
) {
}
public static function create(/* params */): self
{
// validate invariants
$entity = new self(/* params */);
$entity->recordEvent(new {Entity}Created(/* event data */));
return $entity;
}
// Business methods that protect invariants
public function doAction(/* params */): void
{
// guard clause / invariant check
// state change
$this->recordEvent(new {ActionDone}(/* event data */));
}
public function pullDomainEvents(): array
{
$events = $this->domainEvents;
$this->domainEvents = [];
return $events;
}
private function recordEvent(object $event): void
{
$this->domainEvents[] = $event;
}
}Value Object Patterns
Rules
- Always `final readonly class`
- Self-validating in constructor (throw domain exception if invalid)
- Immutable — no setters
- Implement `equals()` for comparison
- Use for: IDs, email, money, dates, status enums
Template
namespace App\Domain\{Module}\ValueObject;
use App\Domain\{Module}\Exception\Invalid{ValueObject}Exception;
final readonly class {ValueObject}
{
public function __construct(
public string $value,
) {
if (/* validation fails */) {
throw new Invalid{ValueObject}Exception($value);
}
}
public function equals(self $other): bool
{
return $this->value === $other->value;
}
public function __toString(): string
{
return $this->value;
}
}Domain Event Patterns
Rules
- Past-tense naming: `UserRegistered`, `OrderPlaced`, `PaymentFailed`
- Immutable (`final readonly class`)
- Carry only the data needed by handlers (IDs, not full entities)
- Created inside entities on state changes
Template
namespace App\Domain\{Module}\Event;
final readonly class {PastTenseEvent}
{
public function __construct(
public string $entityId,
public \DateTimeImmutable $occurredAt = new \DateTimeImmutable(),
// additional event-specific data
) {
}
}Domain Exception Patterns
namespace App\Domain\{Module}\Exception;
final class {DomainException} extends \DomainException
{
public static function because{Reason}(/* context */): self
{
return new self(sprintf('Descriptive message: %s', $context));
}
}References
See `references/` for detailed patterns:
- `entity-patterns.md` — Full entity examples with aggregates
- `value-object-patterns.md` — Common value object implementations
- `domain-event-patterns.md` — Event design and dispatch patterns
A Claude Code plugin that enforces hexagonal architecture (ports & adapters) in Symfony projects. Works with both new projects (full scaffolding) and existing projects (progressive, module-by-module refactoring).
Other skills on symfony-hexagonal-skill.
- /symfony-api-response
Symfony API response standardization — JSON payload format, exception handling, controllers, REST endpoints, error responses, debug mode. Triggers on: API, endpoint, controller, response, error handling, JSON, REST, API response, exception subscriber, HTTP
Open skill - /symfony-cqrs-handlers
Symfony CQRS command/query handlers — commands, queries, handlers, bus configuration, use cases. Triggers on: command, query, handler, CQRS, bus, use case, command handler, query handler, message bus
Open skill - /symfony-doctrine-persistence
Symfony Doctrine persistence — repository adapters, entity mapping, migrations, transactions, database patterns. Triggers on: doctrine, repository, persistence, database, mapping, migration, ORM, entity manager, DBAL, transaction
Open skill - /symfony-hexagonal-architecture
Symfony hexagonal architecture setup — project structure, module scaffolding, layer responsibilities, dependency rules. Triggers on: architecture, module, layer, hexagonal, scaffold, project structure, directory structure, new project, new module
Open skill - /symfony-messenger-async
Symfony Messenger async processing — message queues, retry strategies, failure transport, Symfony Scheduler, idempotency patterns, background jobs. Triggers on: messenger, async, queue, retry, scheduler, background job, worker, transport, message queue, cron, scheduled task
Open skill - /symfony-ports-adapters
Symfony ports and adapters — port interfaces, adapter implementations, dependency injection, autowiring, repository interfaces. Triggers on: port, adapter, interface, repository interface, DI, autowiring, dependency injection, services.yaml, binding
Open skill

