domain-modeling
Skill4 starsChanged 8 months agoCC0-1.0View on GitHub
Use this skill when the user asks about domain modeling, primitive obsession, parse-don't-validate, semantic types, value objects, or making invalid states unrepresentable. Triggers on: "review for domain issues", "primitive obsession", "design domain types", "value object", "newtype wrapper", "semantic type", "make invalid states unrepresentable", "bool-as-state", requests to replace raw strings/ints with proper types, requests to fix swappable parameters, or any code review focusing on type safety and domain integrity. Also activates during TDD domain review phases. NOT for general code review (style, formatting), debugging, database schema design, or design patterns unrelated to domain type modeling.
We split this skill into the rules it gives an agent
19 rules. 0 are common, 16 are distinctive, and 3 are very specific to this project.
0
Common
Most of the 183 edited versions we read keep these.
16
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 43
Do not use raw primitives (String, int, number) for domain concepts.
- Distinctive: line 44
Create types that express business meaning.
- Distinctive: line 56
When reviewing code, flag every parameter, field, or return type where a primitive represents a domain concept.
- Distinctive: line 97
If you find validation logic deep inside business logic, it belongs at the construction boundary instead.
- Distinctive: line 102
Use the type system to make illegal combinations impossible to construct.
- Distinctive: line 118
When reviewing code, ask: "Can this type represent a state that is meaningless in the domain?" If yes, redesign it.
- Very specific: line 148
Every identifier gets its own type.
- Distinctive: line 162
Construction (IN): Always through a validating constructor.
- Distinctive: line 167
Never provide an automatic conversion FROM a primitive -- that bypasses validation and undermines parse-don't-validate.
- Distinctive: line 172
Use enums with exhaustive match/switch to ensure all cases are handled.
- Distinctive: line 173
Never use a catch-all default for domain states -- it silently swallows new variants.
- Distinctive: line 178
When reviewing code (whether in a TDD cycle or a standalone review), you have authority to reject designs that violate these principles.
- Distinctive: line 186
If the other party disagrees, engage substantively for up to two rounds.
- Distinctive: line 189
Do not back down from valid domain concerns to avoid conflict.
- Very specific: line 203
Do not silently accept designs that violate these principles: [RP] -- if human explicitly overrides, record the override and the principle …
- Distinctive: line 208
Veto authority -- "do not back down": Do not rationalize away a valid concern to avoid the friction of escalation.
- Distinctive: line 215
If yes, use an enum. If it genuinely answers a yes/no question with no domain state implications (e.g., "is this calculation positive?"), a …
- Distinctive: line 222
After applying domain modeling principles, verify:
- Distinctive: line 233
If any criterion is not met, create or refine the domain type before proceeding.
“Common” is measured: kept by at least 6 in 10 of the 183 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
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.
// 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.
- Accept raw input at system boundaries (user input, API responses).
- Parse it into a domain type that enforces validity at construction.
- Pass the domain type through the system. No further validation needed.
# 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 validityIf 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=NoneSolution -- encode state in the type:
enum User {
Unverified { email: Email },
Verified { email: Email, verified_at: Timestamp },
}When reviewing code, ask: "Can this type represent a state that is meaningless in the domain?" If yes, redesign it.
Semantic Types Over Structural Types
Name types for what they ARE in the domain, not what they are made of.
| Wrong (structural) | Right (semantic) |
NonEmptyString | UserName |
PositiveInteger | OrderQuantity |
ValidatedEmail | CustomerEmail |
The test: if two fields have the same structural type, the compiler cannot catch you swapping them. Semantic types prevent this.
// BAD: title and name are both NonEmptyString -- swappable
{ title: NonEmptyString, name: NonEmptyString }
// GOOD: distinct types catch mix-ups at compile time
{ title: UserTitle, name: UserName }Structural types are useful as building blocks that semantic types wrap. The semantic type adds domain identity; the structural type provides reusable validation.
Newtypes for Identifiers
Every identifier gets its own type. Never use raw String or int for IDs.
struct AccountId(Uuid);
struct UserId(Uuid);
// Compiler catches: transfer(user_id, account_id) won't compile
fn transfer(from: AccountId, to: AccountId, user: UserId) -> Result<(), Error>Ergonomic Conversions
Make construction validated and extraction easy.
- Construction (IN): Always through a validating constructor. No automatic conversion from primitives.
- Extraction (OUT): Provide
Display,AsRef,Intoor equivalent so the type is convenient to use. Getting the inner value out should be trivial.
Never provide an automatic conversion FROM a primitive -- that bypasses validation and undermines parse-don't-validate.
Exhaustive Matching
Use enums with exhaustive match/switch to ensure all cases are handled. Never use a catch-all default for domain states -- it silently swallows new variants.
Veto Authority
When reviewing code (whether in a TDD cycle or a standalone review), you have authority to reject designs that violate these principles. When exercising this authority:
- State the specific violation (e.g., "primitive obsession:
emailisString, should beEmailtype"). - Propose the alternative with a concrete type definition.
- Explain the impact in one sentence.
- If the other party disagrees, engage substantively for up to two rounds. Then escalate to the human.
Do not back down from valid domain concerns to avoid conflict. Do not silently accept designs that violate these principles.
Enforcement Note
- Standalone mode: Advisory. The agent follows domain modeling principles by convention.
- TDD domain review (chaining): Advisory. Veto is self-enforced: the agent must reject its own work when it violates these principles, with the same rigor as if a separate agent were reviewing.
- TDD domain review (subagents): Gating. Domain veto blocks phase progression. The reviewing agent can reject the handoff.
Hard constraints:
- Do not silently accept designs that violate these principles:
[RP]-- if human explicitly overrides, record the override and the principle it violates.
Constraints
- Veto authority -- "do not back down": Do not rationalize away a valid concern to avoid the friction of escalation. It does not mean refuse to engage with counterarguments. If the counterargument is genuinely convincing -- the principle doesn't apply here -- update your assessment. If you're backing down because escalation is inconvenient, that's a violation.
- Bool-as-state vs. conditions: The test is: does this boolean represent a state transition the domain cares about (e.g., active/inactive, open/closed)? If yes, use an enum. If it genuinely answers a yes/no question with no domain state implications (e.g., "is this calculation positive?"), a boolean is fine. When in doubt, use an enum -- the cost of a small enum is lower than the cost of a boolean that silently accumulates domain meaning.
Verification
After applying domain modeling principles, verify:
- No primitive types (
String,int,number) used for domain concepts - No bool fields encoding domain states (use enums for state machines)
- All identifiers use newtype wrappers, not raw primitives
- Invalid states are unrepresentable (no contradictory field combinations)
- Validation occurs at construction boundaries, not deep in business logic
- Types are named for domain meaning (semantic), not structure
- Two fields with the same underlying type cannot be accidentally swapped
- Enum matching is exhaustive with no catch-all defaults for domain states
If any criterion is not met, create or refine the domain type before proceeding.
Dependencies
This skill works standalone. For enhanced workflows, it integrates with:
- tdd: Domain review is a mandatory checkpoint in the TDD cycle. This skill provides the principles that review checks for.
- code-review: Domain integrity is stage 3 of the three-stage review. This skill defines what to look for.
- architecture-decisions: Architectural patterns (event sourcing, CQRS, hexagonal) affect where domain boundaries fall.
Missing a dependency? Install with:
npx skills add jwilger/agent-skills --skill tddOther versions195 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 195
- mattpocock/skills★ 309ksame name
- QuivrHQ/quivr★ 40ksame name
- windmill-labs/windmill★ 18ksame name
- GreyDGL/PentestGPT★ 16ksame name
- formbricks/formbricks★ 13ksame name
- ArcReel/ArcReel★ 6ksame name
- vinvcn/mattpocock-skills-zh-CN★ 5.5ksame name
- withcoral/coral★ 4.9ksame name
- bestofjs/bestofjs★ 3.1ksame name
- rohitg00/pro-workflow★ 2.9ksame name
185 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
Used alongsideProjects with it also use decision-mapping, setup-ts-deep-modules and 10 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
FabienDehopreSkilldecision-mapping
Turn a loose idea into a sequenced map of investigation tickets, then drive them to resolution one at a time.
Also in 8 projects that use this
liufeng1976Skillsetup-ts-deep-modules
Wire dependency-cruiser into a TypeScript repo so each package is a deep module — implementation hidden in subfolders, reachable only through its entry-point files. User-invoked.
Also in 38 projects that use this
LesaniSkillwriting-beats
Shape an article as a journey of beats, choose-your-own-adventure style. The user picks a starting beat from the raw material, you write only that beat, then offer options for where to pivot next, beat by beat, until the article reaches a natural end. Use when the user has raw material and wants to assemble it as a narrative rather than an argument.
Also in 57 projects that use this
AWSSkillwizard
Generate an interactive bash wizard that walks a human through steps only they can perform. Use when provisioning infrastructure, setting up credentials or CI secrets, walking an unfamiliar third-party dashboard, or running a one-off migration or cutover. Don't invoke this for steps the agent can perform itself.
Also in 128 projects that use this
RoentekSkillloop-me
Grill me about specs for the workflows I want to build, within this workspace.
Also in 51 projects that use this
LesaniSkillwriting-shape
Take a markdown file of raw material and shape it into an article through a conversational session — drafting candidate openings, growing the piece paragraph by paragraph, arguing about format (lists, tables, callouts, quotes) at each step. Use when the user has a pile of notes, fragments, or a rough draft and wants help turning it into something publishable.
Also in 55 projects that use this
- +1 more from Lesani
In this repository21 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- bumpSkill
- agent-coordinationSkill
- architecture-decisionsSkill
- atomic-designSkill
- bootstrapSkill
- ci-integrationSkill
- code-reviewSkill
- debugging-protocolSkill
- design-systemSkill
- ensemble-teamSkill
- event-modelingSkill
- factory-reviewSkill
- memory-protocolSkill
- mutation-testingSkill
- pipelineSkill
- session-reflectionSkill
- task-managementSkill
- tddSkill
- ticket-triageSkill
- user-input-protocolSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

