Learn-skills.dev m09-domain
CRITICAL: Use for domain modeling. Triggers: domain model, DDD, domain-driven design, entity, value object, aggregate, repository pattern, business rules, validation, invariant, 领域模型, 领域驱动设计, 业务规则
install
source · Clone the upstream repo
git clone https://github.com/NeverSight/learn-skills.dev
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/actionbook/rust-skills/m09-domain" ~/.claude/skills/neversight-learn-skills-dev-m09-domain && rm -rf "$T"
manifest:
data/skills-md/actionbook/rust-skills/m09-domain/SKILL.mdsource content
Domain Modeling
Layer 2: Design Choices
Core Question
What is this concept's role in the domain?
Before modeling in code, understand:
- Is it an Entity (identity matters) or Value Object (interchangeable)?
- What invariants must be maintained?
- Where are the aggregate boundaries?
Domain Concept → Rust Pattern
| Domain Concept | Rust Pattern | Ownership Implication |
|---|---|---|
| Entity | struct + Id | Owned, unique identity |
| Value Object | struct + Clone/Copy | Shareable, immutable |
| Aggregate Root | struct owns children | Clear ownership tree |
| Repository | trait | Abstracts persistence |
| Domain Event | enum | Captures state changes |
| Service | impl block / free fn | Stateless operations |
Thinking Prompt
Before creating a domain type:
-
What's the concept's identity?
- Needs unique identity → Entity (Id field)
- Interchangeable by value → Value Object (Clone/Copy)
-
What invariants must hold?
- Always valid → private fields + validated constructor
- Transition rules → type state pattern
-
Who owns this data?
- Single owner (parent) → owned field
- Shared reference → Arc/Rc
- Weak reference → Weak
Trace Up ↑
To domain constraints (Layer 3):
"How should I model a Transaction?" ↑ Ask: What domain rules govern transactions? ↑ Check: domain-fintech (audit, precision requirements) ↑ Check: Business stakeholders (what invariants?)
| Design Question | Trace To | Ask |
|---|---|---|
| Entity vs Value Object | domain-* | What makes two instances "the same"? |
| Aggregate boundaries | domain-* | What must be consistent together? |
| Validation rules | domain-* | What business rules apply? |
Trace Down ↓
To implementation (Layer 1):
"Model as Entity" ↓ m01-ownership: Owned, unique ↓ m05-type-driven: Newtype for Id "Model as Value Object" ↓ m01-ownership: Clone/Copy OK ↓ m05-type-driven: Validate at construction "Model as Aggregate" ↓ m01-ownership: Parent owns children ↓ m02-resource: Consider Rc for shared within aggregate
Quick Reference
| DDD Concept | Rust Pattern | Example |
|---|---|---|
| Value Object | Newtype | |
| Entity | Struct + ID | |
| Aggregate | Module boundary | |
| Repository | Trait | |
| Domain Event | Enum | |
Pattern Templates
Value Object
struct Email(String); impl Email { pub fn new(s: &str) -> Result<Self, ValidationError> { validate_email(s)?; Ok(Self(s.to_string())) } }
Entity
struct UserId(Uuid); struct User { id: UserId, email: Email, // ... other fields } impl PartialEq for User { fn eq(&self, other: &Self) -> bool { self.id == other.id // Identity equality } }
Aggregate
mod order { pub struct Order { id: OrderId, items: Vec<OrderItem>, // Owned children // ... } impl Order { pub fn add_item(&mut self, item: OrderItem) { // Enforce aggregate invariants } } }
Common Mistakes
| Mistake | Why Wrong | Better |
|---|---|---|
| Primitive obsession | No type safety | Newtype wrappers |
| Public fields with invariants | Invariants violated | Private + accessor |
| Leaked aggregate internals | Broken encapsulation | Methods on root |
| String for semantic types | No validation | Validated newtype |
Related Skills
| When | See |
|---|---|
| Type-driven implementation | m05-type-driven |
| Ownership for aggregates | m01-ownership |
| Domain error handling | m13-domain-error |
| Specific domain rules | domain-* |