← Back to list

value-object
by dairectiv
⭐ 0🍴 0📅 Jan 3, 2026
SKILL.md
name: value-object description: Guide for implementing immutable Value Objects with base classes (StringValue, UuidValue) and Doctrine custom types. Use when creating identity objects or domain-specific values. allowed-tools: Read, Write, Edit, Glob, Grep
Value Object Implementation Guide
This Skill provides patterns for implementing immutable Value Objects.
When to Use
- Creating identity value objects (IDs)
- Wrapping primitive values with domain meaning
- Creating Doctrine custom types for persistence
Base Classes
StringValue
For string-based value objects with validation:
final readonly class DirectiveId extends StringValue
{
public static function validate(string $value): void
{
Assert::kebabCase($value, \sprintf('ID "%s" is not kebab-case.', $value));
}
}
Usage:
$id = DirectiveId::fromString('my-rule');
echo $id; // "my-rule"
$id->equals($otherId); // bool
UuidValue
For UUID-based identities (extends Symfony Uuid):
final class ExampleId extends UuidValue
{
}
Usage:
$id = ExampleId::generate(); // Creates UUID v7
$id = ExampleId::setNext('550e8400-e29b-41d4-a716-446655440000'); // For tests
ObjectValue
For complex value objects with JSON serialization:
final readonly class RuleContent extends ObjectValue
{
public function __construct(
public string $body,
public array $tags,
) {}
public function toArray(): array
{
return ['body' => $this->body, 'tags' => $this->tags];
}
public static function fromArray(array $data): static
{
return new self($data['body'], $data['tags']);
}
}
Directory Structure
src/{BoundedContext}/Domain/Object/{Aggregate}/
├── {Entity}Id.php # Identity value object
src/{BoundedContext}/Infrastructure/Doctrine/DBAL/Types/
├── {Entity}IdType.php # Doctrine type
tests/Integration/{BoundedContext}/Infrastructure/Doctrine/DBAL/
├── {Entity}IdTypeTest.php # Type test
Doctrine Custom Types
For StringValue
namespace Dairectiv\{Context}\Infrastructure\Doctrine\DBAL\Types;
final class {Entity}IdType extends StringValueType
{
protected function getStringValueClass(): string
{
return {Entity}Id::class;
}
}
For UuidValue
final class ExampleIdType extends UuidValueType
{
protected function getUuidValueClass(): string
{
return ExampleId::class;
}
}
For ObjectValue
final class RuleContentType extends ObjectValueType
{
protected function getObjectValueClass(): string
{
return RuleContent::class;
}
}
Registration in doctrine.yaml
doctrine:
dbal:
types:
authoring_directive_id: Dairectiv\Authoring\Infrastructure\Doctrine\DBAL\Types\DirectiveIdType
authoring_example_id: Dairectiv\Authoring\Infrastructure\Doctrine\DBAL\Types\RuleExampleIdType
Mapping in Entity
Use the registered type name in your entity mapping:
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Rule extends Directive
{
// Identity with StringValue type
#[ORM\Id]
#[ORM\Column(type: 'authoring_directive_id')]
public private(set) DirectiveId $id;
// UuidValue type for child entities
#[ORM\Id]
#[ORM\Column(type: 'authoring_example_id')]
public private(set) ExampleId $id;
// ObjectValue type for complex JSON data
#[ORM\Column(type: 'authoring_rule_content', nullable: true)]
public private(set) ?RuleContent $content = null;
}
Key points:
- Use the type name registered in
doctrine.yaml, not the class name - For nullable value objects, add
nullable: true - The type handles conversion to/from database automatically
Key Rules
- Always readonly - Value objects are immutable
- Private constructor - Use factory methods (
fromString(),generate()) - Implement validation - Override
validate()for StringValue - Implement Stringable - For easy conversion and debugging
- Implement equals() - For comparison
Testing Custom Types
#[Group('integration')]
#[Group('{bounded-context}')]
final class DirectiveIdTypeTest extends IntegrationTestCase
{
public function testItShouldConvertToDatabaseValue(): void
{
$id = DirectiveId::fromString('my-rule');
$this->assertConvertToDatabaseValue('my-rule', $id, 'authoring_directive_id');
}
public function testItShouldConvertToPHPValue(): void
{
$expected = DirectiveId::fromString('my-rule');
$this->assertConvertToPhpValue($expected, 'my-rule', 'authoring_directive_id');
}
public function testItShouldHandleNull(): void
{
$this->assertConvertToDatabaseValue(null, null, 'authoring_directive_id');
$this->assertConvertToPhpValue(null, null, 'authoring_directive_id');
}
}
Reference Files
api/src/SharedKernel/Domain/Object/ValueObject/StringValue.phpapi/src/SharedKernel/Domain/Object/ValueObject/UuidValue.phpapi/src/SharedKernel/Domain/Object/ValueObject/ObjectValue.phpapi/src/Authoring/Domain/Object/Directive/DirectiveId.phpapi/src/SharedKernel/Infrastructure/Doctrine/DBAL/Types/StringValueType.phpapi/config/packages/doctrine.yaml
Score
Total Score
50/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
Reviews
💬
Reviews coming soon