Anemic Domain Model (Anti-Pattern)
Also Known As
anemic model
data class
behaviour-free model
TL;DR
Domain objects with only data (getters/setters) and no behaviour — business logic scattered across service classes.
Explanation
Martin Fowler describes the Anemic Domain Model as an anti-pattern where domain objects are data containers and all logic lives in service classes that operate on them. This is procedural programming in OO clothing: objects don't encapsulate their own behaviour, leading to scattered logic, duplication, and loss of the Tell Don't Ask principle. The alternative is a rich domain model where objects contain both data and the behaviour that operates on that data, as in DDD entities and value objects.
Diagram
flowchart LR
subgraph Anemic_Anti_Pattern
ENT2[Order entity<br/>only getters setters<br/>no behaviour]
SVC2[OrderService<br/>all business logic<br/>manipulates entities]
ENT2 <-->|data bag only| SVC2
end
subgraph Rich_Domain_Model
ORDER["Order aggregate<br/>place() ship() cancel()<br/>behaviour lives here"]
INVAR[Invariants enforced<br/>inside the object<br/>cannot be invalid]
end
subgraph Result
ANEMIC_PROB[Logic scattered<br/>duplicate validation<br/>impossible to test in isolation]
RICH_GOOD[Logic co-located with data<br/>self-validating<br/>expressive API]
end
style ENT2 fill:#f85149,color:#fff
style ANEMIC_PROB fill:#f85149,color:#fff
style ORDER fill:#238636,color:#fff
style RICH_GOOD fill:#238636,color:#fff
Common Misconception
✗ Separating data (models) from logic (services) is always clean architecture. When domain logic lives entirely in service classes and models are just property bags, you lose encapsulation, invariant enforcement, and the benefits of object-oriented design.
Why It Matters
Anemic domain models push business logic into service classes, scattering it across the codebase — the domain model becomes a passive data bag that requires external orchestration to do anything meaningful.
Common Mistakes
- Creating model classes with only getters and setters and putting all logic in Service or Manager classes.
- Confusing an anemic model with a DTO — DTOs are intentionally data-only; domain models should encapsulate behaviour.
- Believing that a 'thin model, fat service' architecture is always good — it inverts OOP intent for domain objects.
- Not noticing the pattern until the service layer becomes unmaintainably large and all logic is duplicated.
Avoid When
- Simple CRUD applications with no domain logic — rich domain models add complexity where there is nothing to encapsulate.
- Read-only reporting models where the data is never mutated through domain rules.
- Transaction scripts are already clear and maintainable — do not introduce domain objects for their own sake.
When To Use
- Complex domains with business rules that should live close to the data they operate on.
- When service classes are growing with repeated logic that belongs to the entity itself.
- Domain-driven design contexts where entities enforce their own invariants.
- Preventing the same business rule from being duplicated across multiple service classes.
Code Examples
✗ Vulnerable
// Anemic model — data bag, all logic in services
class Order {
public int \$status; // magic number
public float \$total;
public array \$items; // zero behaviour
}
class OrderService {
public function cancel(Order \$o): void {
if (\$o->status !== 2) throw new \Exception('Cannot cancel');
\$o->status = 5; // magic numbers everywhere
}
}
✓ Fixed
// Rich model — behaviour lives with data
class Order {
private OrderStatus \$status;
public function cancel(): void {
if (!\$this->status->canCancel()) throw new CannotCancelException();
\$this->status = OrderStatus::Cancelled;
\$this->recordEvent(new OrderCancelled(\$this->id));
}
public function isPaid(): bool { return \$this->status === OrderStatus::Paid; }
}
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
19 Apr 2026
Views
38
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
Perplexity 2
ChatGPT 2
Amazonbot 1
Unknown AI 1
Perplexity 8
ChatGPT 8
Amazonbot 6
Unknown AI 3
Google 2
Ahrefs 2
SEMrush 2
Also referenced
How they use it
crawler 29
crawler_json 2
Related categories
⚡
DEV INTEL
Tools & Severity
🟡 Medium
⚙ Fix effort: High
⚡ Quick Fix
Move business logic from service classes back into the domain objects that own the data — if your Order class only has getters/setters, it's anemic
📦 Applies To
any
web
cli
queue-worker
🔍 Detection Hints
Domain classes with only getters/setters; all business logic in service or manager classes; OrderService.cancelOrder() not Order.cancel()
Auto-detectable:
✗ No
phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: Medium
False Positives: High
✗ Manual fix
Fix: High
Context: Class
Tests: Update