Domain concepts (Money, PhoneNumber, OrderId, Currency) represented as raw strings, numbers, or booleans — the type system can't tell two domains apart.
Each domain concept has a small typed home — Money, PhoneNumber, OrderId — that knows its rules.
Before the refactoring
function priceFor(cents, currency) {// ...}
After the refactoring
class Money { /* amount + currency, with arithmetic */ }function priceFor(money) {// ...}
Validation and formatting scatter across every consumer; the wrong primitive in the wrong slot compiles fine and fails at runtime; behavior that belongs with the concept has nowhere to live.
Wrapping every primitive is overkill — wrap when the concept needs validation, formatting, or domain-specific behavior beyond what the primitive offers.
Misuse becomes a type error; behavior accretes around the concept; refactoring is local to the wrapper.
Wrapping every primitive on principle — Name, Title, Description as separate classes whose only methods are toString() — adds typing ceremony without enforcing any domain rule.