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 struct Email(String); Entity Struct + ID struct User { id: UserId, ... } Aggregate Module boundary mod order { ... } Repository Trait trait UserRepo { fn find(...) } Domain Event Enum enum OrderEvent { Created, ... } Pattern Templates Value Object struct Email(String);
impl Email {
pub fn new(s: &str) -> Result
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
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-*