Claude
Skills
Sign in
Back

implement-value-object

Included with Lifetime
$97 forever

Creates immutable domain value objects using frozen dataclass pattern with validation. Use when implementing domain value objects, creating immutable data structures, or adding validation to values. Covers @dataclass(frozen=True), object.__setattr__() pattern in __post_init__, factory methods (from_string, from_dict, from_content), and validation in frozen context. Triggers on "create value object for X", "implement immutable Y value", "add validation to Z value", or "build value object".

Generalscripts

What this skill does


Works with Python dataclasses in domain/value_objects/ and domain/values/ directories.
# implement-value-object

## Purpose

Create immutable domain value objects using the frozen dataclass pattern with proper validation, factory methods, and immutability guarantees enforced at the type system level.

## When to Use

Use this skill when:
- **Implementing domain value objects** - Creating validated, immutable domain concepts
- **Creating immutable data structures** - Building type-safe value containers
- **Adding validation to values** - Ensuring domain constraints are enforced

**Trigger phrases:**
- "Create a value object for X"
- "Implement immutable Y value"
- "Add validation to Z value"
- "Build value object with validation"

## Quick Start

Create immutable domain value objects using the frozen dataclass pattern with proper validation, factory methods, and immutability guarantees enforced at the type system level.

**Most common use case:**

For a simple validated value object:

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class EmailAddress:
    """Immutable email address value object."""

    value: str

    def __post_init__(self) -> None:
        """Validate email format."""
        if not self.value:
            raise ValueError("Email address cannot be empty")
        if "@" not in self.value:
            raise ValueError(f"Invalid email format: {self.value}")
```

## Table of Contents

### Core Sections
- [Purpose](#purpose) - Immutable domain value objects with frozen dataclass pattern
- [Instructions](#instructions) - Complete implementation guide
  - [Step 1: Define Frozen Dataclass](#step-1-define-frozen-dataclass) - Basic immutable structure
  - [Step 2: Add Validation in __post_init__](#step-2-add-validation-in-__post_init__) - Validation while respecting immutability
  - [Step 3: Add Factory Methods](#step-3-add-factory-methods) - Convenient constructors for different input types
  - [Step 4: Add String Representations](#step-4-add-string-representations) - Useful string representations
  - [Step 5: Add Domain Behavior](#step-5-add-domain-behavior) - Methods expressing domain operations
  - [Step 6: Add Type Hints and Documentation](#step-6-add-type-hints-and-documentation) - Full type safety
- [Examples](#examples) - 4 production-ready patterns
  - [Example 1: Simple String Value Object](#example-1-simple-string-value-object) - Basic validation pattern
  - [Example 2: Path Value Object with Normalization](#example-2-path-value-object-with-normalization) - Type coercion in frozen context
  - [Example 3: Hash Value Object with Factory](#example-3-hash-value-object-with-factory) - Computing values from content
  - [Example 4: Multi-Field Value Object with Validation](#example-4-multi-field-value-object-with-validation) - Complex constraints

### Advanced Topics
- [Requirements](#requirements) - Dependencies and environment setup
- [Common Patterns](#common-patterns) - Reusable implementation patterns
  - [Pattern: Computed Properties](#pattern-computed-properties) - Derived values with @property
  - [Pattern: Type Coercion in Frozen Context](#pattern-type-coercion-in-frozen-context) - Ensuring type consistency
  - [Pattern: Validation with Custom Errors](#pattern-validation-with-custom-errors) - Clear error messages
  - [Pattern: Serialization](#pattern-serialization) - Dictionary conversion support
- [Testing Value Objects](#testing-value-objects) - Test strategies for validation, immutability, equality, factories, serialization
- [File Locations](#file-locations) - Domain layer placement conventions
- [See Also](#see-also) - Templates, examples, architecture references

### Utility Scripts
- [Convert to Value Object](./scripts/convert_to_value_object.py) - Analyze codebase to suggest value object candidates
- [Generate Value Object](./scripts/generate_value_object.py) - Generate value objects with validation, factory methods, and tests
- [Validate Value Objects](./scripts/validate_value_objects.py) - Validate value object patterns in the codebase

## Purpose

Create immutable domain value objects using the frozen dataclass pattern with proper validation, factory methods, and immutability guarantees enforced at the type system level.

## Instructions

### Step 1: Define Frozen Dataclass

Create the basic immutable structure:

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class YourValueObject:
    """Immutable value object representing [domain concept].

    Encapsulates [what logic/behavior].
    """

    value: str  # Primary value
    # Optional additional fields for metadata
```

**Key points:**
- Always use `@dataclass(frozen=True)` for immutability
- Add comprehensive docstring explaining domain meaning
- Use `value` as primary field name for simple value objects
- All fields are immutable after construction

### Step 2: Add Validation in __post_init__

Validate constraints while respecting immutability:

```python
def __post_init__(self) -> None:
    """Validate value object constraints."""
    # Validation checks (raise ValueError on failure)
    if not self.value:
        raise ValueError("Value cannot be empty")

    # For type coercion or normalization in frozen context:
    if not isinstance(self.value, ExpectedType):
        object.__setattr__(self, "value", ExpectedType(self.value))

    # For normalization (e.g., path resolution):
    object.__setattr__(self, "value", self.normalize(self.value))
```

**CRITICAL: Modifying Frozen Dataclass Fields**

Since `frozen=True` prevents normal attribute assignment, use `object.__setattr__()` to modify fields during `__post_init__`:

```python
# ❌ WRONG - Will raise FrozenInstanceError
def __post_init__(self) -> None:
    self.value = self.value.lower()  # FAILS with frozen=True

# ✅ CORRECT - Use object.__setattr__()
def __post_init__(self) -> None:
    object.__setattr__(self, "value", self.value.lower())
```

**Validation patterns:**
- Empty checks: `if not self.value: raise ValueError(...)`
- Format validation: `if not re.match(pattern, self.value): raise ValueError(...)`
- Type coercion: `object.__setattr__(self, "field", Type(field))`
- Normalization: `object.__setattr__(self, "value", normalized_value)`

### Step 3: Add Factory Methods

Provide convenient constructors for different input types:

```python
@classmethod
def from_string(cls, value_str: str) -> "YourValueObject":
    """Create from string representation.

    Args:
        value_str: String to parse

    Returns:
        YourValueObject instance
    """
    return cls(value=value_str)

@classmethod
def from_dict(cls, data: dict) -> "YourValueObject":
    """Create from dictionary representation.

    Args:
        data: Dictionary with value object data

    Returns:
        YourValueObject instance
    """
    return cls(value=data["value"])

@classmethod
def from_content(cls, content: str) -> "YourValueObject":
    """Create from raw content (e.g., compute hash).

    Args:
        content: Raw content to process

    Returns:
        YourValueObject instance
    """
    # Process content (e.g., hash it)
    processed = process(content)
    return cls(value=processed)
```

**Common factory method patterns:**
- `from_string()` - Parse string representation
- `from_dict()` - Deserialize from dictionary
- `from_content()` - Compute value from content (hashes)
- `from_components()` - Build from multiple parts
- `from_bytes()` - Create from binary data

### Step 4: Add String Representations

Provide useful string representations:

```python
def __str__(self) -> str:
    """User-friendly string representation.

    Returns:
        The value as a string
    """
    return str(self.value)

def __repr__(self) -> str:
    """Developer-friendly representation.

    Returns:
        String showing class and value
    """
    return f"YourValueObject('{self.value}')"
```

**For long values (like hashes), truncate in __repr__:**

```python
def __repr__(self) -> str:
   

Related in General