
Domain Modeling
Guide an agent through domain-type design and domain-focused reviews so invalid states are unrepresentable instead of caught at runtime.
Install
npx skills add https://github.com/jwilger/agent-skills --skill domain-modelingWhat is this skill?
- Detects primitive obsession and recommends semantic types, newtypes, and value objects
- Applies parse-don’t-validate and making invalid states unrepresentable
- Targets type-safety and domain-integrity reviews—not general style or formatting passes
- Explicitly excludes debugging-only work and standalone database schema design
- Fits TDD domain review phases and standalone design or review tasks (metadata phase: decide)
Adoption & trust: 1 installs on skills.sh; 3 GitHub stars; 3/3 security scanners passed (skills.sh audits); trending (+100% hot-view momentum).
Recommended Skills
Journey fit
Rich domain types and value objects are introduced while modeling application logic—the canonical home for semantic typing work in a solo build. Backend and service-layer code is where primitive obsession, swappable parameters, and bool-as-state bugs usually live and where parse-don’t-validate pays off.
Common Questions / FAQ
Is Domain Modeling safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Domain Modeling
# Domain Modeling **Value:** Communication -- domain types make code speak the language of the business. They turn implicit knowledge into explicit, compiler-verified contracts that humans and AI can reason about. ## Purpose Teaches how to build rich domain models that prevent bugs at compile time rather than catching them at runtime. Covers primitive obsession detection, parse-don't-validate, making invalid states unrepresentable, and semantic type design. Independently useful for any code review or design task, and provides the principles that domain review checks for in the TDD cycle. ## Practices ### Avoid Primitive Obsession Do not use raw primitives (`String`, `int`, `number`) for domain concepts. Create types that express business meaning. **Do:** ``` fn transfer(from: AccountId, to: AccountId, amount: Money) -> Result<Receipt, TransferError> ``` **Do not:** ``` fn transfer(from: String, to: String, amount: i64) -> Result<(), String> ``` When reviewing code, flag every parameter, field, or return type where a primitive represents a domain concept. The fix is a newtype or value object that validates on construction. **Bool-as-state anti-pattern:** A `bool` field whose name describes a domain state (`already_exists`, `is_initialized`, `is_published`, `has_been_reviewed`) is a state machine encoded as a primitive. Two states today become three tomorrow, and the bool cannot represent the third. ```rust // BAD: bool encodes a two-state machine as a primitive struct Article { is_published: bool } // GOOD: enum names the states and extends safely enum ArticleState { Draft, Published, Archived } ``` Flag any bool field that answers "what state is this in?" rather than "is this condition true?" The fix is an enum whose variants name the domain states. This check is distinct from "make invalid states unrepresentable" -- that rule catches impossible combinations; this one catches domain concepts hiding inside a boolean. ### Parse, Don't Validate Validate at the boundary. Use strong types internally. Never re-validate data that a type already guarantees. 1. Accept raw input at system boundaries (user input, API responses). 2. Parse it into a domain type that enforces validity at construction. 3. Pass the domain type through the system. No further validation needed. ```python # Boundary: parse raw input into domain type email = Email(raw_input) # raises if invalid # Interior: trust the type def send_welcome(email: Email) -> None: # No need to validate -- Email guarantees validity ``` If you find validation logic deep inside business logic, it belongs at the construction boundary instead. ### Make Invalid States Unrepresentable Use the type system to make illegal combinations impossible to construct. **Problem -- boolean flags create invalid combinations:** ``` struct User { email: Option<String>, email_verified: bool } # Can have email_verified=true with email=None ``` **Solution -- encode state in the type:** ``` enum User { Unverified { email: Email }, Verified { ema