domain-modeling

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

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.
价值: 沟通——领域类型让代码使用业务语言。它们将隐性知识转化为明确的、经编译器验证的契约,供人类和AI进行推理。

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.
教授如何构建丰富的领域模型,在编译时预防错误而非运行时捕获错误。涵盖原始类型滥用检测、“解析而非验证”、使无效状态无法被表示以及语义类型设计。可独立用于任何代码审查或设计任务,并为TDD周期中的领域审查提供检查原则。

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.
不要使用原始类型(
String
int
number
)表示领域概念。创建具有业务含义的类型。
正确示例:
fn transfer(from: AccountId, to: AccountId, amount: Money) -> Result<Receipt, TransferError>
错误示例:
fn transfer(from: String, to: String, amount: i64) -> Result<(), String>
在审查代码时,标记所有使用原始类型表示领域概念的参数、字段或返回类型。修复方法是创建一个在构造时进行验证的newtype或值对象。

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
undefined
在边界处进行验证。内部使用强类型。永远不要对类型已保证有效性的数据重复验证。
  1. 在系统边界(用户输入、API响应)处接受原始输入。
  2. 将其解析为在构造时强制保证有效性的领域类型。
  3. 在系统内传递该领域类型,无需进一步验证。
python
undefined

Boundary: parse raw input into domain type

边界:将原始输入解析为领域类型

email = Email(raw_input) # raises if invalid
email = Email(raw_input) # 无效时抛出异常

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.
def send_welcome(email: Email) -> None: # 无需验证——Email类型已保证有效性

如果在业务逻辑深处发现验证逻辑,应将其移至构造边界处。

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 }
使用类型系统使非法组合无法被构造。
问题——布尔标志会产生无效组合:
struct User { email: Option<String>, email_verified: bool }

Can have email_verified=true with email=None

可能出现email_verified=true但email=None的情况


**Solution -- 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.

**解决方案——在类型中编码状态:**
enum User { Unverified { email: Email }, Verified { email: Email, verified_at: Timestamp }, }

当审查代码时,问自己:“这个类型能否表示领域中无意义的状态?”如果可以,重新设计它。

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.
typescript
// 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.
根据领域中的含义命名类型,而非根据其组成结构。
错误(结构型)正确(语义型)
NonEmptyString
UserName
PositiveInteger
OrderQuantity
ValidatedEmail
CustomerEmail
测试标准:如果两个字段具有相同的结构类型,编译器无法检测到你是否交换了它们。语义类型可以防止这种情况。
typescript
// 不良示例:title和name都是NonEmptyString——可被交换
{ title: NonEmptyString, name: NonEmptyString }

// 良好示例:不同类型在编译时捕获混淆
{ title: UserTitle, name: UserName }
结构类型可用作语义类型的构建块。语义类型添加领域标识;结构类型提供可复用的验证逻辑。

Newtypes for Identifiers

为标识符使用Newtype

Every identifier gets its own type. Never use raw
String
or
int
for IDs.
rust
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>
每个标识符都应有自己的类型。永远不要使用原始的
String
int
作为ID。
rust
struct AccountId(Uuid);
struct UserId(Uuid);

// 编译器会捕获错误:transfer(user_id, account_id)无法编译
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
    ,
    Into
    or 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.
使构造过程经过验证,提取操作简单。
  • 构造(输入): 始终通过验证构造函数进行。不支持从原始类型自动转换。
  • 提取(输出): 提供
    Display
    AsRef
    Into
    或等效方法,使类型使用起来更方便。获取内部值应该非常简单。
绝不要提供从原始类型进行自动转换的方法——这会绕过验证,破坏“解析而非验证”的原则。

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.
使用枚举和穷尽匹配/switch来确保所有情况都被处理。永远不要对领域状态使用通配符默认分支——它会默默地忽略新的变体。

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:
  1. State the specific violation (e.g., "primitive obsession:
    email
    is
    String
    , should be
    Email
    type").
  2. Propose the alternative with a concrete type definition.
  3. Explain the impact in one sentence.
  4. 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.
在审查代码时(无论是在TDD周期中还是独立审查),你有权拒绝违反这些原则的设计。行使该权限时:
  1. 明确指出具体的违规行为(例如:‘原始类型滥用:
    email
    String
    ,应改为
    Email
    类型’)。
  2. 提出带有具体类型定义的替代方案。
  3. 用一句话解释影响。
  4. 如果对方不同意,进行最多两轮实质性讨论。然后升级给人类处理。
不要为了避免冲突而放弃合理的领域关注点。不要默默接受违反这些原则的设计。

Enforcement Note

实施说明

This skill provides advisory guidance on domain modeling quality. It cannot mechanically prevent an agent from using primitives or creating invalid state representations. When used with the
tdd
skill, domain review is a mandatory checkpoint with veto power -- enforcement ranges from advisory (guided mode) to structural (automated mode with subagent isolation). Without the
tdd
skill, these principles are followed by convention and verified through code review.
本技能提供关于领域建模质量的建议指导。它无法机械地阻止Agent使用原始类型或创建无效状态表示。与
tdd
技能一起使用时,领域审查是具有否决权的强制性检查点——实施范围从建议性(引导模式)到结构性(带有子Agent隔离的自动化模式)。如果没有
tdd
技能,这些原则将通过约定遵循并通过代码审查进行验证。

Verification

验证

After applying domain modeling principles, verify:
  • No primitive types (
    String
    ,
    int
    ,
    number
    ) used for domain concepts
  • 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.
应用领域建模原则后,验证以下内容:
  • 未使用原始类型(
    String
    int
    number
    )表示领域概念
  • 所有标识符都使用newtype包装器,而非原始类型
  • 无效状态无法被表示(无矛盾的字段组合)
  • 验证发生在构造边界,而非业务逻辑深处
  • 类型根据领域含义(语义)命名,而非结构
  • 具有相同底层类型的两个字段不会被意外交换
  • 枚举匹配是穷尽的,没有针对领域状态的通配符默认分支
如果任何标准未满足,在继续之前创建或完善领域类型。

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 tdd
本技能可独立使用。为了增强工作流,它可与以下技能集成:
  • tdd: 领域审查是TDD周期中的强制性检查点。本技能提供审查所需的原则。
  • code-review: 领域完整性是三阶段审查的第三阶段。本技能定义了审查要点。
  • architecture-decisions: 架构模式(事件溯源、CQRS、六边形架构)会影响领域边界的划分。
缺少依赖项?使用以下命令安装:
npx skills add jwilger/agent-skills --skill tdd