ts-ddd-value-object
Create, review, or guide Value Object implementation in a TypeScript + DDD codebase.
Skill by llodev
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/llodev-ts-ddd-value-object
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: backend, testing
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Node.js package
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/ts-ddd-value-object
What's inside
- Skill name
ts-ddd-value-object- Description
- Create, review, or guide Value Object implementation in a TypeScript + DDD codebase. Use when the request involves 'value object', 'VO', `*.vo.ts` files (either shared at `libs/shared/src/vo/...` or BC-local at `apps/api/src/<bc>/domain/value-objects/...`), domain attribute validation/normalization, closed-set VOs derived from string enums (PaletteKey-style), composite-value VOs (ImageRef-style), `ValueObject` + `Result` pattern, `ValueObjectConfig`, `tryCreate`/`create` dual API, or VO test coverage.
- License
- MIT
- Metadata
- version: 0.1.0
- Instructions
- 141 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
Files (13)
- CHANGELOG.md
- LICENSE
- README.md
- SKILL.md
- README.es-ES.md · docs/i18n/
- README.pt-BR.md · docs/i18n/
- celebration-slot-index.vo.test.ts · examples/
- celebration-slot-index.vo.ts · examples/
- palette-key.vo.test.ts · examples/
- palette-key.vo.ts · examples/
- slug.vo.ts · examples/
- package.json
- vo-pattern.md · references/
SKILL.md (first lines)
# TypeScript DDD Value Object
**MANDATORY — READ ENTIRE FILE**: Before any implementation step, read
[`references/vo-pattern.md`](references/vo-pattern.md) completely.
**Do NOT load** other DDD skills (entity, use-case) unless explicitly requested.
---
## Where VOs live in this monorepo
| Scope | Path | Import from | Tests |
| ----------------------------------- | ----------------------------------------------------- | --------------------------------------------------- | ----------------------------------------------------------- |
| Shared, cross-BC primitive | `libs/shared/src/vo/<name>.vo.ts` | `../base` (inside lib) / `@acme/shared` (consumers) | `libs/shared/test/vo/<name>.vo.test.ts` |
| BC-local (one bounded context only) | `apps/api/src/<bc>/domain/value-objects/<name>.vo.ts` | `@acme/shared` | `apps/api/test/<bc>/domain/value-objects/<name>.vo.test.ts` |
- Add a new shared VO to `libs/shared/src/vo/index.ts`.
- Inside `libs/shared` always import the base via the relative `../base` barrel — not via `@acme/shared` (self-import).
- BC-local VOs live in `apps/api` because `libs/shared` must stay framework- and BC-vocabulary-free (project rule).
- After a shared change, run `pnpm --filter @acme/shared build` and the consumers' checks (`pnpm --filter api typecheck && pnpm --filter api test`).
---
## Before You Start
Answer first:
- **Reuse or create?** Can `Text` (with `minLength`/`maxLength`) or `Number` (with `minValue`/`maxValue`) cover this through config? Then no new VO — just call `Text.tryCreate(v, { minLength: 2, maxLength: 50 })` inside the entity.
- **Closed set of allowed values?** (e.g. palette key, kind, status, provider, layout). Then it is an **enum-backed VO** — see "Closed-set VOs use enums" below. Never represent a closed set as a raw-string `as const` tuple.
- **Shape**: scalar string/number/Date, or **composite object** (like `ImageRef`)? Composite VOs collect errors and return `Result.fail(string[])` instead of throwing.
- **Subclassable?** Will other VOs extend this? → `protected` constructor. Leaves → `private`.
- **Config**: are constraints fixed or caller-configurable? → extend `ValueObjectConfig` with a typed interface.
- **Normalization**: trim, lowercase, strip accents? → always normalize **before** validating, inside `tryCreate`.
---
## Constructor Visibility Decision
| VO intent | Constructor | Reason |
| --------------------------------------------- | ------------------------- | --------------------------------------------- |
| Leaf VO — no subclasses needed | `private` | Prevents unintended extension |
| Base VO — other VOs extend it | `protected` | Subclasses call `super(value, config)` |
| Specialization — inherits error constant only | `protected` (from parent) | Uses parent's `tryCreate`, overrides constant |
`Text` and `Id` both use `protected` — anything that might need specialization should too. `Slug`, `PaletteKey`, `ImageRef`, `DotSeparatedName`, `Number` are leaves and use `private`.
---
## Closed-set VOs use enums (mandatory)
Every closed set of allowed values is a **string-backed TS enum**; the catalog tuple and the VO's stored type are **derived** from the enum. Never declare the set as a raw-string `as const` tuple, and never compare against string literals at call sites.
```typescript
// libs/shared/src/vo/palette-key.vo.ts
import { Result, ValueObject, ValueObjectConfig } from "../base";
export enum PaletteKeyEnum {
BORDO = "bordo",
ROSE = "rose",
}
export const PALETTE_KEYS = Object.values(PaletteKeyEnum);
export type PaletteKey = (typeof PaletteKeyEnum)[keyof typeof PaletteKeyEnum];
export class PaletteKeyVO extends ValueObject<PaletteKey, ValueObjectConfig> {
private static readonly INVALID_PALETTE_KEY = "INVALID_PALETTE_KEY";
private constructor(value: PaletteKey, config?: ValueObjectConfig) {
super(value, config);
}
public static create(value: string, config?: ValueObjectConfig): PaletteKeyVO {
const r = PaletteKeyVO.tryCreate(value, config);
r.throwIfFailed();
return r.instance;
}
public static tryCreate(value: string, config?: ValueObjectConfig): Result<PaletteKeyVO> {
if (typeof value !== "string" || !PALETTE_KEYS.includes(value as PaletteKey)) {
return Result.fail(PaletteKeyVO.INVALID_PALETTE_KEY);
}
return Result.ok(new PaletteKeyVO(value as PaletteKey, config));
}
}
```
Call sites always compare via the enum:
```typescript
if (palette.value === PaletteKeyEnum.BORDO) { ... } // OK
if (palette.value === "bordo") { ... } // FORBIDDEN
```
> The live `libs/shared/src/vo/palette-key.vo.ts` still uses the old `PALETTE_KEYS as const` form. New closed-set VOs **must** follow the enum pattern above; an existing one should be migrated when it is next touched.
---
## Config Pattern
When constraints are caller-configurable, define a typed config interface:
```typescript
import { Result, ValueObject, ValueObjectConfig } from "../base";
export interface MyVoConfig extends ValueObjectConfig {
minLength?: number;
maxLength?: number;
}
export class MyVo extends ValueObject<string, MyVoConfig> { ... }
```
Never pass raw `ValueObjectConfig` when your VO has domain-specific constraints — the type information is lost and callers can't discover options.
---
## Core Rules
- Error codes are `private`/`protected static readonly` string constants — never inline strings in `throw`/`Result.fail`.
- Always normalize (trim, lowercase, NFD-strip accents) **before** checking invariants inside `tryCreate`.
- `create(value)` is a thin wrapper: call `tryCreate` → `throwIfFailed()` → return `.instance`. No logic there.From llodev/skills, MIT.
More from llodev/skills
- 0.563score
ts-ddd-dto
llodev
Create, review, or guide Data Transfer Objects in a TypeScript + DDD monorepo.
SkillBFreshness: B★ 2Claude Code +9 - 0.563score
ts-ddd-use-case
llodev
Create, review, or guide use case (application service) implementation in a TypeScript + DDD codebase.
SkillBFreshness: B★ 2Claude Code +9 - 0.563score
ts-ddd-controller
llodev
Create, review, or guide HTTP controllers in a TypeScript + DDD api.
SkillBFreshness: B★ 2Claude Code +9 - 0.563score
ts-ddd-domain-service
llodev
Create, review, or guide pure Domain Service implementation in a TypeScript + DDD monorepo.
SkillBFreshness: B★ 2Claude Code +9 - 0.563score
ts-ddd-repository
llodev
Create, review, or guide repository contracts and implementations in a TypeScript + DDD codebase.
SkillBFreshness: B★ 2Claude Code +9 - 0.563score
ts-query-cqrs
llodev
Create, review, or guide read-side queries in a TypeScript + DDD CQRS codebase.
SkillBFreshness: B★ 2Claude Code +9 - 0.563score
ts-ddd-entity
llodev
Create, review, or guide Domain Entity implementation in a TypeScript + DDD codebase.
SkillBFreshness: B★ 2Claude Code +9 - 0.558score
django-schema-design
llodev
Design robust Django database schemas with model best practices, migration patterns, and primary key strategy (incremental, UUID, UUIDv7).
SkillBFreshness: B★ 2Claude Code +9 - 0.558score
pm-tasks-asana
llodev
Asana adapter for the @llodev/pm-tasks-* family.
SkillBFreshness: B★ 2Claude Code +9 - 0.558score
pm-tasks-core
llodev
Core extraction + vocabulary for the @llodev/pm-tasks-* family.
SkillBFreshness: B★ 2Claude Code +9 - 0.558score
pm-tasks-trello
llodev
Trello adapter for the @llodev/pm-tasks-* family.
SkillBFreshness: B★ 2Claude Code +9 - 0.554score
pm-tasks-linear
llodev
Linear adapter for the @llodev/pm-tasks-* family.
SkillBFreshness: B★ 2Claude Code +9
Signals
- Stars
- 2
- Forks
- 0
- Open issues
- 0
- Last commit (this folder)
- Jul 17, 2026 (3 months ago)
- Last release
- Aug 21, 2026 (2 months ago)
From GitHub (fetched Oct 24, 2026).
Details
- Version
- 0.1.0
- License
- MIT
- Repository
- github.com/llodev/skills
- Website
- www.npmjs.com/org/llodev
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from llodev
- 0.563score
ts-ddd-dto
llodev
Create, review, or guide Data Transfer Objects in a TypeScript + DDD monorepo.
SkillBFreshness: B★ 2Claude Code +9 - 0.563score
ts-ddd-repository
llodev
Create, review, or guide repository contracts and implementations in a TypeScript + DDD codebase.
SkillBFreshness: B★ 2Claude Code +9 - 0.563score
ts-ddd-controller
llodev
Create, review, or guide HTTP controllers in a TypeScript + DDD api.
SkillBFreshness: B★ 2Claude Code +9 - 0.563score
ts-ddd-domain-service
llodev
Create, review, or guide pure Domain Service implementation in a TypeScript + DDD monorepo.
SkillBFreshness: B★ 2Claude Code +9 - 0.563score
ts-ddd-entity
llodev
Create, review, or guide Domain Entity implementation in a TypeScript + DDD codebase.
SkillBFreshness: B★ 2Claude Code +9 - 0.563score
ts-ddd-use-case
llodev
Create, review, or guide use case (application service) implementation in a TypeScript + DDD codebase.
SkillBFreshness: B★ 2Claude Code +9
Cite this page
ts-ddd-value-object skill. Agent Mart. https://agentmart.dev/skills/llodev-ts-ddd-value-object (last updated 2026-10-24)