value-object
Value Object Skill
Generates or refactors immutable TypeScript value objects — domain primitives that encapsulate validation, normalization, and conversion logic.
Note: This skill focuses on patterns for creating value objects and does not require specific npm packages. However, if you're integrating with @efesto-cloud packages, install @efesto-cloud/entity for the mapper interfaces:
IValueObjectMapper<VO, Raw>— use this for value object mappers (preferred)IEntityMapper<Entity, Doc>— use this for entity mappers (handled by the persistence skill)
Decision: what to generate
| Request | Output |
|---|---|
| Single primitive (email, UUID, phone, color…) | Interface + scalar wrapper class |
| Composite (address, money, date range…) | Interface + composite class accepting IFoo raw shape |
| Mentions persistence or transport layer | Also generate mapper/FooMapper.ts |
| Improving/retrofitting existing code | Add missing factory + toRaw/toJSON without renaming |
File layout
src/
├── value_object/
├── IFoo.ts # Interface / raw shape
└── impl/
└── Foo.ts # Immutable implementation
Use relative imports for internal files.
Core rules — always apply
private constructor— creation only via factorypublic static create(raw): T— validate, normalize, thrownew Error("…")on invalidpublic readonlyon every field — no mutation after constructiontoRaw()— returns the primitive or a plain object; inverse ofcreate()toJSON()/toDto()— returns the interface shape for serialization
See ./references/value-object-templates.md for concrete implementations.
Scalar wrapper
- Constructor accepts a single primitive (
string,number) toRaw()returns that primitive directly- Interface has a single named field:
{ value: string }
Composite value object
- Constructor accepts the full
IFooshape create(raw: IFoo)validates required fields; normalizes strings with.trim()toRaw()returns a plain object matchingIFoo
Mapper (persistence / transport only)
- Separate class:
FooMapper.fromRaw(raw) → Foo,FooMapper.toRaw(foo) → RawType - Do not put database or transport concerns inside the value object itself
Validation
null/undefinedinput → throw, do not silently produce a broken object- Normalize in the factory (
.trim(),.toLowerCase()) before storing - Throw
new Error("descriptive message")— keep it simple, no custom error classes unless asked
Special cases
| Case | Key notes |
|---|---|
Validate @ presence; normalize to lowercase; toRaw() returns string |
|
| Phone | Decide in factory: strip non-digits or keep formatted |
| UUID | Validate format with regex; do not generate inside the value object |
| Password hash | Store hash only; no plain-text field; toRaw() must not accidentally expose the hash |
| Money / Currency | amount as integer cents + currency as ISO string; never use floats |
| Date range | Validate start < end; expose computed helpers (duration(), contains(d)) if useful |
| Color | Validate hex or RGB; normalize to one canonical format |
| Nullable wrapping | Accept null in fromRaw(); return null from toRaw() if value is absent |
If improving existing code
- Keep existing names (
IEmailAddress,EmailAddress,EmailAddressRaw, etc.) - Add missing
create()/fromRaw()factory with validation - Make constructor
privateif it is not already - Add
toRaw()/toJSON()if absent - Do not restructure unrelated logic or rename symbols
Output style
- Modern TypeScript;
.tsfile extensions in imports export defaultfor the implementation class- Keep generated code short and readable — one responsibility per file
More from efesto-cloud/lib
usecase
>
2observer
Use when writing or reviewing Observable code from the @efesto-cloud/observable package.
2entity
Create or modify domain entities using the @efesto-cloud/entity package. Use this skill whenever the user asks to add a new entity, update an existing entity, add properties or methods to an entity, or work on the entity/dto layer. Trigger when the user says things like "create a Foo entity", "add a field to Bar", "I need a new domain object", or "add entity X". Also trigger for DTO creation or modification.
2persistence
>
2type-enum-dict
|
2monad-maybe
Use when writing or reviewing code that returns Maybe<T> from the @efesto-cloud/maybe package.
2