symfony-api-response
Symfony REST API response standard {result, error, extra, status}: thin controllers, #[MapRequestPayload] / #[MapQueryString]…
Symfony DDD domain modeling in pure PHP: entities, aggregates, value objects, domain events, domain exceptions, ID generation ports, PHP 8.2-8.5 modeling features (readonly, private(set), clone with). Use when: entity, value object, domain event, aggregate, domain exception,
Claude Code
$ npx -y skills add aligundogdu/symfony-hexagonal-skill --skill symfony-domain-modeling --agent claude-code
Codex
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-domain-modeling -a codex -y
Cursor
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-domain-modeling -a cursor -y
Gemini CLI
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-domain-modeling -a gemini-cli -y
GitHub Copilot
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-domain-modeling -a github-copilot -y
OpenCode
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-domain-modeling -a opencode -y
Windsurf
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-domain-modeling -a windsurf -y
name: symfony-domain-modeling description: "Symfony DDD domain modeling in pure PHP: entities, aggregates, value objects, domain events, domain exceptions, ID generation ports, PHP 8.2-8.5 modeling features (readonly, private(set), clone with). Use when: entity, value object, domain event, aggregate, domain exception, domain model, domain logic, business rule, invariant, enum status"
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.
| Feature | Requires | Fallback | |---|---|---| | `final readonly class` | PHP 8.2 | PHP 8.1: `final class` + `public readonly` properties | | `public private(set)` properties, property hooks | PHP 8.4 + Doctrine ORM 3.4 | private property + getter | | `clone($this, ['amount' => $x])` | PHP 8.5 | `new self(...)` | | `array_find()`, `array_any()`, `array_all()` | PHP 8.4 | `foreach` loop |
Generate the fallback form unless the detected PHP constraint allows the newer syntax.
namespace App\Domain\{Module}\Entity;
use App\Domain\Shared\Event\DomainEvent;
final class {Entity}
{
/** @var list<DomainEvent> */
private array $domainEvents = [];
private function __construct(
private {Entity}Id $id,
// typed properties using value objects
) {
}
public static function create({Entity}Id $id /*, params */): self
{
// validate invariants
$entity = new self($id /*, params */);
$entity->recordEvent(new {Entity}Created($id->value));
return $entity;
}
// Business methods that protect invariants
public function doAction(/* params */): void
{
// guard clause / invariant check
// state change
$this->recordEvent(new {ActionDone}($this->id->value));
}
/** @return list<DomainEvent> */
public function pullDomainEvents(): array
{
$events = $this->domainEvents;
$this->domainEvents = [];
return $events;
}
private function recordEvent(DomainEvent $event): void
{
$this->domainEvents[] = $event;
}
}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 Invalid{ValueObject}Exception::forValue($value);
}
}
public function equals(self $other): bool
{
return $this->value === $other->value;
}
public function __toString(): string
{
return $this->value;
}
}namespace App\Domain\{Module}\Event;
use App\Domain\Shared\Event\DomainEvent;
final readonly class {PastTenseEvent} implements DomainEvent
{
public function __construct(
public string $aggregateId,
public \DateTimeImmutable $occurredOn,
// additional event-specific data
) {
}
public function aggregateId(): string
{
return $this->aggregateId;
}
public function occurredOn(): \DateTimeImmutable
{
return $this->occurredOn;
}
}namespace App\Domain\{Module}\Exception;
final class {DomainException} extends \DomainException
{
public static function because{Reason}(string $context): self
{
return new self(sprintf('Descriptive message: %s', $context));
}
}See `references/` for detailed 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).
Symfony REST API response standard {result, error, extra, status}: thin controllers, #[MapRequestPayload] / #[MapQueryString]…
Symfony CQRS with Messenger: commands, queries, handlers, command.bus / query.bus / event.bus configuration, typed bus wrappers,…
Doctrine persistence for Symfony hexagonal architecture (ORM 2.x/3.x, DBAL 3/4, DoctrineBundle 2/3): repository adapters, XML…
Symfony hexagonal architecture (ports & adapters) setup for Symfony 6.4 LTS, 7.4 LTS and 8.x: project structure, module…
Symfony Messenger async processing and Symfony Scheduler instead of cron: transports, routing (#[AsMessage] 7.2+), retry with…
Symfony ports and adapters: port interfaces in Domain, adapter implementations in Infrastructure, dependency injection,…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.