package domain import ( "time" "unicode/utf8" ) // ExampleID is a strongly-typed identifier for examples. type ExampleID string // String returns the string representation of the ID. func (id ExampleID) String() string { return string(id) } // IsZero returns true if the ID is empty. func (id ExampleID) IsZero() bool { return id == "" } // Example name constraints. const ( MinExampleNameLen = 1 MaxExampleNameLen = 100 MaxDescriptionLen = 500 ) // Example represents an example domain entity. // This is a pure domain model with no external dependencies. type Example struct { ID ExampleID Name string Description string CreatedAt time.Time UpdatedAt time.Time } // NewExample creates a new Example with validation. // Returns ErrInvalidExampleName if the name is invalid. func NewExample(id ExampleID, name, description string) (*Example, error) { if err := validateExampleName(name); err != nil { return nil, err } if err := validateDescription(description); err != nil { return nil, err } now := time.Now().UTC() return &Example{ ID: id, Name: name, Description: description, CreatedAt: now, UpdatedAt: now, }, nil } // Update modifies the example's mutable fields with validation. // Returns ErrInvalidExampleName if the name is invalid. func (e *Example) Update(name, description string) error { if err := validateExampleName(name); err != nil { return err } if err := validateDescription(description); err != nil { return err } e.Name = name e.Description = description e.UpdatedAt = time.Now().UTC() return nil } // validateExampleName validates an example name. func validateExampleName(name string) error { length := utf8.RuneCountInString(name) if length < MinExampleNameLen || length > MaxExampleNameLen { return ErrInvalidExampleName } return nil } // validateDescription validates a description. func validateDescription(desc string) error { if utf8.RuneCountInString(desc) > MaxDescriptionLen { return ErrInvalidExampleName } return nil }