Anemic vs Rich Domain Model
An anemic domain model has objects that are just data containers (fields + getters/setters) with no behavior. All logic lives in Service classes. A rich domain model puts business logic inside the domain objects themselves.
Anemic vs Rich = a puppet vs a real dog. An anemic model is a puppet — it just sits there while someone else (the service) moves its limbs. A rich model is a real dog — tell it 'sit' and it knows how. The behavior lives inside the object, not in a puppeteer class.
Anemic model symptom: Order class with only getters/setters; OrderService with 15 methods for every possible order operation. Rich model: Order has place(), ship(), cancel(), addLineItem() — business rules enforced inside the entity. Martin Fowler calls anemic domain model an anti-pattern because it is essentially procedural programming with OOP syntax — objects are passive data; services are procedure modules.
The anemic model has real trade-offs that make it pragmatic in some contexts: (1) it maps directly to database rows — ORMs like JPA generate entities from tables, and those entities are naturally anemic; (2) it separates concerns explicitly — data is in one place, logic is in another, which can be easier for CRUD-heavy applications; (3) it is easier to serialize/deserialize for API layers. The rich model is superior when: (1) business rules are complex and involve multiple fields together; (2) invariants must be enforced consistently regardless of how the object is accessed; (3) the domain logic is the core value of the application. A balanced approach: Transaction Script (anemic) for simple CRUD; Domain Model (rich) for complex business logic. Within DDD, the Aggregate enforces invariants — all mutations go through the Aggregate Root, which guarantees consistency.
I prefer rich domain models for complex business logic. An Order knows how to cancel itself: it checks if cancellation is allowed given its current state, calculates any refund, and raises a domain event — all without a service class. OrderService handles only orchestration: getting the order from the repository, calling order.cancel(), saving back, publishing the event. The test is: can a junior developer find the rule 'orders cannot be cancelled after shipment' by looking at the Order class? In a rich model, yes. In an anemic model, they must search through services. I use anemic models for simple CRUD screens where there is genuinely no domain logic.
Rich domain model makes sense for complex domains. Forcing rich models on simple CRUD applications (user settings, admin lookup tables) adds ceremony with no benefit. Know when the domain warrants the investment.