symfony-domain-modeling — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited symfony-domain-modeling (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
time() inside the entity/** @var list<DomainEvent> */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;
}
}final readonly class (PHP 8.2+)equals() for comparisonnamespace 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;
}
}UserRegistered, OrderPlaced, PaymentFailedfinal readonly class) implementing App\Domain\Shared\Event\DomainEvent#[AsMessage] on domain events (it is a Symfony attribute): route them in messenger.yamlnamespace 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:
entity-patterns.md - Full entity examples with aggregatesvalue-object-patterns.md - Common value object implementations, ID generation portdomain-event-patterns.md - Event design and dispatch patterns~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.